From bac542e65c0030c0d819c7ff1dcfc25892a61844 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 30 Oct 2007 23:29:53 +0000 Subject: [PATCH] dyld-95.3.tar.gz --- doc/man/man1/dyld.1 | 103 +- doc/man/man1/update_dyld_shared_cache.1 | 79 + doc/man/man1/update_prebinding.1 | 1 + doc/man/man3/NSModule.3 | 610 ------ doc/man/man3/NSObjectFileImage.3 | 151 -- doc/man/man3/NSObjectFileImage_priv.3 | 42 - doc/man/man3/dlclose.3 | 11 +- doc/man/man3/dlerror.3 | 4 +- doc/man/man3/dlopen.3 | 88 +- doc/man/man3/dlopen_preflight.3 | 33 + doc/man/man3/dlsym.3 | 3 +- doc/man/man3/dyld.3 | 311 +-- dyld.xcodeproj/kledzik.mode1v3 | 1377 ++++++++++++ dyld.xcodeproj/kledzik.pbxuser | 140 ++ dyld.xcodeproj/project.pbxproj | 671 +++--- include/dlfcn.h | 15 +- include/mach-o/dyld-update-prebinding.h | 19 + include/mach-o/dyld.h | 375 ++-- include/mach-o/dyld_gdb.h | 61 +- include/mach-o/dyld_images.h | 98 + include/mach-o/dyld_priv.h | 53 +- launch-cache/Architectures.hpp | 80 + launch-cache/CacheFileAbstraction.hpp | 116 + launch-cache/FileAbstraction.hpp | 145 ++ launch-cache/MachOBinder.hpp | 590 +++++ launch-cache/MachOFileAbstraction.hpp | 738 +++++++ launch-cache/MachOLayout.hpp | 463 ++++ launch-cache/MachORebaser.hpp | 812 +++++++ launch-cache/com.apple.dyld.plist | 26 + launch-cache/dyld_cache_format.h | 58 + launch-cache/dyld_shared_cache.defs | 19 + launch-cache/update_dyld_shared_cache.cpp | 1845 ++++++++++++++++ src/ImageLoader.cpp | 928 ++++---- src/ImageLoader.h | 360 +++- src/ImageLoaderMachO.cpp | 1901 ++++++++++------- src/ImageLoaderMachO.h | 123 +- src/dyld.cpp | 1615 ++++++++++---- src/dyld.exp | 4 +- src/dyld.h | 37 +- src/dyld64.exp | 4 +- src/dyldAPIs.cpp | 945 ++++---- src/dyldAPIsInLibSystem.cpp | 314 ++- src/dyldExceptions.c | 6 +- src/dyldInitialization.cpp | 131 +- ...readHelpers.h => dyldLibSystemInterface.h} | 16 +- src/dyldLock.cpp | 254 +-- src/dyldLock.h | 52 +- src/dyldNew.cpp | 94 +- src/dyldStartup.s | 24 +- src/dyld_debug.c | 9 +- src/dyld_gdb.cpp | 19 +- src/glue.c | 150 +- src/strip.exp | 4 + src/stub_binding_helper.s | 72 +- unit-tests/bin/exit-non-zero-pass.pl | 10 + unit-tests/bin/exit-zero-pass.pl | 9 + unit-tests/bin/result-filter.pl | 15 +- unit-tests/include/common.makefile | 14 +- unit-tests/run-all-unit-tests | 38 +- .../NSAddImage-MATCH_BY_INSTALLNAME/Makefile | 4 +- .../NSAddImage-RETURN_ONLY_IF_LOADED/Makefile | 2 +- .../test-cases/NSAddImage-leafname/Makefile | 2 +- unit-tests/test-cases/big-stack/Makefile | 33 + unit-tests/test-cases/big-stack/main.c | 76 + unit-tests/test-cases/bundle-basic/Makefile | 2 +- unit-tests/test-cases/bundle-dont-gc/Makefile | 42 + unit-tests/test-cases/bundle-dont-gc/bar.c | 3 + unit-tests/test-cases/bundle-dont-gc/foo.c | 3 + unit-tests/test-cases/bundle-dont-gc/main.c | 91 + .../bundle-memory-load-bad/Makefile | 47 + .../bundle-memory-load-bad/bundle.c | 28 + .../test-cases/bundle-memory-load-bad/main.c | 64 + .../bundle-memory-load-fat/Makefile | 4 +- .../test-cases/bundle-memory-load/Makefile | 2 +- .../test-cases/bundle-multi-link/Makefile | 2 +- .../test-cases/bundle-multi-link/main.c | 8 +- .../test-cases/bundle-multi-load/Makefile | 2 +- .../test-cases/bundle-multi-load/main.c | 6 +- .../test-cases/bundle-name-ownership/Makefile | 39 + .../test-cases/bundle-name-ownership/bundle.c | 32 + .../test-cases/bundle-name-ownership/main.c | 83 + unit-tests/test-cases/bundle-private/Makefile | 2 +- unit-tests/test-cases/bundle-reload/Makefile | 4 +- .../test-cases/bundle-reload/bundle.cxx | 1 + .../test-cases/bundle-unlinkable/Makefile | 2 +- .../bundle-unload-keep-mapped/Makefile | 2 +- unit-tests/test-cases/bundle-v-dylib/Makefile | 9 +- unit-tests/test-cases/bundle-v-dylib/main.c | 52 +- unit-tests/test-cases/bundle-weak/Makefile | 39 + unit-tests/test-cases/bundle-weak/bundle.cxx | 41 + unit-tests/test-cases/bundle-weak/main.c | 52 + unit-tests/test-cases/crt-apple/Makefile | 57 + unit-tests/test-cases/crt-apple/main.c | 44 + unit-tests/test-cases/crt-argv-NULL/Makefile | 46 + unit-tests/test-cases/crt-argv-NULL/main.c | 14 + unit-tests/test-cases/crt-custom/Makefile | 48 + unit-tests/test-cases/crt-custom/main.c | 75 + unit-tests/test-cases/crt-custom/mystart.s | 20 + unit-tests/test-cases/crt-libSystem/Makefile | 54 + unit-tests/test-cases/crt-libSystem/main.c | 120 ++ unit-tests/test-cases/crt-result/Makefile | 55 + unit-tests/test-cases/crt-result/bad.c | 1 + unit-tests/test-cases/crt-result/good.c | 1 + unit-tests/test-cases/deadlock/Makefile | 2 +- unit-tests/test-cases/deadlock/main.c | 1 + unit-tests/test-cases/dladdr/Makefile | 2 +- .../test-cases/dlclose-dylib-unload/Makefile | 44 + .../test-cases/dlclose-dylib-unload/bar.c | 28 + .../test-cases/dlclose-dylib-unload/foo.c | 28 + .../test-cases/dlclose-dylib-unload/main.c | 162 ++ .../dlclose-terminator-dlclose/Makefile | 48 + .../dlclose-terminator-dlclose/bar.c | 28 + .../dlclose-terminator-dlclose/foo.c | 48 + .../dlclose-terminator-dlclose/main.c | 47 + .../test-cases/dlclose-unload-c++/Makefile | 44 + .../test-cases/dlclose-unload-c++/bar.c | 39 + .../foo.c | 20 +- .../test-cases/dlclose-unload-c++/main.c | 98 + unit-tests/test-cases/dlerror-clear/Makefile | 38 + unit-tests/test-cases/dlerror-clear/main.c | 61 + unit-tests/test-cases/dlerror/Makefile | 38 + unit-tests/test-cases/dlerror/main.c | 93 + .../dlopen-NULL-RTLD_FIRST/Makefile | 44 + .../test-cases/dlopen-NULL-RTLD_FIRST/foo.c | 28 + .../test-cases/dlopen-NULL-RTLD_FIRST/main.c | 106 + .../test-cases/dlopen-RTLD_FIRST/Makefile | 55 + unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c | 28 + .../test-cases/dlopen-RTLD_FIRST/base.c | 28 + unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c | 28 + .../test-cases/dlopen-RTLD_FIRST/main.c | 123 ++ .../dlopen-RTLD_NOLOAD-fallback/Makefile | 54 + .../dlopen-RTLD_NOLOAD-fallback/foo.c | 28 + .../dlopen-RTLD_NOLOAD-fallback/main.c | 46 + .../Makefile | 50 + .../dlopen-RTLD_NOLOAD-in-initializer/bar.c | 51 + .../dlopen-RTLD_NOLOAD-in-initializer/base.c | 32 + .../dlopen-RTLD_NOLOAD-in-initializer/baz.c | 34 + .../dlopen-RTLD_NOLOAD-in-initializer/foo.c | 30 + .../dlopen-RTLD_NOLOAD-in-initializer/main.c | 45 + .../dlopen-RTLD_NOLOAD-symlink/Makefile | 59 + .../dlopen-RTLD_NOLOAD-symlink/bar.c | 1 + .../dlopen-RTLD_NOLOAD-symlink/foo.c | 2 + .../dlopen-RTLD_NOLOAD-symlink/main.c | 46 + unit-tests/test-cases/dlopen-basic/main.c | 2 +- .../test-cases/dlopen-dyld-locking/Makefile | 47 + .../test-cases/dlopen-dyld-locking/bar.c | 31 + .../test-cases/dlopen-dyld-locking/base.c | 68 + .../test-cases/dlopen-dyld-locking/base.h | 3 + .../test-cases/dlopen-dyld-locking/foo.c | 33 + .../test-cases/dlopen-dyld-locking/main.c | 68 + .../dlopen-from-anonymous-code/Makefile | 40 + .../dlopen-from-anonymous-code/foo.c | 28 + .../dlopen-from-anonymous-code/main.c | 72 + .../test-cases/dlopen-in-initializer/Makefile | 45 + .../test-cases/dlopen-in-initializer/foo.c | 38 + .../test-cases/dlopen-in-initializer/main.c | 67 + .../test-cases/dlopen-init-dlopen-up/Makefile | 44 + .../test-cases/dlopen-init-dlopen-up/bar.c | 5 + .../test-cases/dlopen-init-dlopen-up/foo.c | 38 + .../test-cases/dlopen-init-dlopen-up/main.c | 60 + .../test-cases/dlopen-init-dlopen/Makefile | 44 + .../test-cases/dlopen-init-dlopen/bar.c | 1 + .../test-cases/dlopen-init-dlopen/foo.c | 38 + .../test-cases/dlopen-init-dlopen/main.c | 66 + unit-tests/test-cases/dlopen-init-up/Makefile | 44 + unit-tests/test-cases/dlopen-init-up/bar.c | 5 + unit-tests/test-cases/dlopen-init-up/foo.c | 39 + unit-tests/test-cases/dlopen-init-up/main.c | 33 + .../dlopen_preflight-basic/Makefile | 54 + .../test-cases/dlopen_preflight-basic/bar.c | 2 + .../test-cases/dlopen_preflight-basic/foo.c | 3 + .../test-cases/dlopen_preflight-basic/main.c | 95 + .../dlsym-RTLD_NEXT-missing/Makefile | 15 +- unit-tests/test-cases/dlsym-RTLD_SELF/main.c | 6 - .../test-cases/dyld-func-lookup/Makefile | 45 + unit-tests/test-cases/dyld-func-lookup/foo.c | 44 + unit-tests/test-cases/dyld-func-lookup/main.c | 69 + .../dyld-launched-prebound/Makefile | 2 +- .../test-cases/dyld-launched-prebound/main.c | 2 +- unit-tests/test-cases/dyld-slide/Makefile | 4 +- unit-tests/test-cases/dyld-slide/main.c | 5 +- .../executable-image-index/Makefile | 41 + .../test-cases/executable-image-index/foo.c | 2 + .../test-cases/executable-image-index/main.c | 45 + .../test-cases/fallback-with-suid/Makefile | 20 +- .../test-cases/fallback-with-suid/foo.c | 4 +- .../test-cases/fallback-with-suid/main.c | 20 +- unit-tests/test-cases/flat-insert/Makefile | 38 + unit-tests/test-cases/flat-insert/foo.c | 30 + unit-tests/test-cases/flat-insert/main.c | 44 + .../test-cases/framework-fallback/Makefile | 2 +- .../test-cases/image-state-change/Makefile | 43 + .../test-cases/image-state-change/bar.c | 2 + .../test-cases/image-state-change/foo.c | 3 + .../test-cases/image-state-change/main.c | 101 + .../test-cases/image-state-deny-OFI/Makefile | 49 + .../test-cases/image-state-deny-OFI/bar.c | 2 + .../test-cases/image-state-deny-OFI/foo.c | 3 + .../test-cases/image-state-deny-OFI/main.c | 132 ++ .../image-state-deny-dlclose/Makefile | 42 + .../image-state-deny-dlclose/base.c | 47 + .../test-cases/image-state-deny-dlclose/foo.c | 3 + .../image-state-deny-dlclose/main.c | 65 + .../test-cases/image-state-deny/Makefile | 46 + unit-tests/test-cases/image-state-deny/bar.c | 2 + unit-tests/test-cases/image-state-deny/foo.c | 3 + unit-tests/test-cases/image-state-deny/main.c | 113 + .../Makefile | 43 + .../image-state-dependents-initialized/bar.c | 119 ++ .../image-state-dependents-initialized/foo.c | 11 + .../image-state-dependents-initialized/main.c | 46 + unit-tests/test-cases/image-suffix/Makefile | 18 +- .../test-cases/initializer-args/Makefile | 2 +- .../Makefile | 34 +- .../insert-libraries-with-initializer/base.c | 61 + .../insert-libraries-with-initializer/base.h | 27 + .../insert-libraries-with-initializer/foo1.c | 35 + .../insert-libraries-with-initializer/foo2.c | 33 + .../insert.c | 34 + .../insert-libraries-with-initializer/main.c | 20 +- .../lazy-binding-reg-params/Makefile | 6 +- .../test-cases/lazy-binding-reg-params/foo.h | 2 +- unit-tests/test-cases/loader_path/Makefile | 2 +- .../main.c | 2 +- unit-tests/test-cases/operator-new/Makefile | 39 + unit-tests/test-cases/operator-new/main.cxx | 57 + .../test-cases/partial-library-load/Makefile | 2 +- unit-tests/test-cases/pie-basic/Makefile | 48 + unit-tests/test-cases/pie-basic/main.c | 33 + unit-tests/test-cases/pie-big/Makefile | 48 + unit-tests/test-cases/pie-big/main.c | 36 + .../test-cases/pie-custom-stack/Makefile | 48 + unit-tests/test-cases/pie-custom-stack/main.c | 33 + .../test-cases/prebased-performance/Makefile | 4 +- .../test-cases/prebased-performance/foo.c | 2 + .../Makefile | 4 +- unit-tests/test-cases/progname/main.c | 49 + .../test-cases/re-export-dylib/Makefile | 62 + unit-tests/test-cases/re-export-dylib/bar.c | 6 + unit-tests/test-cases/re-export-dylib/foo.c | 5 + unit-tests/test-cases/re-export-dylib/main.c | 39 + .../test-cases/re-export-framework/Makefile | 68 + .../test-cases/re-export-framework/bar.c | 6 + .../test-cases/re-export-framework/foo.c | 5 + .../test-cases/re-export-framework/main.c | 39 + .../re-export-sub-framework/Makefile | 66 + .../test-cases/re-export-sub-framework/bar.c | 6 + .../test-cases/re-export-sub-framework/foo.c | 5 + .../test-cases/re-export-sub-framework/main.c | 39 + .../Makefile | 42 + .../foo.cxx | 58 + .../main.c | 12 + .../Makefile | 45 + .../foo.c | 18 + .../main.c | 10 + .../test-cases/read-only-stubs/Makefile | 44 + unit-tests/test-cases/read-only-stubs/bar.c | 35 + unit-tests/test-cases/read-only-stubs/foo.c | 102 + unit-tests/test-cases/read-only-stubs/main.c | 100 + .../rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile | 55 + .../rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c | 30 + .../rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c | 38 + .../rpath-DYLD_LIBRARY_PATH/Makefile | 50 + .../test-cases/rpath-DYLD_LIBRARY_PATH/foo.c | 30 + .../test-cases/rpath-DYLD_LIBRARY_PATH/main.c | 38 + .../test-cases/rpath-LD_LIBRARY_PATH/Makefile | 46 + .../test-cases/rpath-LD_LIBRARY_PATH/foo.c | 25 + .../test-cases/rpath-LD_LIBRARY_PATH/main.c | 36 + unit-tests/test-cases/rpath-basic/Makefile | 49 + unit-tests/test-cases/rpath-basic/bar.c | 3 + unit-tests/test-cases/rpath-basic/foo.c | 3 + unit-tests/test-cases/rpath-basic/main.c | 36 + .../test-cases/rpath-dlopen-in-dylib/Makefile | 50 + .../test-cases/rpath-dlopen-in-dylib/bar.c | 45 + .../test-cases/rpath-dlopen-in-dylib/foo.c | 3 + .../test-cases/rpath-dlopen-in-dylib/main.c | 37 + .../test-cases/rpath-dlopen-indirect/Makefile | 50 + .../test-cases/rpath-dlopen-indirect/bar.c | 23 + .../test-cases/rpath-dlopen-indirect/foo.c | 25 + .../test-cases/rpath-dlopen-indirect/main.c | 37 + .../test-cases/rpath-dlopen-leak/Makefile | 64 + unit-tests/test-cases/rpath-dlopen-leak/foo.c | 25 + .../test-cases/rpath-dlopen-leak/main.c | 47 + unit-tests/test-cases/rpath-dlopen/Makefile | 46 + unit-tests/test-cases/rpath-dlopen/foo.c | 25 + unit-tests/test-cases/rpath-dlopen/main.c | 46 + .../test-cases/rpath-executable_path/Makefile | 50 + .../test-cases/rpath-executable_path/bar.c | 8 + .../test-cases/rpath-executable_path/foo.c | 5 + .../test-cases/rpath-executable_path/main.c | 36 + .../test-cases/rpath-indirect-suid/Makefile | 71 + .../test-cases/rpath-indirect-suid/bar.c | 6 + .../test-cases/rpath-indirect-suid/foo.c | 3 + .../test-cases/rpath-indirect-suid/main.c | 18 + .../rpath-loader_path-dlopen/Makefile | 53 + .../test-cases/rpath-loader_path-dlopen/bar.c | 8 + .../test-cases/rpath-loader_path-dlopen/baz.c | 6 + .../test-cases/rpath-loader_path-dlopen/foo.c | 8 + .../rpath-loader_path-dlopen/main.c | 39 + .../test-cases/rpath-loader_path/Makefile | 48 + unit-tests/test-cases/rpath-loader_path/foo.c | 25 + .../test-cases/rpath-loader_path/main.c | 36 + unit-tests/test-cases/rpath-nesting/Makefile | 55 + unit-tests/test-cases/rpath-nesting/bar.c | 28 + unit-tests/test-cases/rpath-nesting/baz.c | 29 + unit-tests/test-cases/rpath-nesting/foo.c | 31 + unit-tests/test-cases/rpath-nesting/main.c | 37 + .../test-cases/shared-cache-symlink/Makefile | 37 + .../test-cases/shared-cache-symlink/main.c | 76 + unit-tests/test-cases/suid-environ/Makefile | 2 +- .../test-cases/suid-executable_path/Makefile | 82 + .../test-cases/suid-executable_path/foo.c | 5 + .../test-cases/suid-executable_path/main.c | 38 + unit-tests/test-cases/template/Makefile | 34 + .../main.c | 6 +- .../unloadable-library-residue/Makefile | 2 +- .../unloadable-library-residue/main.c | 1 + unit-tests/test-cases/weak-override/main.c | 3 + .../test-cases/weak-symbol-flat/Makefile | 2 +- .../test-cases/zero-fill-segment/Makefile | 43 + unit-tests/test-cases/zero-fill-segment/foo.c | 8 + .../test-cases/zero-fill-segment/main.c | 48 + .../test-cases/zero-fill-segment/zero.s | 5 + .../test-cases/zero-length-segment/Makefile | 47 + .../test-cases/zero-length-segment/foo.c | 1 + .../test-cases/zero-length-segment/main.c | 32 + 326 files changed, 20664 insertions(+), 4673 deletions(-) create mode 100644 doc/man/man1/update_dyld_shared_cache.1 create mode 100644 doc/man/man1/update_prebinding.1 delete mode 100644 doc/man/man3/NSModule.3 delete mode 100644 doc/man/man3/NSObjectFileImage.3 delete mode 100644 doc/man/man3/NSObjectFileImage_priv.3 create mode 100644 doc/man/man3/dlopen_preflight.3 create mode 100644 dyld.xcodeproj/kledzik.mode1v3 create mode 100644 dyld.xcodeproj/kledzik.pbxuser create mode 100644 include/mach-o/dyld-update-prebinding.h create mode 100644 include/mach-o/dyld_images.h create mode 100644 launch-cache/Architectures.hpp create mode 100644 launch-cache/CacheFileAbstraction.hpp create mode 100644 launch-cache/FileAbstraction.hpp create mode 100644 launch-cache/MachOBinder.hpp create mode 100644 launch-cache/MachOFileAbstraction.hpp create mode 100644 launch-cache/MachOLayout.hpp create mode 100644 launch-cache/MachORebaser.hpp create mode 100644 launch-cache/com.apple.dyld.plist create mode 100644 launch-cache/dyld_cache_format.h create mode 100644 launch-cache/dyld_shared_cache.defs create mode 100644 launch-cache/update_dyld_shared_cache.cpp rename src/{dyldLibSystemThreadHelpers.h => dyldLibSystemInterface.h} (76%) create mode 100644 src/strip.exp create mode 100644 unit-tests/test-cases/big-stack/Makefile create mode 100644 unit-tests/test-cases/big-stack/main.c create mode 100644 unit-tests/test-cases/bundle-dont-gc/Makefile create mode 100644 unit-tests/test-cases/bundle-dont-gc/bar.c create mode 100644 unit-tests/test-cases/bundle-dont-gc/foo.c create mode 100644 unit-tests/test-cases/bundle-dont-gc/main.c create mode 100644 unit-tests/test-cases/bundle-memory-load-bad/Makefile create mode 100644 unit-tests/test-cases/bundle-memory-load-bad/bundle.c create mode 100644 unit-tests/test-cases/bundle-memory-load-bad/main.c create mode 100644 unit-tests/test-cases/bundle-name-ownership/Makefile create mode 100644 unit-tests/test-cases/bundle-name-ownership/bundle.c create mode 100644 unit-tests/test-cases/bundle-name-ownership/main.c create mode 100644 unit-tests/test-cases/bundle-weak/Makefile create mode 100644 unit-tests/test-cases/bundle-weak/bundle.cxx create mode 100644 unit-tests/test-cases/bundle-weak/main.c create mode 100644 unit-tests/test-cases/crt-apple/Makefile create mode 100644 unit-tests/test-cases/crt-apple/main.c create mode 100644 unit-tests/test-cases/crt-argv-NULL/Makefile create mode 100644 unit-tests/test-cases/crt-argv-NULL/main.c create mode 100644 unit-tests/test-cases/crt-custom/Makefile create mode 100644 unit-tests/test-cases/crt-custom/main.c create mode 100644 unit-tests/test-cases/crt-custom/mystart.s create mode 100644 unit-tests/test-cases/crt-libSystem/Makefile create mode 100644 unit-tests/test-cases/crt-libSystem/main.c create mode 100644 unit-tests/test-cases/crt-result/Makefile create mode 100644 unit-tests/test-cases/crt-result/bad.c create mode 100644 unit-tests/test-cases/crt-result/good.c create mode 100644 unit-tests/test-cases/dlclose-dylib-unload/Makefile create mode 100644 unit-tests/test-cases/dlclose-dylib-unload/bar.c create mode 100644 unit-tests/test-cases/dlclose-dylib-unload/foo.c create mode 100644 unit-tests/test-cases/dlclose-dylib-unload/main.c create mode 100644 unit-tests/test-cases/dlclose-terminator-dlclose/Makefile create mode 100644 unit-tests/test-cases/dlclose-terminator-dlclose/bar.c create mode 100644 unit-tests/test-cases/dlclose-terminator-dlclose/foo.c create mode 100644 unit-tests/test-cases/dlclose-terminator-dlclose/main.c create mode 100644 unit-tests/test-cases/dlclose-unload-c++/Makefile create mode 100644 unit-tests/test-cases/dlclose-unload-c++/bar.c rename unit-tests/test-cases/{insert-libraries-with-initializer => dlclose-unload-c++}/foo.c (86%) create mode 100644 unit-tests/test-cases/dlclose-unload-c++/main.c create mode 100644 unit-tests/test-cases/dlerror-clear/Makefile create mode 100644 unit-tests/test-cases/dlerror-clear/main.c create mode 100644 unit-tests/test-cases/dlerror/Makefile create mode 100644 unit-tests/test-cases/dlerror/main.c create mode 100644 unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile create mode 100644 unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c create mode 100644 unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile create mode 100644 unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_FIRST/base.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_FIRST/main.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c create mode 100644 unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c create mode 100644 unit-tests/test-cases/dlopen-dyld-locking/Makefile create mode 100644 unit-tests/test-cases/dlopen-dyld-locking/bar.c create mode 100644 unit-tests/test-cases/dlopen-dyld-locking/base.c create mode 100644 unit-tests/test-cases/dlopen-dyld-locking/base.h create mode 100644 unit-tests/test-cases/dlopen-dyld-locking/foo.c create mode 100644 unit-tests/test-cases/dlopen-dyld-locking/main.c create mode 100644 unit-tests/test-cases/dlopen-from-anonymous-code/Makefile create mode 100644 unit-tests/test-cases/dlopen-from-anonymous-code/foo.c create mode 100644 unit-tests/test-cases/dlopen-from-anonymous-code/main.c create mode 100644 unit-tests/test-cases/dlopen-in-initializer/Makefile create mode 100644 unit-tests/test-cases/dlopen-in-initializer/foo.c create mode 100644 unit-tests/test-cases/dlopen-in-initializer/main.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-up/Makefile create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-up/bar.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-up/foo.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen-up/main.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen/Makefile create mode 100644 unit-tests/test-cases/dlopen-init-dlopen/bar.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen/foo.c create mode 100644 unit-tests/test-cases/dlopen-init-dlopen/main.c create mode 100644 unit-tests/test-cases/dlopen-init-up/Makefile create mode 100644 unit-tests/test-cases/dlopen-init-up/bar.c create mode 100644 unit-tests/test-cases/dlopen-init-up/foo.c create mode 100644 unit-tests/test-cases/dlopen-init-up/main.c create mode 100644 unit-tests/test-cases/dlopen_preflight-basic/Makefile create mode 100644 unit-tests/test-cases/dlopen_preflight-basic/bar.c create mode 100644 unit-tests/test-cases/dlopen_preflight-basic/foo.c create mode 100644 unit-tests/test-cases/dlopen_preflight-basic/main.c create mode 100644 unit-tests/test-cases/dyld-func-lookup/Makefile create mode 100644 unit-tests/test-cases/dyld-func-lookup/foo.c create mode 100644 unit-tests/test-cases/dyld-func-lookup/main.c create mode 100644 unit-tests/test-cases/executable-image-index/Makefile create mode 100644 unit-tests/test-cases/executable-image-index/foo.c create mode 100644 unit-tests/test-cases/executable-image-index/main.c create mode 100644 unit-tests/test-cases/flat-insert/Makefile create mode 100644 unit-tests/test-cases/flat-insert/foo.c create mode 100644 unit-tests/test-cases/flat-insert/main.c create mode 100644 unit-tests/test-cases/image-state-change/Makefile create mode 100644 unit-tests/test-cases/image-state-change/bar.c create mode 100644 unit-tests/test-cases/image-state-change/foo.c create mode 100644 unit-tests/test-cases/image-state-change/main.c create mode 100644 unit-tests/test-cases/image-state-deny-OFI/Makefile create mode 100644 unit-tests/test-cases/image-state-deny-OFI/bar.c create mode 100644 unit-tests/test-cases/image-state-deny-OFI/foo.c create mode 100644 unit-tests/test-cases/image-state-deny-OFI/main.c create mode 100644 unit-tests/test-cases/image-state-deny-dlclose/Makefile create mode 100644 unit-tests/test-cases/image-state-deny-dlclose/base.c create mode 100644 unit-tests/test-cases/image-state-deny-dlclose/foo.c create mode 100644 unit-tests/test-cases/image-state-deny-dlclose/main.c create mode 100644 unit-tests/test-cases/image-state-deny/Makefile create mode 100644 unit-tests/test-cases/image-state-deny/bar.c create mode 100644 unit-tests/test-cases/image-state-deny/foo.c create mode 100644 unit-tests/test-cases/image-state-deny/main.c create mode 100644 unit-tests/test-cases/image-state-dependents-initialized/Makefile create mode 100644 unit-tests/test-cases/image-state-dependents-initialized/bar.c create mode 100644 unit-tests/test-cases/image-state-dependents-initialized/foo.c create mode 100644 unit-tests/test-cases/image-state-dependents-initialized/main.c create mode 100644 unit-tests/test-cases/insert-libraries-with-initializer/base.c create mode 100644 unit-tests/test-cases/insert-libraries-with-initializer/base.h create mode 100644 unit-tests/test-cases/insert-libraries-with-initializer/foo1.c create mode 100644 unit-tests/test-cases/insert-libraries-with-initializer/foo2.c create mode 100644 unit-tests/test-cases/insert-libraries-with-initializer/insert.c create mode 100644 unit-tests/test-cases/operator-new/Makefile create mode 100644 unit-tests/test-cases/operator-new/main.cxx create mode 100644 unit-tests/test-cases/pie-basic/Makefile create mode 100644 unit-tests/test-cases/pie-basic/main.c create mode 100644 unit-tests/test-cases/pie-big/Makefile create mode 100644 unit-tests/test-cases/pie-big/main.c create mode 100644 unit-tests/test-cases/pie-custom-stack/Makefile create mode 100644 unit-tests/test-cases/pie-custom-stack/main.c rename unit-tests/test-cases/{flat-lookup-everywhere => progname}/Makefile (86%) create mode 100644 unit-tests/test-cases/progname/main.c create mode 100644 unit-tests/test-cases/re-export-dylib/Makefile create mode 100644 unit-tests/test-cases/re-export-dylib/bar.c create mode 100644 unit-tests/test-cases/re-export-dylib/foo.c create mode 100644 unit-tests/test-cases/re-export-dylib/main.c create mode 100644 unit-tests/test-cases/re-export-framework/Makefile create mode 100644 unit-tests/test-cases/re-export-framework/bar.c create mode 100644 unit-tests/test-cases/re-export-framework/foo.c create mode 100644 unit-tests/test-cases/re-export-framework/main.c create mode 100644 unit-tests/test-cases/re-export-sub-framework/Makefile create mode 100644 unit-tests/test-cases/re-export-sub-framework/bar.c create mode 100644 unit-tests/test-cases/re-export-sub-framework/foo.c create mode 100644 unit-tests/test-cases/re-export-sub-framework/main.c create mode 100644 unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile create mode 100644 unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx create mode 100644 unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c create mode 100644 unit-tests/test-cases/read-only-import-shared-cache-override/Makefile create mode 100644 unit-tests/test-cases/read-only-import-shared-cache-override/foo.c create mode 100644 unit-tests/test-cases/read-only-import-shared-cache-override/main.c create mode 100644 unit-tests/test-cases/read-only-stubs/Makefile create mode 100644 unit-tests/test-cases/read-only-stubs/bar.c create mode 100644 unit-tests/test-cases/read-only-stubs/foo.c create mode 100644 unit-tests/test-cases/read-only-stubs/main.c create mode 100644 unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile create mode 100644 unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c create mode 100644 unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c create mode 100644 unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile create mode 100644 unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c create mode 100644 unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c create mode 100644 unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile create mode 100644 unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c create mode 100644 unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c create mode 100644 unit-tests/test-cases/rpath-basic/Makefile create mode 100644 unit-tests/test-cases/rpath-basic/bar.c create mode 100644 unit-tests/test-cases/rpath-basic/foo.c create mode 100644 unit-tests/test-cases/rpath-basic/main.c create mode 100644 unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile create mode 100644 unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c create mode 100644 unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c create mode 100644 unit-tests/test-cases/rpath-dlopen-in-dylib/main.c create mode 100644 unit-tests/test-cases/rpath-dlopen-indirect/Makefile create mode 100644 unit-tests/test-cases/rpath-dlopen-indirect/bar.c create mode 100644 unit-tests/test-cases/rpath-dlopen-indirect/foo.c create mode 100644 unit-tests/test-cases/rpath-dlopen-indirect/main.c create mode 100644 unit-tests/test-cases/rpath-dlopen-leak/Makefile create mode 100644 unit-tests/test-cases/rpath-dlopen-leak/foo.c create mode 100644 unit-tests/test-cases/rpath-dlopen-leak/main.c create mode 100644 unit-tests/test-cases/rpath-dlopen/Makefile create mode 100644 unit-tests/test-cases/rpath-dlopen/foo.c create mode 100644 unit-tests/test-cases/rpath-dlopen/main.c create mode 100644 unit-tests/test-cases/rpath-executable_path/Makefile create mode 100644 unit-tests/test-cases/rpath-executable_path/bar.c create mode 100644 unit-tests/test-cases/rpath-executable_path/foo.c create mode 100644 unit-tests/test-cases/rpath-executable_path/main.c create mode 100644 unit-tests/test-cases/rpath-indirect-suid/Makefile create mode 100644 unit-tests/test-cases/rpath-indirect-suid/bar.c create mode 100644 unit-tests/test-cases/rpath-indirect-suid/foo.c create mode 100644 unit-tests/test-cases/rpath-indirect-suid/main.c create mode 100644 unit-tests/test-cases/rpath-loader_path-dlopen/Makefile create mode 100644 unit-tests/test-cases/rpath-loader_path-dlopen/bar.c create mode 100644 unit-tests/test-cases/rpath-loader_path-dlopen/baz.c create mode 100644 unit-tests/test-cases/rpath-loader_path-dlopen/foo.c create mode 100644 unit-tests/test-cases/rpath-loader_path-dlopen/main.c create mode 100644 unit-tests/test-cases/rpath-loader_path/Makefile create mode 100644 unit-tests/test-cases/rpath-loader_path/foo.c create mode 100644 unit-tests/test-cases/rpath-loader_path/main.c create mode 100644 unit-tests/test-cases/rpath-nesting/Makefile create mode 100644 unit-tests/test-cases/rpath-nesting/bar.c create mode 100644 unit-tests/test-cases/rpath-nesting/baz.c create mode 100644 unit-tests/test-cases/rpath-nesting/foo.c create mode 100644 unit-tests/test-cases/rpath-nesting/main.c create mode 100644 unit-tests/test-cases/shared-cache-symlink/Makefile create mode 100644 unit-tests/test-cases/shared-cache-symlink/main.c create mode 100644 unit-tests/test-cases/suid-executable_path/Makefile create mode 100644 unit-tests/test-cases/suid-executable_path/foo.c create mode 100644 unit-tests/test-cases/suid-executable_path/main.c create mode 100644 unit-tests/test-cases/template/Makefile rename unit-tests/test-cases/{flat-lookup-everywhere => template}/main.c (88%) create mode 100644 unit-tests/test-cases/zero-fill-segment/Makefile create mode 100644 unit-tests/test-cases/zero-fill-segment/foo.c create mode 100644 unit-tests/test-cases/zero-fill-segment/main.c create mode 100644 unit-tests/test-cases/zero-fill-segment/zero.s create mode 100644 unit-tests/test-cases/zero-length-segment/Makefile create mode 100644 unit-tests/test-cases/zero-length-segment/foo.c create mode 100644 unit-tests/test-cases/zero-length-segment/main.c diff --git a/doc/man/man1/dyld.1 b/doc/man/man1/dyld.1 index 9e0b776..0ef371a 100644 --- a/doc/man/man1/dyld.1 +++ b/doc/man/man1/dyld.1 @@ -1,4 +1,4 @@ -.TH DYLD 1 "January 15, 2005" "Apple Computer, Inc." +.TH DYLD 1 "March 23, 2007" "Apple Inc." .SH NAME dyld \- the dynamic link editor .SH SYNOPSIS @@ -12,6 +12,8 @@ DYLD_FALLBACK_LIBRARY_PATH .br DYLD_ROOT_PATH .br +DYLD_SHARED_REGION +.br DYLD_INSERT_LIBRARIES .br DYLD_FORCE_FLAT_NAMESPACE @@ -28,11 +30,9 @@ DYLD_PRINT_LIBRARIES_POST_LAUNCH .br DYLD_BIND_AT_LAUNCH .br -DYLD_PREBIND_DEBUG -.br -DYLD_NEW_LOCAL_SHARED_REGIONS +DYLD_NO_FIX_PREBINDING .br -DYLD_IGNORE_PREBINDING +DYLD_DISABLE_DOFS .br DYLD_PRINT_APIS .br @@ -45,6 +45,8 @@ DYLD_PRINT_REBASINGS DYLD_PRINT_SEGMENTS .br DYLD_PRINT_STATISTICS +.br +DYLD_PRINT_DOFS .SH DESCRIPTION The dynamic linker uses the following environment variables. They affect any program that uses the dynamic linker. @@ -118,6 +120,15 @@ to $(HOME)/lib:/usr/local/lib:/lib:/usr/lib. This is a colon separated list of directories. The dynamic linker will prepend each of this directory paths to every image access until a file is found. .TP +.B DYLD_SHARED_REGION +This can be "use" (the default), "avoid", or "private". Settting 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() +back in a private copy of the dyld shared cache in the shared region address +range. This is only useful if the shared cache on disk has been updated +and is different than the shared cache in use. +.TP .B DYLD_INSERT_LIBRARIES This is a colon separated list of dynamic libraries to load before the ones specified in the program. This lets you test new modules of existing dynamic @@ -163,74 +174,22 @@ but the printing starts after the program gets to its entry point. .TP .B DYLD_BIND_AT_LAUNCH When this is set, the dynamic linker binds all undefined symbols -the program needs at launch time. This includes function symbols that can are normally lazily bound at the time of their first call. -.TP -.B DYLD_PREBIND_DEBUG -When this is set, the dynamic linker prints diagnostics about -launching prebound programs and libraries. This lets you determine why a -program is not being launched prebound. -You can view the recorded library time stamps with the -.B \-Lv -option to -.IR otool (1). -.TP -.PP -For secure programs that are UNIX set uid or set gid, the dynamic linker will -not use the dyld environment variables for path searching and library insertion, -unless the program is run as the real user. For secure programs, the dynamic -linker clears out the value of the dyld path and insertion environment -variables. This is so that if a program is -.IR exec (2)'ed -from a secure program too will not have it's libraries searched for, as well. -For statically linked secure programs that -.IR exec (2) -other programs that might use the dynamic linker, they too should clear out the -values of the dyld path and insertion environment variables. -.TP -.B DYLD_NEW_LOCAL_SHARED_REGIONS -When set, the dynamic linker directs the system to provide a new set of shared -regions as the repository for library load requests for dynamic libraries -built with -.SM MH_SPLIT_SEGS -(split shared libraries). - -Split shared libraries reside in a defined contiguous region of address space -in all dynamic linker runtime processes. This space is backed by named regions -or sub-maps. These sub-maps are owned by the system and items which are to -mapped into them must be mapped via the -.IR load_shared_file (2) -call. The use of -sub-maps promotes a high degree of system resource sharing between the -processes which incorporate and use them. However, some processes require -either additional or different libraries to be loaded into the shared region. -While there is some space available within the shared region for alternate and -new shared libraries, it is inappropriate to use that area for temporary or -private libraries. Setting the -.SM DYLD_NEW_LOCAL_SHARED_REGIONS -flag will cause -all children of the current process to have their own set of sub-maps. In this -way the libraries found in the children's submaps will not be caused to be -present in the submaps shared by the rest of the system. - -.SM DYLD_NEW_LOCAL_SHARED_REGIONS -should be set by anyone wishing to run -non-standard or temporary split shared libraries by setting an explicit path to -point to them. i.e. by using the DYLD_LIBRARY_PATH environment variable -instead of changing the root by executing a -.IR chroot (2) -call. +the program needs at launch time. This includes function symbols that can are normally +lazily bound at the time of their first call. .TP .B DYLD_PRINT_STATISTICS Right before the process's main() is called, dyld prints out information about how dyld spent its time. Useful for analyzing launch performance. .TP -.B DYLD_IGNORE_PREBINDING { app | all } -Valid values are "app", "all", and "" (empty). The variable is useful for testing -how various mixes of prebound and unprebound libraries perform. When set to "all", -all prebinding is ignored. That is, dyld fixes up any prebound images as if the prebinding -in it was invalid. When set to "all", just the prebinding information in main -executables is ignored. When set the nothing, the prebinding in split-seg libraries -is used, by all other prebinding is ignored. +.B DYLD_NO_FIX_PREBINDING +Normally, dyld will trigger the dyld shared cache to be regenerated if it notices +the cache is out of date while launching a process. If this environment variable +is set, dyld will not trigger a cache rebuild. This is useful to set while installing +a large set of OS dylibs, to ensure the cache is not regenerated until the install +is complete. +.TP +.B DYLD_DISABLE_DOFS +Causes dyld not register dtrace static probes with the kernel. .TP .B DYLD_PRINT_INITIALIZERS Causes dyld to print out a line when running each initializers in every image. Initializers @@ -242,12 +201,14 @@ Causes dyld to print a line whenever a dyld API is called (e.g. NSAddImage()). .TP .B DYLD_PRINT_SEGMENTS Causes dyld to print out a line containing the name and address range of each mach-o segment -that dyld maps in. +that dyld maps. In addition it prints information about if the image was from the dyld +shared cache. .TP .B DYLD_PRINT_BINDINGS 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. .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 new file mode 100644 index 0000000..a370962 --- /dev/null +++ b/doc/man/man1/update_dyld_shared_cache.1 @@ -0,0 +1,79 @@ +.Dd March 23, 2007 +.Dt update_dyld_shared_cache 1 +.Os Darwin +.Sh NAME +.Nm update_dyld_shared_cache +.Nd "Updates dyld's shared cache" +.Sh SYNOPSIS +.Nm +.Op Fl root Ar directory +.Op Fl arch Ar arch +.Op Fl force +.Op Fl debug +.Op Fl sort_by_name +.Op Fl universal_boot +.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. +.Pp +The dyld shared cache +is mapped by dyld into a process at launch time. Later, when loading +any mach-o image, dyld will first check if is in the share cache, and if +it is will use that pre-bound version instead of opening, mapping, and binding +the original file. This results in significant performance improvements to +launch time. +.Pp +.Nm update_dyld_shared_cache +scans the directory /var/db/dyld/shared_region_roots for text files containing paths to +mach-o executables. The full dependencies of all dylibs required by those executables is +used to determine which libraries are commonly used and should be placed in the +shared cache. If one of the text files contains a path to a dylib, that dylib and its +depenents 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 +map of the cached are generated to /var/db/dyld. +.Pp +You must be root to run this tool. +.Pp +The options are as follows: +.Bl -tag +.It Fl root Ar directory +This option specifies the root of an OS installation for which dyld's +shared cache should be updated. This allosw you to update the +shared cache on a non-running version of the OS. The cache files +are created in the var/db/dyld directory of the specified directory. +.It Fl arch Ar arch +By default +.Nm update_dyld_shared_cache +generates cache files for all architecture that the current machine +can execute. You can override this behavior by specifying one or more -arch options and list +exactly which architectures should have their shared caches updated. +.It Fl force +This option will cause +.Nm update_dyld_shared_cache +to regenerated the shared cache files even if they appear to be already up-to-date. +.It Fl debug +This option prints out additional information about the work being done. +.It Fl 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 +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. +.El +.Sh FILES +.Tp +/var/db/dyld/shared_region_roots +directory of text files with paths to mach-o images used to determine what should be in shared cache. +.Sh SEE ALSO +.Xr dyld 1 diff --git a/doc/man/man1/update_prebinding.1 b/doc/man/man1/update_prebinding.1 new file mode 100644 index 0000000..a441bed --- /dev/null +++ b/doc/man/man1/update_prebinding.1 @@ -0,0 +1 @@ +.so man1/update_dyld_shared_cache.1 diff --git a/doc/man/man3/NSModule.3 b/doc/man/man3/NSModule.3 deleted file mode 100644 index 3677477..0000000 --- a/doc/man/man3/NSModule.3 +++ /dev/null @@ -1,610 +0,0 @@ -.TH NSModule 3 "October 6, 2003" "Apple Computer, Inc." -.SH NAME -NSModule \- programmatic interface for working with modules and symbols -.SH SYNOPSIS -.nf -.PP -#include -.sp .5 -typedef void * NSModule; -.sp .5 -extern NSModule NSLinkModule( - NSObjectFileImage objectFileImage, - const char *moduleName, - unsigned long options); -.sp .5 -extern enum DYLD_BOOL NSUnLinkModule( - NSModule module, - unsigned long options); -.sp .5 -extern const char * NSNameOfModule( - NSModule m); -.sp .5 -extern const char * NSLibraryNameForModule( - NSModule m); -.sp 2 -typedef void * NSSymbol; -.sp .5 -extern enum DYLD_BOOL NSIsSymbolNameDefined( - const char *symbolName); -.sp .5 -extern enum DYLD_BOOL NSIsSymbolNameDefinedWithHint( - const char *symbolName - const char *libraryNameHint); -.sp .5 -extern enum DYLD_BOOL NSIsSymbolNameDefinedInImage( - const struct mach_header *image, - const char *symbolName); -.sp .5 -extern NSSymbol NSLookupAndBindSymbol( - const char *symbolName); -.sp .5 -extern NSSymbol NSLookupAndBindSymbolWithHint( - const char *symbolName - const char *libraryNameHint); -.sp .5 -extern NSSymbol NSLookupSymbolInModule( - NSModule module, - const char *symbolName); -.sp .5 -extern NSSymbol NSLookupSymbolInImage( - const struct mach_header *image, - const char *symbolName, - unsigned long options); -.sp .5 -extern const char * NSNameOfSymbol( - NSSymbol symbol); -.sp .5 -extern void * NSAddressOfSymbol( - NSSymbol symbol); -.sp .5 -extern NSModule NSModuleForSymbol( - NSSymbol symbol); -.sp .5 -extern enum DYLD_BOOL NSAddLibrary( - const char *pathName); -.sp .5 -extern enum DYLD_BOOL NSAddLibraryWithSearching( - const char *pathName); -.sp .5 -extern const struct mach_header * NSAddImage( - const char *image_name, - unsigned long options); -.sp .5 -extern long NSVersionOfRunTimeLibrary( - const char *libraryName); -.sp .5 -extern long NSVersionOfLinkTimeLibrary( - const char *libraryName); -.sp .5 -extern int _NSGetExecutablePath( - char *buf, - unsigned long *bufsize) -.sp 2 -extern void NSInstallLinkEditErrorHandlers( - NSLinkEditErrorHandlers *handlers); -.sp .5 -extern void NSLinkEditError( - NSLinkEditErrors *c, - int *errorNumber, - const char **fileName, - const char **errorString); -.if -.SH "FUTURE SYNOPSIS" -.nf -.PP -extern NSModule NSReplaceModule( - NSModule moduleToReplace, - NSObjectFileImage newObjectFileImage, - unsigned long options); -.fi -.PP -These routines are the programmatic interface for working with modules and -symbols in a program. A program is composed of a set of images, an executable, -plugins, and dynamic shared libraries. An image which is an executable or a -plugin is composed of one module containing a collection of symbols. A dynamic -shared library is composed of one or more modules with each of those modules -containing a separate collection of symbols. If a symbol is used from a module -then all the symbols from that module are used. -.PP -When a program is executed it selectively binds the symbols it needs from the -modules in the dynamic libraries that are loaded. Normally a program is -staticly linked against a set of dynamic shared libraries when it is built. -So when the program is executed the dynamic linker will automaticly load those -dynamic shared libraries. -.PP -A program may programmatically load plugins after it starts executing and that -is done with two sets of API's. The first is the API's of -.IR NSObjectFileImage (3) -and the second is -.I NSLinkModule. -Unlike modules in the dynamic libraries when a plugin is loaded it is not -selectively bound to but always bound into the program. -.PP -.I NSLinkModule -links the specified object file image into the program and returns the module -handle for it. -Currently the implementation is limited to only Mach-O MH_BUNDLE types which -are used for plugins. -A module name is specified when a module is linked so that later -.I NSNameOfModule -can be used with the module handle and to do things like report errors. -If you want -.IR gdb (1) -to be able to debug your module, when calling -.I NSLinkModule -you should pass the image path as the module name. -When a module is linked, all libraries referenced by the module are added to -the list of libraries to be searched. -The parameter, -.I options, -can have a set of options or'ed together. The options for -.I NSLinkModule -are as follows: -.TP -.B NSLINKMODULE_OPTION_NONE -This specifies no options. With this the global symbols from the module are -made part of the global symbol table of the program. If any errors occur the -handlers installed with -.I NSInstallLinkEditErrorHandlers -are called or the default action is taken if there are no handlers. -.TP -.B NSLINKMODULE_OPTION_BINDNOW -This option causes the dynamic link editor to bind all undefined references for -the loaded module and not allow references to be bound as needed. This affects -all the references in the module and all of the dependent references. -.TP -.B NSLINKMODULE_OPTION_PRIVATE -With this option the global symbols from the module are not made part of -the global symbol table of the program. The global symbols of the -module can then be looked up using -.I NSLookupSymbolInModule. -.TP -.B NSLINKMODULE_OPTION_RETURN_ON_ERROR -With this option if errors occur while binding this module it is automaticly -unloaded and -.SM NULL -is returned as the module handle. To get the error information for the module -that failed to load the routine -.I NSLinkEditError -is then used. It has the same parameters as the link edit error handler (see -below) except all the parameters are pointers in which the information is -returned indirectly. -.TP -.B NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES -With this option the module init routines are not called. This is only useful -to the fix-and-continue implementation. -.TP -.B NSLINKMODULE_OPTION_TRAILING_PHYS_NAME -With this option the parameter, -.I moduleName -is assumed to be a string with the logical name of the image with the physical -name of the object file tailing after the NULL character of the logical name. -This is only useful to the zero-link implementation. -.PP -.I NSUnLinkModule -unlinks the specified module handle from the program. Currently the -implementation is limited to only allow modules linked with -.I NSLinkModule -to be unlinked. The parameter, -.I options, -can have a set of options or'ed together. The options for -.I NSUnLinkModule -are as follows: -.TP -.B NSUNLINKMODULE_OPTION_NONE -This specifies no options. With this the module is unlinked from the program -and the memory for the module is deallocated. If any errors occur the -handlers installed with -.I NSInstallLinkEditErrorHandlers -are called or the default action is taken if there are no handlers. -.TP -.B NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED -With this option the memory for the module is not deallocated allowing pointers -into the module to still be valid. -.TP -.B NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES -With this option any lazy references (direct function calls) to symbols defined -in the module are reset to be bound on first call again and not cause any -undefined symbol errors. Currently this is only implemented for the PowerPC -architecture. -.PP -.I NSNameOfModule -is passed a module handle and returns the name of the module. If the module -handle is invalid -.SM NULL -is returned. -.PP -.I NSLibraryNameForModule -is passed a module handle and returns the name of the library the module is in -if any. If the module handle is for a module that is not in a library (in the -executable or a plugin) or the module handle is invalid -.SM NULL -is returned. -.PP -.I NSIsSymbolNameDefined -is passed a global symbol name (global 'C' symbols names are preceded with an -underbar '\_') and returns -.SM TRUE -or -.SM FALSE -based on if the symbol is defined in the program's global symbol table. -If the symbol is not defined no error occurs. -.PP -.I NSIsSymbolNameDefinedWithHint -is the same as -.I NSIsSymbolNameDefined -but the -.I libraryNameHint -parameter provides a hint as to where to start the lookup in a prebound -program. The -.I libraryNameHint -parameter is matched up with the actual library install names with -.IR strstr (3). -.PP -.I NSIsSymbolNameDefinedInImage -is passed a pointer to the mach_header of a mach_header structure of a -dynamic library being used by the program and a symbol name. This returns -.SM TRUE -or FALSE -based on if the symbol is defined in the specified image or one of the image's -sub-frameworks or sub-umbrellas. -If the program was built with the -.IR ld (1) -.B \-force_flat_namespace -flag or executed with the environment variable -.SM DYLD_FORCE_FLAT_NAMESPACE -set and the pointer to a mach_header structure is not of a bundle loaded with -the -.B NSLINKMODULE_OPTION_PRIVATE -option of -.IR NSLinkModule (3) -then the pointer to a mach_header is ignored and the symbol is looked up in -all the images using the first definition if found. -.PP -The image handle parameter for -.I NSLookupSymbolInImage -and -.I NSIsSymbolNameDefinedInImage -is a pointer to a read-only mach header structure of a dynamic library being -used by the program. Besides the -.IR NSAddImage (3) -routine the pointer to a mach header can also be obtained by using a link editor -defined symbol as in and described on the -.IR ld (1) -man page. -Also the -.IR dyld (3) -routine -.IR _dyld_get_image_header (3) -and the mach_header pointer arguments to the call back routines called from -.IR _dyld_register_func_for_add_image (3) -routines can also be used. -.PP -.I NSLookupAndBindSymbol -is passed a global symbol name and looks up and binds the symbol into the -program. -It returns an NSSymbol for the symbol. If any errors occur the handlers -installed with -.I NSInstallLinkEditErrorHandlers -are called or the default action is taken if there are no handlers. -.PP -.I NSLookupAndBindSymbolWithHint -is the same as -.I NSLookupAndBindSymbol -but the -.I libraryNameHint -parameter provides a hint as to where to start the lookup in a prebound -program. The -.I libraryNameHint -parameter is matched up with the actual library install names with -.IR strstr (3). -.PP -.I NSLookupSymbolInModule -is passed a symbol name and a module handle and looks up the symbol in that -module. Currently this is only implemented for module handles returned with -.I NSLinkModule. -If the symbol is found an NSSymbol for the symbol is returned otherwise -.SM NULL -is returned and no error occurs. -.PP -.I NSLookupSymbolInImage -is passed a pointer to a mach_header structure of a dynamic library being used -by the program and a symbol name. It returns an NSSymbol for the symbol for -defined in the specified image or the image's sub-frameworks or sub-umbrellas. -If the program was built with the -.IR ld (1) -.B \-force_flat_namespace -flag or executed with the environment variable -.SM DYLD_FORCE_FLAT_NAMESPACE -set and the pointer to a mach_header structure is not of a bundle loaded with -the -.B NSLINKMODULE_OPTION_PRIVATE -option of -.IR NSLinkModule (3) -then the pointer to a mach_header is ignored and the symbol is looked up in -all the images using the first definition found. -If the option -.SM NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR -is not used if any errors occur the handlers installed with -.I NSInstallLinkEditErrorHandlers -are called or the default action is taken if there are no handlers. -The options of -.I NSLookupSymbolInImage -are as follows: -.TP -.B NSLOOKUPSYMBOLINIMAGE_OPTION_BIND -Just bind the non-lazy symbols of module that defines the -.I symbolName -and let all lazy symbols in the module be bound on first call. This should be -used in the normal case for a trusted module expected to bind without any errors -like a module in a system supplied library. -.TP -.B NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW -Bind all the non-lazy and lazy symbols of module that defines the -.I symbolName -and let all dependent symbols in the needed libraries be bound as needed. This -would be used for a module that might not be expected bind without errors but -links against only system supplied libraries which are expected to bind without -any errors. -.TP -.B NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY -Bind all the symbols of the module that defines the -.I symbolName -and all the dependent symbols of all needed libraries. This should only be -used for things like signal handlers and linkedit error handlers that can't -bind other symbols when executing to handle the signal or error. -.TP -.B NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR -With this option if errors occur while binding the module that defines the -.I symbolName -then the module is automaticly unloaded and -.SM NULL -is returned as the NSSymbol. To get the error information for why the module -that failed to bind the routine -.I NSLinkEditError -is then used. It has the same parameters as the link edit error handler (see -below) except all the parameters are pointers in which the information is -returned indirectly. -.PP -.I NSNameOfSymbol -is passed an NSSymbol and returns the name of the symbol. -.PP -.I NSAddressOfSymbol -is passed an NSSymbol and returns the address of the symbol. -.PP -.I NSModuleForSymbol -is passed an NSSymbol and returns the NSModule that symbol is defined in. -.PP -.I NSAddLibrary -is passed the file name of a dynamic shared library to be added to the search -list. If it is successful it returns -.SM TRUE -else it returns -.SM FALSE. -.PP -.I NSAddLibraryWithSearching -is passed the file name of a dynamic shared library to be added to the search -list the file name passed will be effected by the various -.SM DYLD -environment variables as if this library were linked into the program. If it -is successful it returns -.SM TRUE -else it returns -.SM FALSE. -.PP -.I NSAddImage -is passed the file name of a dynamic shared library to be added to the search -list of the program if not already loaded. It returns a pointer to the -mach_header structure of the dynamic library being used by the program. -For best performance of this routine if the library is expected to be already -loaded by the program the -.I image_name -should be a full path name and the same as the name recorded by the program. -If it is a symlink then an -.IR open (2) -and an -.IR fstat (2) -are needed to determine it is the same file as one already loaded. -.PP -If the dynamic shared library has not already been loaded it along with all the -needed dependent libraries are loaded. With the options parameter -.SM NSADDIMAGE_OPTION_NONE -then any error in loading will cause the linkEdit error handler set by -.IR NSInstallLinkEditErrorHandlers (3) -to be called or the default action of printing the error and exiting to be -taken. The other options of -.I NSAddImage -are as follows: -.TP -.B NSADDIMAGE_OPTION_RETURN_ON_ERROR -With this option if errors occur while loading this library it is automatically -unloaded and -.SM NULL -is returned. To get the error information for the library that failed to load -the routine -.I NSLinkEditError -is then used. It has the same parameters as the link edit error handler (see -below) except all the parameters are pointers in which the information is -returned indirectly. -.TP -.B NSADDIMAGE_OPTION_WITH_SEARCHING -With this option the -.I image_name -passed for the library and all its dependents will be effected by the various -.SM DYLD -environment variables as if this library were linked into the program. -.TP -.B NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED -With this option if the -.I image_name -passed for the library has not already been loaded it is not loaded. Only if -it has been loaded the pointer to the mach_header will not be -.SM NULL. -.TP -.B NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME -When this option is specified if a later load of a dependent dynamic library -with a file system path is needed by an image that matches the install name of -the dynamic library loaded with this option, then the dynamic library loaded -with the call to NSAddImage() is used in place of the dependent dynamic library. -.PP -.I NSVersionOfRunTimeLibrary -is passed the install name of a dynamic shared library and returns -current_version number of the library the program is using or \-1 if the -program is not using that library. -.PP -.I NSVersionOfLinkTimeLibrary -is passed the install name of a dynamic shared library and returns the -current_version number of the library the executable program was built -with or \-1 if the program was not built with that library. -.PP -.I _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 -buffer is not large enough, \-1 is returned and the expected buffer size is -copied in *bufsize. Note that _NSGetExecutablePath will return "a path" to -the executable not a "real path" to the executable. That is the path may be -a symbolic link and not the real file. And with deep directories the total -bufsize needed could be more than MAXPATHLEN. -.SH ERROR HANDLING -.PP -.I NSInstallLinkEditErrorHandlers -is passed a pointer to a NSLinkEditErrorHandlers which contains three function -pointers to be used for handling dynamic link errors. The prototypes for these -functions are given in the following typedef: -.RS -.nf -typedef struct { - void (*undefined)(const char *symbolName); - NSModule (*multiple)(NSSymbol s, NSModule oldModule, NSModule newModule); - void (*linkEdit)(NSLinkEditErrors errorClass, int errorNumber, - const char *fileName, const char *errorString); -} NSLinkEditErrorHandlers; -.fi -.RE -.PP -The first two functions allow the programmer to direct the link edit processing -of undefined symbols and multiply defined symbols. -The third function allows the programmer to catch all other link editor -errors. -.PP -The state when one of the user error functions gets called will be such that no -module will be partially loaded (except in the case of resource errors like out -of memory and other relocation errors). -However, with undefined symbol errors those modules referencing undefined -symbols will be partially bound, and use of such modules can and will crash the -program. -.PP -Great care should be taken when implementing these functions, as the program is -running in a state that will crash if it uses an unbound symbol. -To be safe, these functions should only rely on other things in the same module -or in the executable. -.PP -If the user does not supply these functions, the default will be to write an -error message on to file descriptor 2 (usually stderr) and exit the program -(except for the -.I linkEdit -error handler when the -.I NSLinkEditErrors -is NSLinkEditWarningError, then the default is to do nothing). -.PP -The specified undefined handler may make calls to any of the runtime loading -functions to add modules based on the undefined symbol name. -After dealing with this symbol name successfully (by doing a runtime loading -operation to resolve the undefined reference) the handler simply returns. -If more symbol's names remain undefined the handler will be called repeatedly -with an undefined symbol name. -If the handler can't deal with the symbol it should not return (put up a panel, -abort, etc) and cause the program to exit. -Or it can remove itself as the undefined handler and return which will cause -the default action of printing the undefined symbol names and exiting. -.PP -The specified multiply defined symbol handler is called during the process of -runtime linking and thus it may not call any of the runtime loading functions -as only one set of linking operations can be performed in the task at a time. -The only programmatic functions that can be called from a multiply defined -symbol handler are -.I NSNameOfSymbol, -.I NSNameOfModule -and -.I NSLibraryNameForModule -(provided they are linked into the program before the handler is called). -This handler returns the module handle for the symbol that is to be used for -further link editing, either the -.I oldModule -or the -.I newModule. -It may also record one of the module handles to later take action after the -runtime linking process has completed (for example later unlink the module). -The dynamic link editor updates the references to the symbol if the handler -specifies the new symbol is to be used. -The references which are updated are those that the compiler system generated -as indirect references. Initialized data and references that were created at -runtime are not effected. -.PP -The specified -.I linkEdit -error handler is called for all other runtime linking errors. -These other runtime linking errors are either warnings or fatal errors. -If the user's link edit error handler function returns -for a fatal error it will cause the program to exit. -There is small set of major error classes which have specific error numbers. -These numbers are be passed in the parameter -.I errorClass. -These major error classes include: -.RS -.nf -typedef enum { - NSLinkEditFileAccessError, - NSLinkEditFileFormatError, - NSLinkEditMachResourceError, - NSLinkEditUnixResourceError, - NSLinkEditOtherError, - NSLinkEditWarningError, - NSLinkEditMultiplyDefinedError, - NSLinkEditUndefinedError -} NSLinkEditErrors; -.fi -.RE -.PP -For the error class NSLinkEditUnixResourceError the -.I errorNumber -parameter will be an -.I errno -value (see -.IR intro (2)). -For the error class NSLinkEditMachResourceError the -.I errorNumber -parameter will be a -.I kern_return_t -value. -For the error class NSLinkEditOtherError the -.I errorNumber -parameter will be a one of the following values: -.RS -.nf -typedef enum { - NSOtherErrorRelocation, - NSOtherErrorLazyBind, - NSOtherErrorIndrLoop, - NSOtherErrorLazyInit, - NSOtherErrorInvalidArgs -} NSOtherErrorNumbers; -.fi -.RE -.PP -For all errors, an attempt to pass an error string will be made. -In some cases such as resource errors, it may not be possible to return a -string. -In those cases the -.I errorString -parameter will be -.sm NULL. -.PP -For file access errors and file format errors, an attempt to return a file name -will also be passed, and if that is not possible the -.I filename -parameter will be -.sm NULL. -.SH ALSO SEE -NSObjectFileImage(3), dyld(3) diff --git a/doc/man/man3/NSObjectFileImage.3 b/doc/man/man3/NSObjectFileImage.3 deleted file mode 100644 index c235960..0000000 --- a/doc/man/man3/NSObjectFileImage.3 +++ /dev/null @@ -1,151 +0,0 @@ -.TH NSObjectFileImage 3 "March 14, 2003" "Apple Computer, Inc." -.SH NAME -NSObjectFileImage \- programmatic interface for working with Mach-O files -.SH SYNOPSIS -.nf -.PP -#include -.sp .5 -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile( - const char *pathName, - NSObjectFileImage *objectFileImage); -.sp .5 -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory( - void *address, - unsigned long size, - NSObjectFileImage *objectFileImage); -.sp .5 -extern NSObjectFileImageReturnCode NSCreateCoreFileImageFromFile( - const char *pathName, - NSObjectFileImage *objectFileImage); -.sp .5 -extern enum DYLD_BOOL NSDestroyObjectFileImage( - NSObjectFileImage objectFileImage); -.sp .5 -extern unsigned long NSSymbolDefinitionCountInObjectFileImage( - NSObjectFileImage objectFileImage); -.sp .5 -extern const char * NSSymbolDefinitionNameInObjectFileImage( - NSObjectFileImage objectFileImage, - unsigned long ordinal); -.sp .5 -extern unsigned long NSSymbolReferenceCountInObjectFileImage( - NSObjectFileImage objectFileImage); -.sp .5 -extern const char * NSSymbolReferenceNameInObjectFileImage( - NSObjectFileImage objectFileImage, - unsigned long ordinal, - enum DYLD_BOOL *tentative_definition); /* can be NULL */ -.sp .5 -extern enum DYLD_BOOL NSIsSymbolDefinedInObjectFileImage( - NSObjectFileImage objectFileImage, - const char *symbolName); -.sp .5 -extern void * NSGetSectionDataInObjectFileImage( - NSObjectFileImage objectFileImage, - const char *segmentName, - const char *sectionName, - unsigned long* size); /* can be NULL */ -.sp .5 -extern enum DYLD_BOOL NSHasModInitObjectFileImage( - NSObjectFileImage objectFileImage); -.fi -.SH DESCRIPTION -.PP -These routines are the programmatic interface for working with Mach-O files. -They bring the Mach-O file into memory and the API allows the file to -be inspected or loaded into the program. On creation of an object file image -it is checked to insure it is a valid format and it is compatible with the host -machine's cpu architecture. -.PP -.I NSCreateObjectFileImageFromFile -takes the parameter -.I pathName -as the path name to the file name in the file system and creates and returns -an NSObjectFileImage. Currently only -.SM MH_BUNDLE -files can be used with -.I NSCreateObjectFileImageFromFile -which can then be loaded into the program using -.IR NSLinkModule (3). -If the file is valid an NSObjectFileImage is returned and the return code is -NSObjectFileImageSuccess. -.I NSCreateObjectFileImageFromMemory -does the same as -.I NSCreateObjectFileImageFromFile -but takes two parameters -.I address -and -.I size -for the Mach-O file that is in memory. -.PP -.I NSCreateCoreFileImageFromFile -takes the parameter -.I pathName -as the path name to a core file in the file system and creates and returns -an NSObjectFileImage. This NSObjectFileImage can then can be loaded into a -task with -.IR _dyld_debug_task_from_core (3) -to determine what libraries were loaded and which modules were linked. -.PP -.I NSSymbolDefinitionCountInObjectFileImage -returns the number of symbol definitions in the NSObjectFileImage. -.PP -.I NSSymbolDefinitionNameInObjectFileImage -returns the name of the i'th symbol definitions in the NSObjectFileImage. -The 'C' string returned should not be freed. If the ordinal specified is -outside the range [0..NSSymbolDefinitionCountInObjectFileImage], NULL will be -returned. -.PP -.I NSSymbolReferenceCountInObjectFileImage -returns the number of references to undefined symbols the NSObjectFileImage. -.PP -.I NSSymbolReferenceNameInObjectFileImage -returns the name of the i'th undefined symbol in the NSObjectFileImage. -The 'C' string returned should not be freed. If the ordinal specified is -outside the range [0..NSSymbolReferenceCountInObjectFileImage], NULL will be -returned. -.PP -.I NSIsSymbolDefinedInObjectFileImage -returns TRUE if the specified symbol name has a definition in the -NSObjectFileImage. -.PP -.I NSGetSectionDataInObjectFileImage -returns the address of the data for the named section in the named segment in -the NSObjectFileImage. If the parameter size is not NULL, the size of the -section is returned in size. If the section cannot be found or a zerofill -section, NULL is returned and the size returned is zero. -.PP -.I NSHasModInitObjectFileImage -returns TRUE if the NSObjectFileImage has any module initialization routines -and FALSE otherwise. - -.SH RETURN CODES -The API's that create NSObjectFileImage's return an NSObjectFileImageReturnCode -with the following possible values: -.TP -.B NSObjectFileImageSuccess -Indicates the API was successful and it returned a valid NSObjectFileImage for -the host machine's cpu architecture. -.TP -.B NSObjectFileImageFailure -Indicates the API failed and no NSObjectFileImage was returned. If this is -returned an error message is printed on stderr as to the reason for the -failure. -.TP -.B NSObjectFileImageInappropriateFile -Indicates the API failed because the file passed to it was not an appropriate -type of object file. -.TP -.B NSObjectFileImageArch -Indicates the API failed because the host machine's cpu architecture could not -be found in the file. -.TP -.B NSObjectFileImageFormat -Indicates the API failed because the Mach-O format was malformed. If this is -returned an error message is printed on stderr as to the format error. -.TP -.B NSObjectFileImageAccess -Indicates the API failed because the file could not be accessed. -.SH ALSO SEE -NSModule(3), dyld(3) diff --git a/doc/man/man3/NSObjectFileImage_priv.3 b/doc/man/man3/NSObjectFileImage_priv.3 deleted file mode 100644 index aeddd1c..0000000 --- a/doc/man/man3/NSObjectFileImage_priv.3 +++ /dev/null @@ -1,42 +0,0 @@ -.TH NSObjectFileImage 3 "July 9, 2003" "Apple Computer, Inc." -.SH NAME -NSObjectFileImage_priv \- programmatic interface for working with Mach-O files -.SH SYNOPSIS -.nf -.PP -#include -.sp .5 -extern enum DYLD_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 */ -.sp .5 -extern enum DYLD_BOOL -NSHasModInitObjectFileImage( - NSObjectFileImage objectFileImage); -.fi -.SH DESCRIPTION -.PP -These routines are the programmatic interface for working with Mach-O files. -They bring the Mach-O file into memory and the API allows the file to -be inspected or loaded into the program. On creation of an object file image -it is checked to insure it is a valid format and it is compatible with the host -machine's cpu architecture. -.PP -.PP -.I NSFindSectionAndOffsetInObjectFileImage -is supplied an imageOffset into an ObjectFileImage and returns -via parameters the segment/section name and offset into that section of -that imageOffset. It returns FALSE if the imageOffset is not -in any section, otherwise TRUE. You can used the resulting sectionOffset to -index into the data returned by NSGetSectionDataInObjectFileImage. -.PP -.I NSHasModInitObjectFileImage -returns TRUE if the NSObjectFileImage has any module initialization routines -and FALSE otherwise. - -.SH ALSO SEE -NSObjectFileImage(3), NSModule(3), dyld(3) diff --git a/doc/man/man3/dlclose.3 b/doc/man/man3/dlclose.3 index c966d6a..1200e80 100644 --- a/doc/man/man3/dlclose.3 +++ b/doc/man/man3/dlclose.3 @@ -1,4 +1,4 @@ -.Dd Sept 25, 2004 +.Dd Nov 6, 2006 .Dt DLCLOSE 3 .Sh NAME .Nm dlclose @@ -19,6 +19,13 @@ Just before removing a dynamic library or bundle in this way, any termination routines in it are called. .Fa handle is the value returned by a previous call to dlopen. +.Pp +Prior to Mac OS X 10.5, only bundles could be unloaded. Starting in Mac OS X 10.5, +dynamic libraries may also be unloaded. There are a couple of cases in which a +dynamic library will never be unloaded: 1) the main executable links against it, +2) An API that does not supoort unloading (e.g. NSAddImage()) was used to load +it or some other dynnamic library that depends on it, 3) the dynamic library +is in dyld's shared cache. .Sh RETURN VALUES If .Fn dlclose @@ -32,7 +39,5 @@ retrived with .Xr dlsym 3 .Xr dlerror 3 .Xr dyld 3 -.Xr NSModule 3 -.Xr NSObjectFileImage 3 .Xr ld 1 .Xr cc 1 diff --git a/doc/man/man3/dlerror.3 b/doc/man/man3/dlerror.3 index 67594b3..758cc18 100644 --- a/doc/man/man3/dlerror.3 +++ b/doc/man/man3/dlerror.3 @@ -1,4 +1,4 @@ -.Dd Sept 25, 2004 +.Dd April 17, 2006 .Dt DLERROR 3 .Sh NAME .Nm dlerror @@ -12,6 +12,7 @@ returns a null-terminated character string describing the last error that occurred on this thread during a call to .Fn dlopen , +.Fn dlopen_preflight , .Fn dlsym , or .Fn dlclose . @@ -27,6 +28,7 @@ where the second call follows the first immediately, the second call will always return a null pointer. .Sh SEE ALSO .Xr dlopen 3 +.Xr dlopen_preflight 3 .Xr dlclose 3 .Xr dlsym 3 .Xr dyld 3 diff --git a/doc/man/man3/dlopen.3 b/doc/man/man3/dlopen.3 index e4ef8f2..8f88c47 100644 --- a/doc/man/man3/dlopen.3 +++ b/doc/man/man3/dlopen.3 @@ -1,4 +1,4 @@ -.Dd February 8, 2005 +.Dd Nov 6, 2006 .Os .Dt DLOPEN 3 .Sh NAME @@ -63,7 +63,7 @@ RTLD_LAZY nor RTLD_NOW is specified, the default is RTLD_LAZY. One of the following flags may be ORed into the .Fa mode argument: -.Bl -tag -width RTLD_GLOBALX +.Bl -tag -width RTLD_LOCALX .It Dv RTLD_GLOBAL Symbols exported from this image (dynamic library or bundle) will be available to any images build with -flat_namespace option to @@ -77,37 +77,82 @@ and only availble to .Fn dlsym when directly using the handle returned by this call to .Fn dlopen . +.Pp +.El If neither RTLD_GLOBAL nor RTLD_LOCAL is specified, the default is RTLD_GLOBAL. +.Pp +One of the following may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_NODELETEX +.It Dv RTLD_NOLOAD +The specified image is not loaded. However, a valid +.Fa handle +is returned if the image already exists in the process. This provides a way +to query if an image is already loaded. The +.Fa handle +returned is ref-counted, so you eventually need a corresponding call to +.Fn dlclose +.It Dv RTLD_NODELETE +The specified image is tagged so that will never be removed from the address space, +even after all clients have released it via +.Fn dlclose +.El +.Pp +Additionally, the following may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_FIRSTX +.It Dv RTLD_FIRST +The retuned +.Fa handle +is tagged so that any +.Fn dlsym +calls on the +.Fa handle +will only search the image specified, and not subsequent images. If +.Fa path +is NULL and the option RTLD_FIRST is used, the +.Fa handle +returned will only search the main executable. .El .Sh SEARCHING .Fn dlopen -uses a series of steps to find a compatible mach-o file. The first compatible file found is used. +searches for a compatible Mach-O file in the directories specified by a set of environment variables and +the process's current working directory. +When set, the environment variables must contain a colon-separated list of directory paths, +which can be absolute or relative to the current working directory. The environment variables +are LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, and DYLD_FALLBACK_LIBRARY_PATH. +The first two variables have no default value. The default value of DYLD_FALLBACK_LIBRARY_PATH +is $HOME/lib;/usr/local/lib;/usr/lib. +.Fn dlopen +searches the directories specified in the environment variables in the order they are listed. .Pp -1) If the directory specified by -.Fa path -does not contain a slash '/' (i.e. it is a leaf name) then the environment variable LD_LIBRARY_PATH is -used. LD_LIBRARY_PATH should be a colon seperated list of directories. +When +.Fa path +doesn't contain a slash character (i.e. it is just a leaf name), .Fn dlopen -searches each directory, in the order specified, for the leaf name -.Fa path . -.Pp -2) If DYLD_LIBRARY_PATH is set, then those directories are searched, in order, -with the leaf name of -.Fa path . +searches the following the following until it finds a compatible Mach-O file: $LD_LIBRARY_PATH, +$DYLD_LIBRARY_PATH, current working directory, $DYLD_FALLBACK_LIBRARY_PATH. .Pp -3) If DYLD_FALLBACK_LIBRARY_PATH is set, then those directories are searched, in order -with the leaf name of -.Fa path . -If DYLD_FALLBACK_LIBRARY_PATH is not set, then the following directories are searched: $HOME/lib, /usr/local/lib, /usr/lib -.Pp -4) Lastly, +When +.Fa path +contains a slash (i.e. a full path or a partial path) +.Fn dlopen +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 -is tried as-is as a regular file path. That means it might resolve relative to the current working directory. +). .Pp Note: There are no configuration files to control dlopen searching. .Pp -Note: Mac OS X uses "fat" files to combine 32-bit and 64-bit libraries. This means there are no separate 32-bit and 64-bit search paths. +Note: If the main executable is a set[ug]id binary, then all environment variables are ignored, and only a full path can be used. +.Pp +Note: Mac OS X uses "universal" files to combine 32-bit and 64-bit libraries. This means there are no separate 32-bit and 64-bit search paths. .Pp .Sh RETURN VALUES If @@ -121,6 +166,7 @@ and Peter O'Gorman . In Mac OS X 10.4, dlopen was rewritten to be a native part of dyld. .Pp .Sh SEE ALSO +.Xr dlopen_preflight 3 .Xr dlclose 3 .Xr dlsym 3 .Xr dlerror 3 diff --git a/doc/man/man3/dlopen_preflight.3 b/doc/man/man3/dlopen_preflight.3 new file mode 100644 index 0000000..aa27dbf --- /dev/null +++ b/doc/man/man3/dlopen_preflight.3 @@ -0,0 +1,33 @@ +.Dd April 17, 2006 +.Os +.Dt DLOPEN_PREFLIGHT 3 +.Sh NAME +.Nm dlopen_preflight +.Nd preflight the load of a dynamic library or bundle +.Sh SYNOPSIS +.In dlfcn.h +.Ft bool +.Fn dlopen_preflight "const char* path" +.Sh DESCRIPTION +.Fn dlopen_preflight +examines the mach-o file specified by +.Fa path . +It checks if the file and libraries it depends on are all compatible with the current process. +That is, they contain the correct architecture and are not otherwise ABI incompatible. +.Pp +.Fn dlopen_preflight +was first available in Mac OS X 10.5. +.Sh SEARCHING +.Fn dlopen_preflight +uses the same steps as +.Fn dlopen +to find a compatible mach-o file. +.Sh RETURN VALUES +.Fn dlopen_preflight +returns true on if the mach-o file is compatible. If the file is not compatible, it returns false +and sets an error string that can be examined with +.Fn dlerror . +.Pp +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlerror 3 diff --git a/doc/man/man3/dlsym.3 b/doc/man/man3/dlsym.3 index 6072b92..d9afeb8 100644 --- a/doc/man/man3/dlsym.3 +++ b/doc/man/man3/dlsym.3 @@ -30,7 +30,8 @@ If is called with the special .Fa handle .Dv RTLD_DEFAULT , -then every mach-o image in the process is searched in the order they were loaded. +then all mach-o images in the process (except those loaded with dlopen(xxx, RTLD_LOCAL)) +are searched in the order they were loaded. This can be a costly search and should be avoided. .Pp If diff --git a/doc/man/man3/dyld.3 b/doc/man/man3/dyld.3 index 9e10b90..9013b81 100644 --- a/doc/man/man3/dyld.3 +++ b/doc/man/man3/dyld.3 @@ -1,200 +1,123 @@ -.TH DYLD 3 "January 15, 2005" "Apple Computer, Inc." -.SH NAME -dyld \- low level programatic interface to the dynamic link editor -.SH SYNOPSIS -.nf -.PP -#include -bool _dyld_present(void); -.sp .5 -uint32_t _dyld_image_count(void); -.sp .5 -const struct mach_header *_dyld_get_image_header( - uint32_t image_index); -.sp .5 -intptr_t _dyld_get_image_vmaddr_slide( - uint32_t image_index); -.sp .5 -const char *_dyld_get_image_name( - uint32_t image_index); -.sp .5 -void _dyld_lookup_and_bind( - const char *symbol_name, - void **address, - NSModule *module); -.sp .5 -void _dyld_lookup_and_bind_with_hint( - const char *symbol_name, - const char *library_name_hint, - void **address, - NSModule *module); -.sp .5 -void _dyld_lookup_and_bind_fully( - const char *symbol_name, - void **address, - NSModule *module); -.sp .5 -bool _dyld_bind_fully_image_containing_address( - const void *address); -.sp .5 -bool _dyld_image_containing_address( - const void* address); -.sp .5 -const struct mach_header * _dyld_get_image_header_containing_address( - const void* address); -.sp .5 -bool _dyld_launched_prebound(void); -.sp .5 -bool _dyld_all_twolevel_modules_prebound(void); -.sp .5 -int _dyld_func_lookup( - const char *dyld_func_name, - void **address); -.sp .5 -extern void _dyld_bind_objc_module( - const void *objc_module); -.sp .5 -extern void _dyld_get_objc_module_sect_for_module( - NSModule module, - void **objc_module, - size_t *size); -.sp .5 -extern void _dyld_lookup_and_bind_objc( - const char *symbol_name, - void **address, - NSModule *module); -.sp .5 -extern void _dyld_moninit( - void (*monaddition)(char *lowpc, char *highpc)); -.sp .5 - -extern void _dyld_register_func_for_add_image( - void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)); -.sp .5 -extern void _dyld_register_func_for_remove_image( - void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)); -.sp .5 -extern void _dyld_register_func_for_link_module( - void (*func)(NSModule module)); -.fi -.SH DESCRIPTION -These routines are the low level programatic interface to the dynamic link -editor. -.PP -.I _dyld_present returns non-zero if the dynamic linker is being used in the -program and zero otherwise. If this returns zero this rest of these functions -should not be called and most likely crash the program if called. -.PP -.I _dyld_image_count -returns the current number of images mapped in by the dynamic link editor. -.PP -.I _dyld_get_image_header -returns the mach header of the image indexed by image_index. If image_index is -out of range NULL is returned. -.PP -.I _dyld_get_image_vmaddr_slide +.Dd August 16, 2006 +.Dt dyld 3 +.Sh NAME +.Nm _dyld_image_count, +.Nm _dyld_get_image_header, +.Nm _dyld_get_image_vmaddr_slide, +.Nm _dyld_get_image_name, +.Nm _dyld_register_func_for_add_image, +.Nm _dyld_register_func_for_remove_image, +.Nm NSVersionOfRunTimeLibrary, +.Nm NSVersionOfLinkTimeLibrary +.Nm _NSGetExecutablePath +.Sh SYNOPSIS +.In mach-o/dyld.h +.Ft uint32_t +.Fo _dyld_image_count +.Fa "void" +.Fc +.Ft const struct mach_header* +.Fo _dyld_get_image_header +.Fa "uint32_t image_index" +.Fc +.Ft intptr_t +.Fo _dyld_get_image_vmaddr_slide +.Fa "uint32_t image_index" +.Fc +.Ft const char* +.Fo _dyld_get_image_name +.Fa "uint32_t image_index" +.Fc +.Ft void +.Fo _dyld_register_func_for_add_image +.Fa "void \*[lp]*func\*[rp]\*[lp]const struct mach_header* mh, intptr_t vmaddr_slide\*[rp]" +.Fc +.Ft void +.Fo _dyld_register_func_for_remove_image +.Fa "void \*[lp]*func\*[rp]\*[lp]const struct mach_header* mh, intptr_t vmaddr_slide\*[rp]" +.Fc +.Ft int32_t +.Fo NSVersionOfRunTimeLibrary +.Fa "const char* libraryName" +.Fc +.Ft int32_t +.Fo NSVersionOfLinkTimeLibrary +.Fa "const char* libraryName" +.Fc +.Ft int +.Fo _NSGetExecutablePath +.Fa "char* buf" +.Fa "uint32_t* bufsize" +.Fc +.Sh DESCRIPTION +These routines provide additional introspection of dyld beyond that provided by +.Fn dlopen +and +.Fn dladdr +. +.Pp +.Fn _dyld_image_count +returns the current number of images mapped in by dyld. Note that using this +count to iterate all images is not thread safe, because another thread +may be adding or removing images during the iteration. +.Pp +.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. +.Pp +.Fn _dyld_get_image_vmaddr_slide returns the virtural memory address slide amount of the image indexed by -.I image_index. -If image_index is out of range zero is returned. -.PP -.I _dyld_get_image_name +.Fa image_index. +If +.Fa image_index +is out of range zero is returned. +.Pp +.Fn _dyld_get_image_name returns the name of the image indexed by -.I image_index. -If image_index is out of range NULL is returned. -.PP -.I _dyld_lookup_and_bind -looks up the -.I symbol_name -and binds it into the program. It indirectly returns the -.I address -and and a pointer to the -.I module -that defined the symbol. -.PP -.I _dyld_lookup_and_bind_with_hint -is the same as -.I _dyld_lookup_and_bind -but the -.I library_name_hint -parameter provides a hint as to where to start the lookup in a prebound -program. The -.I library_name_hint -parameter is matched up with the actual library install names with -.IR strstr (3). -.PP -.I _dyld_lookup_and_bind_fully -looks up the -.I symbol_name -and binds it and all of its references into the program. It indirectly returns -the -.I address -and and a pointer to the -.I module -that defined the symbol. -.PP -.I _dyld_bind_fully_image_containing_address -fully binds the image containing the specified address. It returns TRUE if the -address is contained in a loaded image and FALSE otherwise. -.PP -.I _dyld_image_containing_address -It returns TRUE if the address is contained in an image dyld loaded and FALSE -otherwise. -.PP -.I _dyld_get_image_header_containing_address -It returns a pointer to the mach header of the image if the address is contained -in an image dyld loaded and NULL otherwise. -.PP -.I _dyld_launched_prebound -returns TRUE if the program was launched using the prebound state and FALSE -otherwise. -.PP -.I_dyld_all_twolevel_modules_prebound(void); -returns TRUE if all the libraries currently in use by the program are being used -as two-level namespace libraries, are prebound and have all their modules bound. -Otherwise it returns FALSE. -.PP -.I _dyld_func_lookup -is passed a name, -.I dyld_func_name, -of a dynamic link editor function and returns the -.I address -of the function indirectly. It returns non-zero if the function is found -and zero otherwise. -.PP -.I _dyld_bind_objc_module -is passed a pointer to something in an (__OBJC,__module) section and causes the -module that is associated with that address to be bound. -.PP -.I _dyld_get_objc_module_sect_for_module -is passed a module and sets a pointer to the (__OBJC,__module) section and its -size for the specified module. -.PP -.I _dyld_lookup_and_bind_objc() -is the same as _dyld_lookup_and_bind() but does not update the symbol pointers -if the symbol is in a bound module. The reason for this is that an objc symbol -like -.I .objc_class_name_Object -is never used by a symbol pointer. Since this is done a lot by the objc -runtime and updating symbol pointers is not cheep it should not be done. -.PP -.I _dyld_moninit -is called from the profiling runtime routine -.IR moninit(3) -to cause the dyld loaded code to be profiled. It is passed a pointer to the -the profiling runtime routine -.IR monaddtion(3) -to be called after an image had been mapped in. -.PP -.I _dyld_register_func_for_add_image +.Fa image_index. +The C-string continues to be owned by dyld and should not deleted. +If +.Fa image_index +is out of range NULL is returned. +.Pp +.Fn _dyld_register_func_for_add_image registers the specified function to be called when a new image is added (a bundle or a dynamic shared library) to the program. When this function is first registered it is called for once for each image that is currently part of -the program. -.PP -.I _dyld_register_func_for_remove_image +the process. +.Pp +.Fn _dyld_register_func_for_remove_image registers the specified function to be called when an image is removed -(a bundle or a dynamic shared library) from the program. -.I _dyld_register_func_for_link_module -registers the specified function to be called when a module is bound into the -program. When this function is first registered it is called for once for each -module that is currently bound into the program. +(a bundle or a dynamic shared library) from the process. +.Pp +.Fn NSVersionOfRunTimeLibrary +returns the current_version number of the currently loaded dylib +specifed by the libraryName. The libraryName parameter would be "bar" for /path/libbar.3.dylib and +"Foo" for /path/Foo.framework/Versions/A/Foo. This function returns -1 if no such library is loaded. +.Pp +.Fn NSVersionOfLinkTimeLibrary +returns the current_version number that the main executable was linked +against at build time. The libraryName parameter would be "bar" for /path/libbar.3.dylib and +"Foo" for /path/Foo.framework/Versions/A/Foo. This function returns -1 if the main executable did not link +against the specified library. +.Pp +.Fn _NSGetExecutablePath +copies the path of the main executable into the buffer +.Fa buf . +The +.Fa bufsize +parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied. +It returns -1 if the buffer is not large enough, and * +.Fa bufsize +is set to the size required. +Note that +.Fn _NSGetExecutablePath +will return "a path" to the executable not a "real path" to the executable. +That is, the path may be a symbolic link and not the real file. With deep directories the total bufsize +needed could be more than MAXPATHLEN. +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dladdr 3 +.Xr dyld 1 +http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachOTopics/index.html \ No newline at end of file diff --git a/dyld.xcodeproj/kledzik.mode1v3 b/dyld.xcodeproj/kledzik.mode1v3 new file mode 100644 index 0000000..cbe28df --- /dev/null +++ b/dyld.xcodeproj/kledzik.mode1v3 @@ -0,0 +1,1377 @@ + + + + + ActivePerspectiveName + Project + AllowedModules + + + BundleLoadPath + + MaxInstances + n + Module + PBXSmartGroupTreeModule + Name + Groups and Files Outline View + + + BundleLoadPath + + MaxInstances + n + Module + PBXNavigatorGroup + Name + Editor + + + BundleLoadPath + + MaxInstances + n + Module + XCTaskListModule + Name + Task List + + + BundleLoadPath + + MaxInstances + n + Module + XCDetailModule + Name + File and Smart Group Detail Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXBuildResultsModule + Name + Detailed Build Results Viewer + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXProjectFindModule + Name + Project Batch Find Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCProjectFormatConflictsModule + Name + Project Format Conflicts List + + + BundleLoadPath + + MaxInstances + n + Module + PBXBookmarksModule + Name + Bookmarks Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXClassBrowserModule + Name + Class Browser + + + BundleLoadPath + + MaxInstances + n + Module + PBXCVSModule + Name + Source Code Control Tool + + + BundleLoadPath + + MaxInstances + n + Module + PBXDebugBreakpointsModule + Name + Debug Breakpoints Tool + + + BundleLoadPath + + MaxInstances + n + Module + XCDockableInspector + Name + Inspector + + + BundleLoadPath + + MaxInstances + n + Module + PBXOpenQuicklyModule + Name + Open Quickly Tool + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugSessionModule + Name + Debugger + + + BundleLoadPath + + MaxInstances + 1 + Module + PBXDebugCLIModule + Name + Debug Console + + + BundleLoadPath + + MaxInstances + n + Module + XCSnapshotModule + Name + Snapshots Tool + + + BundlePath + /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources + Description + DefaultDescriptionKey + DockingSystemVisible + + Extension + mode1v3 + FavBarConfig + + PBXProjectModuleGUID + F9DBB28F0CD7F5F1009A2B25 + XCBarModuleItemNames + + XCBarModuleItems + + + FirstTimeWindowDisplayed + + Identifier + com.apple.perspectives.project.mode1v3 + MajorVersion + 33 + MinorVersion + 0 + Name + Default + Notifications + + + XCObserverAutoDisconnectKey + + XCObserverDefintionKey + + XCObserverFactoryKey + XCPerspectivesSpecificationIdentifier + XCObserverGUIDKey + XCObserverProjectIdentifier + XCObserverNotificationKey + PBXStatusBuildStateMessageNotification + XCObserverTargetKey + XCMainBuildResultsModuleGUID + XCObserverTriggerKey + awakenModuleWithObserver: + XCObserverValidationKey + + + + OpenEditors + + PerspectiveWidths + + -1 + -1 + + Perspectives + + + ChosenToolbarItems + + active-target-popup + active-buildstyle-popup + active-executable-popup + NSToolbarFlexibleSpaceItem + buildOrClean + build-and-goOrGo + com.apple.ide.PBXToolbarStopButton + get-info + toggle-editor + NSToolbarFlexibleSpaceItem + com.apple.pbx.toolbar.searchfield + + ControllerClassBaseName + + IconName + WindowOfProjectWithEditor + Identifier + perspective.project + IsVertical + + Layout + + + BecomeActive + + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 186 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + F9ED4C870630A72200DF4E74 + 1C37FBAC04509CD000000102 + F9DBB28B0CD7F5F1009A2B25 + F9DBB28C0CD7F5F1009A2B25 + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 14 + 11 + 6 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 102}, {186, 338}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {203, 356}} + GroupTreeTableConfiguration + + MainColumn + 186 + + RubberWindowFrame + 128 669 690 397 0 0 1920 1178 + + Module + PBXSmartGroupTreeModule + Proportion + 203pt + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20306471E060097A5F4 + PBXProjectModuleLabel + MyNewFile14.java + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CE0B20406471E060097A5F4 + PBXProjectModuleLabel + MyNewFile14.java + + SplitCount + 1 + + StatusBarVisibility + + + GeometryConfiguration + + Frame + {{0, 0}, {482, 0}} + RubberWindowFrame + 128 669 690 397 0 0 1920 1178 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CE0B20506471E060097A5F4 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{0, 5}, {482, 351}} + RubberWindowFrame + 128 669 690 397 0 0 1920 1178 + + Module + XCDetailModule + Proportion + 351pt + + + Proportion + 482pt + + + Name + Project + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + XCModuleDock + PBXNavigatorGroup + XCDetailModule + + TableOfContents + + F9DBB28D0CD7F5F1009A2B25 + 1CE0B1FE06471DED0097A5F4 + F9DBB28E0CD7F5F1009A2B25 + 1CE0B20306471E060097A5F4 + 1CE0B20506471E060097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.defaultV3 + + + ControllerClassBaseName + + IconName + WindowOfProject + Identifier + perspective.morph + IsVertical + 0 + Layout + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C37FBAC04509CD000000102 + 1C37FAAC04509CD000000102 + 1C08E77C0454961000C914BD + 1C37FABC05509CD000000102 + 1C37FABC05539CD112110102 + E2644B35053B69B200211256 + 1C37FABC04509CD000100104 + 1CC0EA4004350EF90044410B + 1CC0EA4004350EF90041110B + + PBXProjectModuleGUID + 11E0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + yes + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 186 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 29B97314FDCFA39411CA2CEA + 1C37FABC05509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {186, 337}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 1 + XCSharingToken + com.apple.Xcode.GFSharingToken + + GeometryConfiguration + + Frame + {{0, 0}, {203, 355}} + GroupTreeTableConfiguration + + MainColumn + 186 + + RubberWindowFrame + 373 269 690 397 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 100% + + + Name + Morph + PreferredWidth + 300 + ServiceClasses + + XCModuleDock + PBXSmartGroupTreeModule + + TableOfContents + + 11E0B1FE06471DED0097A5F4 + + ToolbarConfiguration + xcode.toolbar.config.default.shortV3 + + + PerspectivesBarVisible + + ShelfIsVisible + + SourceDescription + file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode1.xcperspec' + StatusbarIsVisible + + TimeStamp + 215479759.49117801 + ToolbarDisplayMode + 1 + ToolbarIsVisible + + ToolbarSizeMode + 1 + Type + Perspectives + UpdateMessage + The Default Workspace in this version of Xcode now includes support to hide and show the detail view (what has been referred to as the "Metro-Morph" feature). You must discard your current Default Workspace settings and update to the latest Default Workspace in order to gain this feature. Do you wish to update to the latest Workspace defaults for project '%@'? + WindowJustification + 5 + WindowOrderList + + /tmp/ttt/dyld-95.3/dyld.xcodeproj + + WindowString + 128 669 690 397 0 0 1920 1178 + WindowToolsV3 + + + Identifier + windowTool.build + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528F0623707200166675 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CD052900623707200166675 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {500, 215}} + RubberWindowFrame + 192 257 500 500 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 218pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + XCMainBuildResultsModuleGUID + PBXProjectModuleLabel + Build + + GeometryConfiguration + + Frame + {{0, 222}, {500, 236}} + RubberWindowFrame + 192 257 500 500 0 0 1280 1002 + + Module + PBXBuildResultsModule + Proportion + 236pt + + + Proportion + 458pt + + + Name + Build Results + ServiceClasses + + PBXBuildResultsModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAA5065D492600B07095 + 1C78EAA6065D492600B07095 + 1CD0528F0623707200166675 + XCMainBuildResultsModuleGUID + + ToolbarConfiguration + xcode.toolbar.config.buildV3 + WindowString + 192 257 500 500 0 0 1280 1002 + + + Identifier + windowTool.debugger + Layout + + + Dock + + + ContentConfiguration + + Debugger + + HorizontalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {317, 164}} + {{317, 0}, {377, 164}} + + + VerticalSplitView + + _collapsingFrameDimension + 0.0 + _indexOfCollapsedView + 0 + _percentageOfCollapsedView + 0.0 + isCollapsed + yes + sizes + + {{0, 0}, {694, 164}} + {{0, 164}, {694, 216}} + + + + LauncherConfigVersion + 8 + PBXProjectModuleGUID + 1C162984064C10D400B95A72 + PBXProjectModuleLabel + Debug - GLUTExamples (Underwater) + + GeometryConfiguration + + DebugConsoleDrawerSize + {100, 120} + DebugConsoleVisible + None + DebugConsoleWindowFrame + {{200, 200}, {500, 300}} + DebugSTDIOWindowFrame + {{200, 200}, {500, 300}} + Frame + {{0, 0}, {694, 380}} + RubberWindowFrame + 321 238 694 422 0 0 1440 878 + + Module + PBXDebugSessionModule + Proportion + 100% + + + Proportion + 100% + + + Name + Debugger + ServiceClasses + + PBXDebugSessionModule + + StatusbarIsVisible + 1 + TableOfContents + + 1CD10A99069EF8BA00B06720 + 1C0AD2AB069F1E9B00FABCE6 + 1C162984064C10D400B95A72 + 1C0AD2AC069F1E9B00FABCE6 + + ToolbarConfiguration + xcode.toolbar.config.debugV3 + WindowString + 321 238 694 422 0 0 1440 878 + WindowToolGUID + 1CD10A99069EF8BA00B06720 + WindowToolIsVisible + 0 + + + Identifier + windowTool.find + Layout + + + Dock + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1CDD528C0622207200134675 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1CD0528D0623707200166675 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {781, 167}} + RubberWindowFrame + 62 385 781 470 0 0 1440 878 + + Module + PBXNavigatorGroup + Proportion + 781pt + + + Proportion + 50% + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD0528E0623707200166675 + PBXProjectModuleLabel + Project Find + + GeometryConfiguration + + Frame + {{8, 0}, {773, 254}} + RubberWindowFrame + 62 385 781 470 0 0 1440 878 + + Module + PBXProjectFindModule + Proportion + 50% + + + Proportion + 428pt + + + Name + Project Find + ServiceClasses + + PBXProjectFindModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C530D57069F1CE1000CFCEE + 1C530D58069F1CE1000CFCEE + 1C530D59069F1CE1000CFCEE + 1CDD528C0622207200134675 + 1C530D5A069F1CE1000CFCEE + 1CE0B1FE06471DED0097A5F4 + 1CD0528E0623707200166675 + + WindowString + 62 385 781 470 0 0 1440 878 + WindowToolGUID + 1C530D57069F1CE1000CFCEE + WindowToolIsVisible + 0 + + + Identifier + MENUSEPARATOR + + + Identifier + windowTool.debuggerConsole + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAAC065D492600B07095 + PBXProjectModuleLabel + Debugger Console + + GeometryConfiguration + + Frame + {{0, 0}, {650, 250}} + RubberWindowFrame + 516 632 650 250 0 0 1680 1027 + + Module + PBXDebugCLIModule + Proportion + 209pt + + + Proportion + 209pt + + + Name + Debugger Console + ServiceClasses + + PBXDebugCLIModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAAD065D492600B07095 + 1C78EAAE065D492600B07095 + 1C78EAAC065D492600B07095 + + ToolbarConfiguration + xcode.toolbar.config.consoleV3 + WindowString + 650 41 650 250 0 0 1280 1002 + WindowToolGUID + 1C78EAAD065D492600B07095 + WindowToolIsVisible + 0 + + + Identifier + windowTool.snapshots + Layout + + + Dock + + + Module + XCSnapshotModule + Proportion + 100% + + + Proportion + 100% + + + Name + Snapshots + ServiceClasses + + XCSnapshotModule + + StatusbarIsVisible + Yes + ToolbarConfiguration + xcode.toolbar.config.snapshots + WindowString + 315 824 300 550 0 0 1440 878 + WindowToolIsVisible + Yes + + + Identifier + windowTool.scm + Layout + + + Dock + + + ContentConfiguration + + PBXProjectModuleGUID + 1C78EAB2065D492600B07095 + PBXProjectModuleLabel + <No Editor> + PBXSplitModuleInNavigatorKey + + Split0 + + PBXProjectModuleGUID + 1C78EAB3065D492600B07095 + + SplitCount + 1 + + StatusBarVisibility + 1 + + GeometryConfiguration + + Frame + {{0, 0}, {452, 0}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + + Module + PBXNavigatorGroup + Proportion + 0pt + + + BecomeActive + 1 + ContentConfiguration + + PBXProjectModuleGUID + 1CD052920623707200166675 + PBXProjectModuleLabel + SCM + + GeometryConfiguration + + ConsoleFrame + {{0, 259}, {452, 0}} + Frame + {{0, 7}, {452, 259}} + RubberWindowFrame + 743 379 452 308 0 0 1280 1002 + TableConfiguration + + Status + 30 + FileName + 199 + Path + 197.09500122070312 + + TableFrame + {{0, 0}, {452, 250}} + + Module + PBXCVSModule + Proportion + 262pt + + + Proportion + 266pt + + + Name + SCM + ServiceClasses + + PBXCVSModule + + StatusbarIsVisible + 1 + TableOfContents + + 1C78EAB4065D492600B07095 + 1C78EAB5065D492600B07095 + 1C78EAB2065D492600B07095 + 1CD052920623707200166675 + + ToolbarConfiguration + xcode.toolbar.config.scm + WindowString + 743 379 452 308 0 0 1280 1002 + + + Identifier + windowTool.breakpoints + IsVertical + 0 + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + PBXBottomSmartGroupGIDs + + 1C77FABC04509CD000000102 + + PBXProjectModuleGUID + 1CE0B1FE06471DED0097A5F4 + PBXProjectModuleLabel + Files + PBXProjectStructureProvided + no + PBXSmartGroupTreeModuleColumnData + + PBXSmartGroupTreeModuleColumnWidthsKey + + 168 + + PBXSmartGroupTreeModuleColumnsKey_v4 + + MainColumn + + + PBXSmartGroupTreeModuleOutlineStateKey_v7 + + PBXSmartGroupTreeModuleOutlineStateExpansionKey + + 1C77FABC04509CD000000102 + + PBXSmartGroupTreeModuleOutlineStateSelectionKey + + + 0 + + + PBXSmartGroupTreeModuleOutlineStateVisibleRectKey + {{0, 0}, {168, 350}} + + PBXTopSmartGroupGIDs + + XCIncludePerspectivesSwitch + 0 + + GeometryConfiguration + + Frame + {{0, 0}, {185, 368}} + GroupTreeTableConfiguration + + MainColumn + 168 + + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + PBXSmartGroupTreeModule + Proportion + 185pt + + + ContentConfiguration + + PBXProjectModuleGUID + 1CA1AED706398EBD00589147 + PBXProjectModuleLabel + Detail + + GeometryConfiguration + + Frame + {{190, 0}, {554, 368}} + RubberWindowFrame + 315 424 744 409 0 0 1440 878 + + Module + XCDetailModule + Proportion + 554pt + + + Proportion + 368pt + + + MajorVersion + 3 + MinorVersion + 0 + Name + Breakpoints + ServiceClasses + + PBXSmartGroupTreeModule + XCDetailModule + + StatusbarIsVisible + 1 + TableOfContents + + 1CDDB66807F98D9800BB5817 + 1CDDB66907F98D9800BB5817 + 1CE0B1FE06471DED0097A5F4 + 1CA1AED706398EBD00589147 + + ToolbarConfiguration + xcode.toolbar.config.breakpointsV3 + WindowString + 315 424 744 409 0 0 1440 878 + WindowToolGUID + 1CDDB66807F98D9800BB5817 + WindowToolIsVisible + 1 + + + Identifier + windowTool.debugAnimator + Layout + + + Dock + + + Module + PBXNavigatorGroup + Proportion + 100% + + + Proportion + 100% + + + Name + Debug Visualizer + ServiceClasses + + PBXNavigatorGroup + + StatusbarIsVisible + 1 + ToolbarConfiguration + xcode.toolbar.config.debugAnimatorV3 + WindowString + 100 100 700 500 0 0 1280 1002 + + + Identifier + windowTool.bookmarks + Layout + + + Dock + + + Module + PBXBookmarksModule + Proportion + 100% + + + Proportion + 100% + + + Name + Bookmarks + ServiceClasses + + PBXBookmarksModule + + StatusbarIsVisible + 0 + WindowString + 538 42 401 187 0 0 1280 1002 + + + Identifier + windowTool.projectFormatConflicts + Layout + + + Dock + + + Module + XCProjectFormatConflictsModule + Proportion + 100% + + + Proportion + 100% + + + Name + Project Format Conflicts + ServiceClasses + + XCProjectFormatConflictsModule + + StatusbarIsVisible + 0 + WindowContentMinSize + 450 300 + WindowString + 50 850 472 307 0 0 1440 877 + + + Identifier + windowTool.classBrowser + Layout + + + Dock + + + BecomeActive + 1 + ContentConfiguration + + OptionsSetName + Hierarchy, all classes + PBXProjectModuleGUID + 1CA6456E063B45B4001379D8 + PBXProjectModuleLabel + Class Browser - NSObject + + GeometryConfiguration + + ClassesFrame + {{0, 0}, {374, 96}} + ClassesTreeTableConfiguration + + PBXClassNameColumnIdentifier + 208 + PBXClassBookColumnIdentifier + 22 + + Frame + {{0, 0}, {630, 331}} + MembersFrame + {{0, 105}, {374, 395}} + MembersTreeTableConfiguration + + PBXMemberTypeIconColumnIdentifier + 22 + PBXMemberNameColumnIdentifier + 216 + PBXMemberTypeColumnIdentifier + 97 + PBXMemberBookColumnIdentifier + 22 + + PBXModuleWindowStatusBarHidden2 + 1 + RubberWindowFrame + 385 179 630 352 0 0 1440 878 + + Module + PBXClassBrowserModule + Proportion + 332pt + + + Proportion + 332pt + + + Name + Class Browser + ServiceClasses + + PBXClassBrowserModule + + StatusbarIsVisible + 0 + TableOfContents + + 1C0AD2AF069F1E9B00FABCE6 + 1C0AD2B0069F1E9B00FABCE6 + 1CA6456E063B45B4001379D8 + + ToolbarConfiguration + xcode.toolbar.config.classbrowser + WindowString + 385 179 630 352 0 0 1440 878 + WindowToolGUID + 1C0AD2AF069F1E9B00FABCE6 + WindowToolIsVisible + 0 + + + Identifier + windowTool.refactoring + IncludeInToolsMenu + 0 + Layout + + + Dock + + + BecomeActive + 1 + GeometryConfiguration + + Frame + {0, 0}, {500, 335} + RubberWindowFrame + {0, 0}, {500, 335} + + Module + XCRefactoringModule + Proportion + 100% + + + Proportion + 100% + + + Name + Refactoring + ServiceClasses + + XCRefactoringModule + + WindowString + 200 200 500 356 0 0 1920 1200 + + + + diff --git a/dyld.xcodeproj/kledzik.pbxuser b/dyld.xcodeproj/kledzik.pbxuser new file mode 100644 index 0000000..ad6f46d --- /dev/null +++ b/dyld.xcodeproj/kledzik.pbxuser @@ -0,0 +1,140 @@ +// !$*UTF8*$! +{ + F93937310A94FAF700070A07 /* update_dyld_shared_cache */ = { + activeExec = 0; + executables = ( + F9DBB2860CD7F5CF009A2B25 /* update_dyld_shared_cache */, + ); + }; + F9DBB2850CD7F5CF009A2B25 /* dyld */ = { + isa = PBXExecutable; + activeArgIndices = ( + ); + argumentStrings = ( + ); + autoAttachOnCrash = 1; + breakpointsEnabled = 1; + configStateDict = { + }; + customDataFormattersEnabled = 1; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 0; + libgmallocEnabled = 0; + name = dyld; + sourceDirectories = ( + ); + }; + F9DBB2860CD7F5CF009A2B25 /* update_dyld_shared_cache */ = { + isa = PBXExecutable; + activeArgIndices = ( + ); + argumentStrings = ( + ); + autoAttachOnCrash = 1; + breakpointsEnabled = 1; + configStateDict = { + }; + customDataFormattersEnabled = 1; + debuggerPlugin = GDBDebugging; + disassemblyDisplayState = 0; + enableDebugStr = 1; + environmentEntries = ( + ); + executableSystemSymbolLevel = 0; + executableUserSymbolLevel = 0; + libgmallocEnabled = 0; + name = update_dyld_shared_cache; + sourceDirectories = ( + ); + }; + F9DBB2890CD7F5D3009A2B25 /* Source Control */ = { + isa = PBXSourceControlManager; + fallbackIsa = XCSourceControlManager; + isSCMEnabled = 0; + scmConfiguration = { + }; + }; + F9DBB28A0CD7F5D3009A2B25 /* Code sense */ = { + isa = PBXCodeSenseManager; + indexTemplatePath = ""; + }; + F9ED4C8B0630A72300DF4E74 /* Project object */ = { + activeArchitecture = i386; + activeBuildConfigurationName = Release; + activeExecutable = F9DBB2850CD7F5CF009A2B25 /* dyld */; + activeTarget = F9ED4C920630A73900DF4E74 /* all */; + codeSenseManager = F9DBB28A0CD7F5D3009A2B25 /* Code sense */; + executables = ( + F9DBB2850CD7F5CF009A2B25 /* dyld */, + F9DBB2860CD7F5CF009A2B25 /* update_dyld_shared_cache */, + ); + perUserDictionary = { + PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 243, + 20, + 48.16259765625, + 43, + 43, + 20, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + PBXFileDataSource_Target_ColumnID, + ); + }; + PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { + PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; + PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; + PBXFileTableDataSourceColumnWidthsKey = ( + 20, + 203, + 60, + 20, + 48.16259765625, + 43, + 43, + ); + PBXFileTableDataSourceColumnsKey = ( + PBXFileDataSource_FiletypeID, + PBXFileDataSource_Filename_ColumnID, + PBXTargetDataSource_PrimaryAttribute, + PBXFileDataSource_Built_ColumnID, + PBXFileDataSource_ObjectSize_ColumnID, + PBXFileDataSource_Errors_ColumnID, + PBXFileDataSource_Warnings_ColumnID, + ); + }; + PBXPerProjectTemplateStateSaveDate = 215479759; + PBXWorkspaceStateSaveDate = 215479759; + }; + sourceControlManager = F9DBB2890CD7F5D3009A2B25 /* Source Control */; + userBuildSettings = { + }; + }; + F9ED4C920630A73900DF4E74 /* all */ = { + activeExec = 0; + }; + F9ED4C970630A76000DF4E74 /* dyld */ = { + activeExec = 0; + executables = ( + F9DBB2850CD7F5CF009A2B25 /* dyld */, + ); + }; + F9ED4C9E0630A76B00DF4E74 /* libdyld */ = { + activeExec = 0; + }; +} diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index a10989e..ab2b56e 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -12,21 +12,10 @@ buildConfigurationList = F9D8C7E5087B087300E93EFB /* Build configuration list for PBXAggregateTarget "all" */; buildPhases = ( ); - buildSettings = { - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = all; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); - }; dependencies = ( F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */, F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */, + F93937380A94FB6A00070A07 /* PBXTargetDependency */, ); name = all; productName = all; @@ -41,17 +30,23 @@ 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 */; }; - EF79A017070D295200F78484 /* NSModule.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FF1070D27BB00F78484 /* NSModule.3 */; }; - EF79A018070D295200F78484 /* NSObjectFileImage.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FF2070D27BB00F78484 /* NSObjectFileImage.3 */; }; - EF79A019070D295200F78484 /* NSObjectFileImage_priv.3 in CopyFiles */ = {isa = PBXBuildFile; fileRef = EF799FF3070D27BB00F78484 /* NSObjectFileImage_priv.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 */; }; - F939F21A078F1A2100AC144F /* dyld_debug.h in Headers */ = {isa = PBXBuildFile; fileRef = F939F219078F1A2100AC144F /* dyld_debug.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 */; }; + 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"; }; }; + 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 */; }; 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 */; }; @@ -64,7 +59,6 @@ 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 */; }; - F9FE429C06C82066001D8CE5 /* dyldLibSystemThreadHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = F9FE429B06C82066001D8CE5 /* dyldLibSystemThreadHelpers.h */; }; /* End PBXBuildFile section */ /* Begin PBXBuildRule section */ @@ -110,27 +104,21 @@ }; /* End PBXBuildRule section */ -/* Begin PBXBuildStyle section */ - F9ED4C890630A72300DF4E74 /* Development */ = { - isa = PBXBuildStyle; - buildSettings = { - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = NO; - GCC_OPTIMIZATION_LEVEL = 0; - }; - name = Development; +/* Begin PBXContainerItemProxy section */ + F93937370A94FB6A00070A07 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93937310A94FAF700070A07; + remoteInfo = update_dyld_shared_cache; }; - F9ED4C8A0630A72300DF4E74 /* Deployment */ = { - isa = PBXBuildStyle; - buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_CPP_RTTI = NO; - }; - name = Deployment; + F93937390A94FB6E00070A07 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93937310A94FAF700070A07; + remoteInfo = update_dyld_shared_cache; }; -/* End PBXBuildStyle section */ - -/* Begin PBXContainerItemProxy section */ F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; @@ -148,6 +136,16 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + F932C2560BC32AC30018B20D /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /System/Library/LaunchDaemons; + dstSubfolderSpec = 0; + files = ( + F932C2520BC32ABB0018B20D /* com.apple.dyld.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; F93AA9B30630AE8200301D9F /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -156,6 +154,7 @@ files = ( F939F21B078F1A2C00AC144F /* dyld_debug.h in CopyFiles */, F93AA9A50630AE1E00301D9F /* dyld.h in CopyFiles */, + F98D274D0AA79D7400416316 /* dyld_images.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; @@ -165,6 +164,7 @@ dstPath = "/usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( + F919ECB1090455AB002331E3 /* dyld-update-prebinding.h in CopyFiles */, F93AA9A30630AE1E00301D9F /* dyld_gdb.h in CopyFiles */, F93AA9A40630AE1E00301D9F /* dyld_priv.h in CopyFiles */, ); @@ -192,9 +192,7 @@ EF79A014070D295200F78484 /* dlopen.3 in CopyFiles */, EF79A015070D295200F78484 /* dlsym.3 in CopyFiles */, EF79A016070D295200F78484 /* dyld.3 in CopyFiles */, - EF79A017070D295200F78484 /* NSModule.3 in CopyFiles */, - EF79A018070D295200F78484 /* NSObjectFileImage.3 in CopyFiles */, - EF79A019070D295200F78484 /* NSObjectFileImage_priv.3 in CopyFiles */, + F9E572020A66EF4A007D9BE9 /* dlopen_preflight.3 in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; @@ -208,6 +206,17 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + F9D238DD0A9E2FEE002B55C7 /* CopyFiles */ = { + 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 */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -218,15 +227,38 @@ EF799FEE070D27BB00F78484 /* dlopen.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlopen.3; path = doc/man/man3/dlopen.3; sourceTree = SOURCE_ROOT; }; EF799FEF070D27BB00F78484 /* dlsym.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlsym.3; path = doc/man/man3/dlsym.3; sourceTree = SOURCE_ROOT; }; EF799FF0070D27BB00F78484 /* dyld.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dyld.3; path = doc/man/man3/dyld.3; sourceTree = SOURCE_ROOT; }; - EF799FF1070D27BB00F78484 /* NSModule.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = NSModule.3; path = doc/man/man3/NSModule.3; sourceTree = SOURCE_ROOT; }; - EF799FF2070D27BB00F78484 /* NSObjectFileImage.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = NSObjectFileImage.3; path = doc/man/man3/NSObjectFileImage.3; sourceTree = SOURCE_ROOT; }; - EF799FF3070D27BB00F78484 /* NSObjectFileImage_priv.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = NSObjectFileImage_priv.3; path = doc/man/man3/NSObjectFileImage_priv.3; sourceTree = SOURCE_ROOT; }; 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 = ""; }; + 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 = ""; }; + F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = ""; }; + F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = ""; }; + F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = ""; }; + 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 = ""; }; + 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; }; + 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; }; - F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylinker"; includeInIndex = 0; path = dyld; sourceTree = BUILT_PRODUCTS_DIR; }; + F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = ""; }; + F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = ""; }; + F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld; sourceTree = BUILT_PRODUCTS_DIR; }; F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdyldapis.a; sourceTree = BUILT_PRODUCTS_DIR; }; F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; }; F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; }; @@ -247,18 +279,10 @@ 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; }; - F9FE429B06C82066001D8CE5 /* dyldLibSystemThreadHelpers.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemThreadHelpers.h; path = src/dyldLibSystemThreadHelpers.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - F9ED4C960630A76000DF4E74 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - F9ED4C9D0630A76B00DF4E74 /* Frameworks */ = { + F93937300A94FAF700070A07 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( @@ -282,6 +306,8 @@ isa = PBXGroup; children = ( EF799FE9070D27BB00F78484 /* dyld.1 */, + F93C8C290B84EBFC00615A00 /* update_prebinding.1 */, + F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */, ); name = man1; path = doc/man/man1; @@ -295,15 +321,33 @@ EF799FED070D27BB00F78484 /* dlerror.3 */, EF799FEE070D27BB00F78484 /* dlopen.3 */, EF799FEF070D27BB00F78484 /* dlsym.3 */, + F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */, EF799FF0070D27BB00F78484 /* dyld.3 */, - EF799FF1070D27BB00F78484 /* NSModule.3 */, - EF799FF2070D27BB00F78484 /* NSObjectFileImage.3 */, - EF799FF3070D27BB00F78484 /* NSObjectFileImage_priv.3 */, ); name = man3; path = doc/man/man3; sourceTree = SOURCE_ROOT; }; + F939373D0A94FC4700070A07 /* launch-cache */ = { + isa = PBXGroup; + children = ( + F939373E0A94FC4700070A07 /* Architectures.hpp */, + F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */, + F93937400A94FC4700070A07 /* dyld_cache_format.h */, + F93937410A94FC4700070A07 /* FileAbstraction.hpp */, + F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */, + F93937440A94FC4700070A07 /* MachOLayout.hpp */, + F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */, + F98935B90A9A412B00FB6228 /* MachOBinder.hpp */, + 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 */, + ); + path = "launch-cache"; + sourceTree = ""; + }; F9ED4C870630A72200DF4E74 = { isa = PBXGroup; children = ( @@ -311,6 +355,7 @@ F9ED4CC30630A7BE00DF4E74 /* doc */, F9ED4CBE0630A7B100DF4E74 /* include */, F9ED4C990630A76000DF4E74 /* Products */, + F939373D0A94FC4700070A07 /* launch-cache */, ); sourceTree = ""; }; @@ -319,6 +364,7 @@ children = ( F9ED4C980630A76000DF4E74 /* dyld */, F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */, + F93937320A94FAF700070A07 /* update_dyld_shared_cache */, ); name = Products; sourceTree = ""; @@ -329,9 +375,9 @@ F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */, F9ED4CC70630A7F100DF4E74 /* dyld.cpp */, F9ED4CC80630A7F100DF4E74 /* dyld.h */, - F9FE429B06C82066001D8CE5 /* dyldLibSystemThreadHelpers.h */, F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */, F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */, + F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */, F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */, F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */, F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */, @@ -343,8 +389,14 @@ F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */, F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */, F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */, + F973667C0BD004F200E5E1B4 /* ImageLoaderPE.cpp */, + F973667D0BD004F200E5E1B4 /* ImageLoaderPE.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 */, F906E2230639E96400B13DB2 /* dyld_debug.c */, ); name = src; @@ -353,8 +405,10 @@ F9ED4CBE0630A7B100DF4E74 /* include */ = { isa = PBXGroup; children = ( + 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 */, @@ -372,76 +426,40 @@ }; /* End PBXGroup section */ -/* Begin PBXHeadersBuildPhase section */ - F9ED4C9B0630A76B00DF4E74 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - F9FE429C06C82066001D8CE5 /* dyldLibSystemThreadHelpers.h in Headers */, - F939F21A078F1A2100AC144F /* dyld_debug.h in Headers */, +/* Begin PBXNativeTarget section */ + F93937310A94FAF700070A07 /* update_dyld_shared_cache */ = { + 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 */, ); - runOnlyForDeploymentPostprocessing = 0; + buildRules = ( + ); + dependencies = ( + ); + name = update_dyld_shared_cache; + productName = update_dyld_shared_cache; + productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */; + productType = "com.apple.product-type.tool"; }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ F9ED4C970630A76000DF4E74 /* dyld */ = { isa = PBXNativeTarget; buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */; buildPhases = ( F9ED4C950630A76000DF4E74 /* Sources */, - F9ED4C960630A76000DF4E74 /* Frameworks */, ); buildRules = ( F921D318070376B0000D1056 /* PBXBuildRule */, F921D317070376A6000D1056 /* PBXBuildRule */, F921D3160703769A000D1056 /* PBXBuildRule */, ); - buildSettings = { - ARCHS = ppc; - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - DEAD_CODE_STRIPPING = YES; - EXPORTED_SYMBOLS_FILE = ""; - GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_CPP_RTTI = NO; - GCC_MODEL_TUNING = G4; - GCC_OPTIMIZATION_LEVEL = 3; - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = ./include; - INSTALL_PATH = /usr/lib; - LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.4; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ( - "-v", - "-seg1addr", - 8fe00000, - "-exported_symbols_list", - src/dyld.exp, - "-nostdlib", - "-lstdc++", - /usr/local/lib/system/libc.a, - "-lgcc", - "-Wl,-e,__dyld_start", - "-Wl,-dylinker", - "-Wl,-dylinker_install_name,/usr/lib/dyld", - ); - OTHER_REZFLAGS = ""; - 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", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); - }; dependencies = ( + F939373A0A94FB6E00070A07 /* PBXTargetDependency */, ); name = dyld; productName = dyld; @@ -452,38 +470,20 @@ isa = PBXNativeTarget; buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld" */; buildPhases = ( + F9AC7E7E0B7BB3D300FEB38B /* ShellScript */, + F9AB70540BA73985002F6068 /* ShellScript */, F9ED4C9C0630A76B00DF4E74 /* Sources */, - F9ED4C9B0630A76B00DF4E74 /* Headers */, F93AA9B30630AE8200301D9F /* CopyFiles */, F9574CB206C95C0D00142BFA /* CopyFiles */, F93AA9B60630AEB100301D9F /* CopyFiles */, F93AA9C20630AF0700301D9F /* CopyFiles */, F93AA9C60630AF1F00301D9F /* CopyFiles */, - F9CA205D06CBF578000BA084 /* ShellScript */, - F9ED4C9D0630A76B00DF4E74 /* Frameworks */, + F918692408B16F6900E0F9DB /* ShellScript */, ); buildRules = ( F921D31E070376F1000D1056 /* PBXBuildRule */, F9574C4906C94DA700142BFA /* PBXBuildRule */, ); - buildSettings = { - ARCHS = ppc; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - HEADER_SEARCH_PATHS = ./include; - INSTALL_PATH = /usr/local/lib/system; - LIBRARY_STYLE = STATIC; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = dyldapis; - SECTORDER_FLAGS = ""; - VALID_ARCHS = "ppc ppc64 i386 x86_64"; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); - }; dependencies = ( ); name = libdyld; @@ -497,57 +497,126 @@ F9ED4C8B0630A72300DF4E74 /* Project object */ = { isa = PBXProject; buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */; - buildSettings = { - }; - buildStyles = ( - F9ED4C890630A72300DF4E74 /* Development */, - F9ED4C8A0630A72300DF4E74 /* Deployment */, - ); + compatibilityVersion = "Xcode 2.4"; hasScannedForEncodings = 1; mainGroup = F9ED4C870630A72200DF4E74; productRefGroup = F9ED4C990630A76000DF4E74 /* Products */; projectDirPath = ""; + projectRoot = ""; targets = ( F9ED4C920630A73900DF4E74 /* all */, F9ED4C970630A76000DF4E74 /* dyld */, F9ED4C9E0630A76B00DF4E74 /* libdyld */, + F93937310A94FAF700070A07 /* update_dyld_shared_cache */, ); }; /* End PBXProject section */ /* Begin PBXShellScriptBuildPhase section */ - F9CA205D06CBF578000BA084 /* ShellScript */ = { + F918692408B16F6900E0F9DB /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( - "${DSTROOT}/usr/local/lib/system/ldyldapis.a", ); outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "cd ${DSTROOT}/usr/local/lib/system\nln -s libdyldapis.a libdyldapis_profile.a\nln -s libdyldapis.a libdyldapis_debug.a\n"; + 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 */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/version.c", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/Developer/Makefiles/bin/version.pl ${ProjectName} > ${BUILT_PRODUCTS_DIR}/version.c\n"; + showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + F939372F0A94FAF700070A07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93937470A94FC4700070A07 /* update_dyld_shared_cache.cpp in Sources */, + F93075C30BA1FE4D004BCA09 /* dyld_shared_cache_server.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F9ED4C950630A76000DF4E74 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */, - F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */, + F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */, F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */, F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */, F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */, - F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */, - F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */, + F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */, F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */, F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */, F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */, F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */, + F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -558,12 +627,24 @@ F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */, F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */, F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */, + F9AC7E940B7BB67700FEB38B /* version.c in Sources */, + F9AB705B0BA73A11002F6068 /* dyld_shared_cache_user.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + F93937380A94FB6A00070A07 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93937310A94FAF700070A07 /* update_dyld_shared_cache */; + targetProxy = F93937370A94FB6A00070A07 /* PBXContainerItemProxy */; + }; + F939373A0A94FB6E00070A07 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93937310A94FAF700070A07 /* update_dyld_shared_cache */; + targetProxy = F93937390A94FB6E00070A07 /* PBXContainerItemProxy */; + }; F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F9ED4C970630A76000DF4E74 /* dyld */; @@ -577,67 +658,90 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - F9D8C7DE087B087300E93EFB /* Development */ = { + F93937350A94FB2900070A07 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; - DEAD_CODE_STRIPPING = NO; - EXPORTED_SYMBOLS_FILE = ""; + DEBUG_INFORMATION_FORMAT = dwarf; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_CPP_RTTI = NO; - GCC_MODEL_TUNING = G4; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = ./include; - INSTALL_PATH = /usr/lib; - LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.4; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS_i386 = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_ppc = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_ppc64 = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld64.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_x86_64 = "-lstdc++-static -seg1addr 0x7fff5fc00000 -exported_symbols_list src/dyld64.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_REZFLAGS = ""; - PER_ARCH_CFLAGS_ppc = ""; - PER_ARCH_CFLAGS_ppc64 = "-msoft-float"; + GCC_THREADSAFE_STATICS = NO; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - PRODUCT_NAME = dyld; - STRIPFLAGS = "-S"; - UNSTRIPPED_PRODUCT = NO; - VALID_ARCHS = "ppc ppc64 i386 x86_64"; - VERSIONING_SYSTEM = "apple-generic"; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); + PRODUCT_NAME = update_dyld_shared_cache; + VALID_ARCHS = "ppc i386"; }; - name = Development; + name = Debug; }; - F9D8C7DF087B087300E93EFB /* Deployment */ = { + F93937360A94FB2900070A07 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ppc; COPY_PHASE_STRIP = YES; 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"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + F9D8C7DE087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BASE_ADDRESS_i386 = 0x8f000000; + BASE_ADDRESS_ppc = 0x8f000000; + BASE_ADDRESS_ppc64 = 0x7fff5fc00000; + BASE_ADDRESS_x86_64 = 0x7fff5fc00000; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; - EXPORTED_SYMBOLS_FILE = ""; + 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"; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_CPP_RTTI = NO; GCC_MODEL_TUNING = G4; - GCC_OPTIMIZATION_LEVEL = 3; + GCC_OPTIMIZATION_LEVEL = 0; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = ./include; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + HEADER_SEARCH_PATHS = ( + ./include, + "./launch-cache", + ); INSTALL_PATH = /usr/lib; - LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.4; + MACOSX_DEPLOYMENT_TARGET = 10.5; OTHER_CFLAGS = ""; - OTHER_LDFLAGS_i386 = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_ppc = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_ppc64 = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld64.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_x86_64 = "-lstdc++-static -seg1addr 0x7fff5fc00000 -exported_symbols_list src/dyld64.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_REZFLAGS = ""; + OTHER_LDFLAGS = ( + "-seg1addr", + "$(BASE_ADDRESS_$(CURRENT_ARCH))", + "-lstdc++-static", + "-nostdlib", + /usr/local/lib/system/libc.a, + "-lgcc_eh", + "-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; @@ -649,33 +753,51 @@ WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", - "-Wno-unknown-pragmas", ); }; - name = Deployment; + name = Debug; }; - F9D8C7E0087B087300E93EFB /* Default */ = { + F9D8C7E0087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ppc; + 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 = "$(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"; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_CPP_RTTI = NO; GCC_MODEL_TUNING = G4; GCC_OPTIMIZATION_LEVEL = 3; GCC_SYMBOLS_PRIVATE_EXTERN = NO; - HEADER_SEARCH_PATHS = ./include; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + ./include, + "./launch-cache", + ); INSTALL_PATH = /usr/lib; - LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.4; + MACOSX_DEPLOYMENT_TARGET = 10.5; OTHER_CFLAGS = ""; - OTHER_LDFLAGS_i386 = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_ppc = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_ppc64 = "-lstdc++-static -seg1addr 8fe00000 -exported_symbols_list src/dyld64.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_LDFLAGS_x86_64 = "-lstdc++-static -seg1addr 0x7fff5fc00000 -exported_symbols_list src/dyld64.exp -nostdlib /usr/local/lib/system/libc.a -lgcc_eh -lgcc -Wl,-e,__dyld_start -Wl,-dylinker -Wl,-dylinker_install_name,/usr/lib/dyld"; - OTHER_REZFLAGS = ""; + OTHER_LDFLAGS = ( + "-seg1addr", + "$(BASE_ADDRESS_$(CURRENT_ARCH))", + "-lstdc++-static", + "-nostdlib", + /usr/local/lib/system/libc.a, + "-lgcc_eh", + "-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; @@ -690,24 +812,19 @@ "-Wno-unknown-pragmas", ); }; - name = Default; + name = Release; }; - F9D8C7E2087B087300E93EFB /* Development */ = { + F9D8C7E2087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ppc; - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = NO; + ARCHS = "$(NATIVE_ARCH_ACTUAL)"; GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; GCC_OPTIMIZATION_LEVEL = 0; HEADER_SEARCH_PATHS = ./include; INSTALL_PATH = /usr/local/lib/system; LIBRARY_STYLE = STATIC; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; PRODUCT_NAME = dyldapis; - SECTORDER_FLAGS = ""; VALID_ARCHS = "ppc ppc64 i386 x86_64"; WARNING_CFLAGS = ( "-Wmost", @@ -715,45 +832,19 @@ "-Wno-unknown-pragmas", ); }; - name = Development; + name = Debug; }; - F9D8C7E3087B087300E93EFB /* Deployment */ = { + F9D8C7E4087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = ppc; - COPY_PHASE_STRIP = YES; + COPY_PHASE_STRIP = NO; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; HEADER_SEARCH_PATHS = ./include; INSTALL_PATH = /usr/local/lib/system; LIBRARY_STYLE = STATIC; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = dyldapis; - SECTORDER_FLAGS = ""; - VALID_ARCHS = "ppc ppc64 i386 x86_64"; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); - }; - name = Deployment; - }; - F9D8C7E4087B087300E93EFB /* Default */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = ppc; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - HEADER_SEARCH_PATHS = ./include; - INSTALL_PATH = /usr/local/lib/system; - LIBRARY_STYLE = STATIC; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; PRODUCT_NAME = dyldapis; - SECTORDER_FLAGS = ""; VALID_ARCHS = "ppc ppc64 i386 x86_64"; WARNING_CFLAGS = ( "-Wmost", @@ -761,121 +852,81 @@ "-Wno-unknown-pragmas", ); }; - name = Default; + name = Release; }; - F9D8C7E6087B087300E93EFB /* Development */ = { + F9D8C7E6087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; - DEAD_CODE_STRIPPING = NO; - GCC_OPTIMIZATION_LEVEL = 0; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; PRODUCT_NAME = all; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); }; - name = Development; + name = Debug; }; - F9D8C7E7087B087300E93EFB /* Deployment */ = { + F9D8C7E8087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = YES; - GCC_ENABLE_CPP_RTTI = NO; - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; PRODUCT_NAME = all; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); }; - name = Deployment; + name = Release; }; - F9D8C7E8087B087300E93EFB /* Default */ = { + F9D8C7EA087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - OTHER_CFLAGS = ""; - OTHER_LDFLAGS = ""; - OTHER_REZFLAGS = ""; - PRODUCT_NAME = all; - SECTORDER_FLAGS = ""; - WARNING_CFLAGS = ( - "-Wmost", - "-Wno-four-char-constants", - "-Wno-unknown-pragmas", - ); }; - name = Default; + name = Debug; }; - F9D8C7EA087B087300E93EFB /* Development */ = { + F9D8C7EC087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; buildSettings = { }; - name = Development; - }; - F9D8C7EB087B087300E93EFB /* Deployment */ = { - isa = XCBuildConfiguration; - buildSettings = { - }; - name = Deployment; - }; - F9D8C7EC087B087300E93EFB /* Default */ = { - isa = XCBuildConfiguration; - buildSettings = { - }; - name = Default; + name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93937350A94FB2900070A07 /* Debug */, + F93937360A94FB2900070A07 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */ = { isa = XCConfigurationList; buildConfigurations = ( - F9D8C7DE087B087300E93EFB /* Development */, - F9D8C7DF087B087300E93EFB /* Deployment */, - F9D8C7E0087B087300E93EFB /* Default */, + F9D8C7DE087B087300E93EFB /* Debug */, + F9D8C7E0087B087300E93EFB /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Default; + defaultConfigurationName = Release; }; F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld" */ = { isa = XCConfigurationList; buildConfigurations = ( - F9D8C7E2087B087300E93EFB /* Development */, - F9D8C7E3087B087300E93EFB /* Deployment */, - F9D8C7E4087B087300E93EFB /* Default */, + F9D8C7E2087B087300E93EFB /* Debug */, + F9D8C7E4087B087300E93EFB /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Default; + defaultConfigurationName = Release; }; F9D8C7E5087B087300E93EFB /* Build configuration list for PBXAggregateTarget "all" */ = { isa = XCConfigurationList; buildConfigurations = ( - F9D8C7E6087B087300E93EFB /* Development */, - F9D8C7E7087B087300E93EFB /* Deployment */, - F9D8C7E8087B087300E93EFB /* Default */, + F9D8C7E6087B087300E93EFB /* Debug */, + F9D8C7E8087B087300E93EFB /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Default; + defaultConfigurationName = Release; }; F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */ = { isa = XCConfigurationList; buildConfigurations = ( - F9D8C7EA087B087300E93EFB /* Development */, - F9D8C7EB087B087300E93EFB /* Deployment */, - F9D8C7EC087B087300E93EFB /* Default */, + F9D8C7EA087B087300E93EFB /* Debug */, + F9D8C7EC087B087300E93EFB /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Default; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/include/dlfcn.h b/include/dlfcn.h index 77d7521..2e502c5 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -36,7 +36,9 @@ extern "C" { #include -#ifndef _POSIX_C_SOURCE +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +#include +#include /* * Structure filled in by dladdr(). */ @@ -55,20 +57,27 @@ extern char * dlerror(void); extern void * dlopen(const char * __path, int __mode); extern void * dlsym(void * __handle, const char * __symbol); +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +extern bool dlopen_preflight(const char* __path) AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER; +#endif /* not POSIX */ + + #define RTLD_LAZY 0x1 #define RTLD_NOW 0x2 #define RTLD_LOCAL 0x4 #define RTLD_GLOBAL 0x8 -#ifndef _POSIX_C_SOURCE +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) #define RTLD_NOLOAD 0x10 #define RTLD_NODELETE 0x80 +#define RTLD_FIRST 0x100 /* Mac OS X 10.5 and later */ /* * Special handle arguments for dlsym(). */ #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) */ #endif /* not POSIX */ #ifdef __cplusplus diff --git a/include/mach-o/dyld-update-prebinding.h b/include/mach-o/dyld-update-prebinding.h new file mode 100644 index 0000000..9e09413 --- /dev/null +++ b/include/mach-o/dyld-update-prebinding.h @@ -0,0 +1,19 @@ + +#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 62cb9a7..10b7099 100644 --- a/include/mach-o/dyld.h +++ b/include/mach-o/dyld.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,33 +23,111 @@ #ifndef _MACH_O_DYLD_H_ #define _MACH_O_DYLD_H_ -#if __cplusplus -extern "C" { -#endif /* __cplusplus */ #include #include -#if __cplusplus - /* C++ has bool type built in */ -#else - #include -#endif +#include + #include #include +#if __cplusplus +extern "C" { +#endif + +/* + * The following functions allow you to iterate through all loaded images. + * This is not a thread safe operation. Another thread can add or remove + * an image during the iteration. + * + * Many uses of these routines can be replace by a call to dladdr() which + * 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; + + +/* + * The following functions allow you to install callbacks which will be called + * by dyld whenever an image is loaded or unloaded. During a call to _dyld_register_func_for_add_image() + * the callback func is called for every existing image. Later, it is called as each new image + * is loaded and bound (but initializers not yet run). The callback registered with + * _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; + + +/* + * NSVersionOfRunTimeLibrary() returns the current_version number of the currently dylib + * 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; + + +/* + * NSVersionOfRunTimeLibrary() returns the current_version number that the main executable was linked + * against at build time. The libraryName parameter would be "bar" for /path/libbar.3.dylib and + * "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; + + +/* + * _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter + * should initially be the size of the buffer. The function returns 0 if the path was successfully copied. + * It returns -1 if the buffer is not large enough, and *bufsize is set to the size required. + * + * Note that _NSGetExecutablePath will return "a path" to the executable not a "real path" to the executable. + * That is the path may be a symbolic link and not the real file. With deep directories the total bufsize + * needed could be more than MAXPATHLEN. + */ +extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER; + + + +/* + * _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; + + + + + +/* + * The following dyld API's are deprecated as of Mac OS X 10.5. They are either + * no longer necessary or are superceeded by dlopen and friends in . + * dlopen/dlsym/dlclose have been available since Mac OS X 10.3 and work with + * dylibs and bundles. + * + * NSAddImage -> dlopen + * NSLookupSymbolInImage -> dlsym + * NSCreateObjectFileImageFromFile -> dlopen + * NSDestroyObjectFileImage -> dlclose + * NSLinkModule -> not needed when dlopen used + * NSUnLinkModule -> not needed when dlclose used + * NSLookupSymbolInModule -> dlsym + * _dyld_image_containing_address -> dladdr + * NSLinkEditError -> dlerror + * + */ + #ifndef ENUM_DYLD_BOOL #define ENUM_DYLD_BOOL -#undef FALSE -#undef TRUE -enum DYLD_BOOL { - FALSE, - TRUE -}; + #undef FALSE + #undef TRUE + enum DYLD_BOOL { FALSE, TRUE }; #endif /* ENUM_DYLD_BOOL */ -/* - * The high level NS... API. - */ /* Object file image API */ typedef enum { @@ -63,119 +141,52 @@ typedef enum { typedef struct __NSObjectFileImage* NSObjectFileImage; -/* limited implementation, only MH_BUNDLE files can be used */ -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile( - const char *pathName, - NSObjectFileImage *objectFileImage); -extern NSObjectFileImageReturnCode NSCreateCoreFileImageFromFile( - const char *pathName, - NSObjectFileImage *objectFileImage); -extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory( - const void *address, - size_t size, - NSObjectFileImage *objectFileImage); -extern bool NSDestroyObjectFileImage( - NSObjectFileImage objectFileImage); -/* - * API on NSObjectFileImage's for: - * "for Each Symbol Definition In Object File Image" (for Dynamic Bundles) - * and the same thing for references - */ -extern uint32_t NSSymbolDefinitionCountInObjectFileImage( - NSObjectFileImage objectFileImage); -extern const char * NSSymbolDefinitionNameInObjectFileImage( - NSObjectFileImage objectFileImage, - uint32_t ordinal); -extern uint32_t NSSymbolReferenceCountInObjectFileImage( - NSObjectFileImage objectFileImage); -extern const char * NSSymbolReferenceNameInObjectFileImage( - NSObjectFileImage objectFileImage, - uint32_t ordinal, - bool *tentative_definition); /* can be NULL */ -/* - * API on NSObjectFileImage: - * "does Object File Image define symbol name X" (using sorted symbol table) - * and a way to get the named objective-C section - */ -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); /* can be NULL */ -/* SPI first appeared in Mac OS X 10.3 */ -extern bool NSHasModInitObjectFileImage( - NSObjectFileImage objectFileImage) - AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; - -/* module API */ +/* 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; + typedef struct __NSModule* NSModule; -extern const char * NSNameOfModule( - NSModule m); -extern const char * NSLibraryNameForModule( - NSModule m); - -/* limited implementation, only MH_BUNDLE files can be linked */ -extern NSModule NSLinkModule( - NSObjectFileImage objectFileImage, - const char *moduleName, - uint32_t options); -#define NSLINKMODULE_OPTION_NONE 0x0 -#define NSLINKMODULE_OPTION_BINDNOW 0x1 -#define NSLINKMODULE_OPTION_PRIVATE 0x2 -#define NSLINKMODULE_OPTION_RETURN_ON_ERROR 0x4 -#define NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES 0x8 -#define NSLINKMODULE_OPTION_TRAILING_PHYS_NAME 0x10 - -/* limited implementation, only modules loaded with NSLinkModule() can be - unlinked */ -extern bool NSUnLinkModule( - NSModule module, - uint32_t options); -#define NSUNLINKMODULE_OPTION_NONE 0x0 -#define NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED 0x1 +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 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; +#define NSLINKMODULE_OPTION_NONE 0x0 +#define NSLINKMODULE_OPTION_BINDNOW 0x1 +#define NSLINKMODULE_OPTION_PRIVATE 0x2 +#define NSLINKMODULE_OPTION_RETURN_ON_ERROR 0x4 +#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; +#define NSUNLINKMODULE_OPTION_NONE 0x0 +#define NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED 0x1 #define NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES 0x2 -/* not yet implemented */ -extern NSModule NSReplaceModule( - NSModule moduleToReplace, - NSObjectFileImage newObjectFileImage, - uint32_t options); - /* 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); -extern NSSymbol NSLookupSymbolInImage( - const struct mach_header *image, - const char *symbolName, - uint32_t options); +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; #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); -extern void * NSAddressOfSymbol( - NSSymbol symbol); -extern NSModule NSModuleForSymbol( - NSSymbol symbol); +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; /* error handling API */ typedef enum { @@ -203,119 +214,41 @@ typedef enum { NSOtherErrorInvalidArgs } NSOtherErrorNumbers; -extern void NSLinkEditError( - NSLinkEditErrors *c, - int *errorNumber, - const char **fileName, - const char **errorString); +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; typedef struct { - void (*undefined)(const char *symbolName); + void (*undefined)(const char* symbolName); NSModule (*multiple)(NSSymbol s, NSModule oldModule, NSModule newModule); void (*linkEdit)(NSLinkEditErrors errorClass, int errorNumber, - const char *fileName, const char *errorString); + const char* fileName, const char* errorString); } NSLinkEditErrorHandlers; -extern void NSInstallLinkEditErrorHandlers( - const NSLinkEditErrorHandlers *handlers); - -/* other API */ -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); +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 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; #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 int32_t NSVersionOfRunTimeLibrary( - const char *libraryName); -extern int32_t NSVersionOfLinkTimeLibrary( - const char *libraryName); -extern int _NSGetExecutablePath( /* SPI first appeared in Mac OS X 10.2 */ - char *buf, - uint32_t *bufsize); -/* - * The low level _dyld_... API. - * (used by the objective-C runtime primarily) - */ -extern bool _dyld_present( - void); - -extern uint32_t _dyld_image_count( - void); -extern const struct mach_header * _dyld_get_image_header( - uint32_t image_index); -extern intptr_t _dyld_get_image_vmaddr_slide( - uint32_t image_index); -extern const char * _dyld_get_image_name( - uint32_t image_index); - -extern void _dyld_register_func_for_add_image( - void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)); -extern void _dyld_register_func_for_remove_image( - void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)); -extern void _dyld_register_func_for_link_module( - void (*func)(NSModule module)); -/* not yet implemented */ -extern void _dyld_register_func_for_unlink_module( - void (*func)(NSModule module)); -/* not yet implemented */ -extern void _dyld_register_func_for_replace_module( - void (*func)(NSModule oldmodule, NSModule newmodule)); -extern void _dyld_get_objc_module_sect_for_module( - NSModule module, - void **objc_module, - size_t *size); -extern void _dyld_bind_objc_module( - const void *objc_module); -extern bool _dyld_bind_fully_image_containing_address( - const void *address); -extern bool _dyld_image_containing_address( - const void* address); -/* SPI first appeared in Mac OS X 10.3 */ -extern const struct mach_header * _dyld_get_image_header_containing_address( - const void* address) - AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; - -extern void _dyld_moninit( - void (*monaddition)(char *lowpc, char *highpc)); -extern bool _dyld_launched_prebound( - void); -/* SPI first appeared in Mac OS X 10.3 */ -extern bool _dyld_all_twolevel_modules_prebound( - void) - AVAILABLE_MAC_OS_X_VERSION_10_3_AND_LATER; - -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_objc( - const char *symbol_name, - void **address, - NSModule* module); -extern void _dyld_lookup_and_bind_fully( - const char *symbol_name, - void **address, - NSModule* module); - -extern int _dyld_func_lookup( - const char *dyld_func_name, - void **address); +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; + #if __cplusplus } -#endif /* __cplusplus */ +#endif #endif /* _MACH_O_DYLD_H_ */ diff --git a/include/mach-o/dyld_gdb.h b/include/mach-o/dyld_gdb.h index 9910dcd..04137ac 100644 --- a/include/mach-o/dyld_gdb.h +++ b/include/mach-o/dyld_gdb.h @@ -22,21 +22,20 @@ */ #ifndef _DYLD_GDB_ #define _DYLD_GDB_ + /* - * This file describes the interface between gdb and dyld created for - * MacOS X GM. Prior to MacOS X GM gdb used the dyld_debug interfaces - * described in . + * For Mac OS X 10.4 or later, use the interface in mach-o/dylib_images.h */ - +#include #ifdef __cplusplus extern "C" { #endif - -#define OLD_GDB_DYLD_INTERFACE __ppc__ || __i386__ - -#if OLD_GDB_DYLD_INTERFACE +/* + * Prior to Mac OS 10.4, this is the interface gdb used to discover the mach-o images loaded in a process + */ +#if __ppc__ || __i386__ /* * gdb_dyld_version is the version of gdb interface that dyld is currently * exporting. For the interface described in this header file gdb_dyld_version @@ -112,51 +111,7 @@ extern unsigned int gdb_nlibrary_images; /* the size of each gdb_library_image structure */ extern unsigned int gdb_library_image_size; -#endif /* OLD_GDB_DYLD_INTERFACE */ - - -/* - * Beginning in Mac OS X 10.4, there is a new mechanism for dyld to notify gdb and other about new images. - * - * - */ - -enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1 }; - -struct dyld_image_info { - const struct mach_header* imageLoadAddress; /* base address image is mapped into */ - const char* imageFilePath; /* path dyld used to load the image */ - uintptr_t imageFileModDate; /* time_t of image file */ - /* if stat().st_mtime of imageFilePath does not match imageFileModDate, */ - /* then file has been modified since dyld loaded it */ -}; - - -typedef void (*dyld_image_notifier)(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]); - -/* - * gdb looks for the symbol "_dyld_all_image_infos" in dyld. It contains the fields below. - * - * For a snap shot of what images are currently loaded, the infoArray fields contain a pointer - * to an array of all images. If infoArray is NULL, it means it is being modified, come back later. - * - * To be notified of changes, gdb sets a break point on the notification field. The function - * it points to is called by dyld with an array of information about what images have been added - * (dyld_image_adding) or are about to be removed (dyld_image_removing). - * - * 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. - */ - struct dyld_all_image_infos { - uint32_t version; /* == 1 in Mac OS X 10.4 */ - uint32_t infoArrayCount; - const struct dyld_image_info* infoArray; - dyld_image_notifier notification; - bool processDetachedFromSharedRegion; -}; -extern struct dyld_all_image_infos dyld_all_image_infos; - +#endif diff --git a/include/mach-o/dyld_images.h b/include/mach-o/dyld_images.h new file mode 100644 index 0000000..1f7b96b --- /dev/null +++ b/include/mach-o/dyld_images.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _DYLD_IMAGES_ +#define _DYLD_IMAGES_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * Beginning in Mac OS X 10.4, this is how gdb discovers which mach-o images are loaded in a process. + * + * gdb looks for the symbol "_dyld_all_image_infos" in dyld. It contains the fields below. + * + * For a snashot of what images are currently loaded, the infoArray fields contain a pointer + * to an array of all images. If infoArray is NULL, it means it is being modified, come back later. + * + * To be notified of changes, gdb sets a break point on the address pointed to by the notificationn + * field. The function it points to is called by dyld with an array of information about what images + * have been added (dyld_image_adding) or are about to be removed (dyld_image_removing). + * + * 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. + */ + +enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1 }; + +struct dyld_image_info { + const struct mach_header* imageLoadAddress; /* base address image is mapped into */ + const char* imageFilePath; /* path dyld used to load the image */ + uintptr_t imageFileModDate; /* time_t of image file */ + /* if stat().st_mtime of imageFilePath does not match imageFileModDate, */ + /* then file has been modified since dyld loaded it */ +}; + +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 infoArrayCount; + const struct dyld_image_info* infoArray; + dyld_image_notifier notification; + bool processDetachedFromSharedRegion; +}; +extern struct dyld_all_image_infos dyld_all_image_infos; + + + + +/* + * Beginning in Mac OS X 10.5, this is how gdb discovers where the shared cache is in a process. + * Images that are in the shared cache have their segments rearranged, so when using imageFilePath + * to load the file from disk, you have to know to adjust addresses based on how their segment + * was rearranged. + * + * gdb looks for the symbol "_dyld_shared_region_ranges" in dyld. + * + * It contains information the count of shared regions used by the process. The count is + * the number of start/length pairs. + */ +struct dyld_shared_cache_ranges { + uintptr_t sharedRegionsCount; /* how many ranges follow */ + struct { + uintptr_t start; + uintptr_t length; + } ranges[4]; /* max regions */ +}; +extern struct dyld_shared_cache_ranges dyld_shared_cache_ranges; + + + +#ifdef __cplusplus +} +#endif + +#endif /* _DYLD_IMAGES_ */ diff --git a/include/mach-o/dyld_priv.h b/include/mach-o/dyld_priv.h index 5111ebb..8d327f4 100644 --- a/include/mach-o/dyld_priv.h +++ b/include/mach-o/dyld_priv.h @@ -1,5 +1,6 @@ -/* - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,6 +26,7 @@ #include +#include #if __cplusplus extern "C" { @@ -51,6 +53,53 @@ NSFindSectionAndOffsetInObjectFileImage( unsigned long* sectionOffset); /* can be NULL */ +// +// Possible state changes for which you can register to be notified +// +enum dyld_image_states +{ + dyld_image_state_mapped = 10, // No batch notification for this + dyld_image_state_dependents_mapped = 20, // Only batch notification for this + dyld_image_state_rebased = 30, + 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 +}; + +// +// Callback that provides a bottom-up array of images +// For dyld_image_state_[dependents_]mapped state only, returning non-NULL will cause dyld to abort loading all those images +// and append the returned string to its load failure error message. dyld does not free the string, so +// it should be a literal string or a static buffer +// +typedef const char* (*dyld_image_state_change_handler)(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]); + +// +// Register a handler to be called when any image changes to the requested state. +// If 'batch' is true, the callback is called with an array of all images that are in the requested state sorted by dependency. +// If 'batch' is false, the callback is called with one image at a time as each image transitions to the the requested state. +// During the call to this function, the handler may be called back with existing images and the handler should +// not return a string, since there is no load to abort. In batch mode, existing images at or past the request +// state supplied in the callback. In non-batch mode, the callback is called for each image exactly in the +// requested state. +// +extern void +dyld_register_image_state_change_handler(enum dyld_image_states state, bool batch, dyld_image_state_change_handler handler); + + +// +// +// +extern void +_dyld_library_locator(const char* (*handler)(const char*)); + +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) */ + + #if __cplusplus diff --git a/launch-cache/Architectures.hpp b/launch-cache/Architectures.hpp new file mode 100644 index 0000000..e735f9e --- /dev/null +++ b/launch-cache/Architectures.hpp @@ -0,0 +1,80 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __ARCHITECTURES__ +#define __ARCHITECTURES__ + +#include "FileAbstraction.hpp" + + +// +// Architectures +// +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 }; +}; + + + + + + +#endif // __ARCHITECTURES__ + + diff --git a/launch-cache/CacheFileAbstraction.hpp b/launch-cache/CacheFileAbstraction.hpp new file mode 100644 index 0000000..cb98ac3 --- /dev/null +++ b/launch-cache/CacheFileAbstraction.hpp @@ -0,0 +1,116 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_ABSTRACTION__ +#define __DYLD_CACHE_ABSTRACTION__ + +#include "dyld_cache_format.h" + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +template +class dyldCacheHeader { +public: + const char* magic() const INLINE { return fields.magic; } + void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); } + + //uint32_t architecture() const INLINE { return E::get32(fields.architecture); } + //void set_architecture(uint32_t value) INLINE { E::set32(fields.architecture, value); } + + uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); } + void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); } + + uint32_t mappingCount() const INLINE { return E::get32(fields.mappingCount); } + void set_mappingCount(uint32_t value) INLINE { E::set32(fields.mappingCount, value); } + + uint32_t imagesOffset() const INLINE { return E::get32(fields.imagesOffset); } + void set_imagesOffset(uint32_t value) INLINE { E::set32(fields.imagesOffset, value); } + + uint32_t imagesCount() const INLINE { return E::get32(fields.imagesCount); } + void set_imagesCount(uint32_t value) INLINE { E::set32(fields.imagesCount, value); } + + uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); } + void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); } + + //uint32_t dependenciesOffset() const INLINE { return E::get32(fields.dependenciesOffset); } + //void set_dependenciesOffset(uint32_t value) INLINE { E::set32(fields.dependenciesOffset, value); } + + //uint32_t dependenciesCount() const INLINE { return E::get32(fields.dependenciesCount); } + //void set_dependenciesCount(uint32_t value) INLINE { E::set32(fields.dependenciesCount, value); } + +private: + dyld_cache_header fields; +}; + + +template +class dyldCacheFileMapping { +public: + uint64_t address() const INLINE { return E::get64(fields.sfm_address); } + void set_address(uint64_t value) INLINE { E::set64(fields.sfm_address, value); } + + uint64_t size() const INLINE { return E::get64(fields.sfm_size); } + void set_size(uint64_t value) INLINE { E::set64(fields.sfm_size, value); } + + uint64_t file_offset() const INLINE { return E::get64(fields.sfm_file_offset); } + void set_file_offset(uint64_t value) INLINE { E::set64(fields.sfm_file_offset, value); } + + uint32_t max_prot() const INLINE { return E::get32(fields.sfm_max_prot); } + void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.sfm_max_prot, value); } + + uint32_t init_prot() const INLINE { return E::get32(fields.sfm_init_prot); } + void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.sfm_init_prot, value); } + +private: + shared_file_mapping_np fields; +}; + + +template +class dyldCacheImageInfo { +public: + uint64_t address() const INLINE { return E::get64(fields.address); } + void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } + + uint64_t modTime() const INLINE { return E::get64(fields.modTime); } + void set_modTime(uint64_t value) INLINE { E::set64(fields.modTime, value); } + + uint64_t inode() const INLINE { return E::get64(fields.inode); } + void set_inode(uint64_t value) INLINE { E::set64(fields.inode, value); } + + uint32_t pathFileOffset() const INLINE { return E::get32(fields.pathFileOffset); } + void set_pathFileOffset(uint32_t value) INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; } + + //uint32_t dependenciesStartOffset() const INLINE { return E::get32(fields.dependenciesStartOffset); } + //void set_dependenciesStartOffset(uint32_t value) INLINE { E::set32(fields.dependenciesStartOffset, value); } + +private: + dyld_cache_image_info fields; +}; + + + +#endif // __DYLD_CACHE_ABSTRACTION__ + + diff --git a/launch-cache/FileAbstraction.hpp b/launch-cache/FileAbstraction.hpp new file mode 100644 index 0000000..1f7a629 --- /dev/null +++ b/launch-cache/FileAbstraction.hpp @@ -0,0 +1,145 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/launch-cache/MachOBinder.hpp b/launch-cache/MachOBinder.hpp new file mode 100644 index 0000000..cb37248 --- /dev/null +++ b/launch-cache/MachOBinder.hpp @@ -0,0 +1,590 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __MACHO_BINDER__ +#define __MACHO_BINDER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "MachOLayout.hpp" +#include "MachORebaser.hpp" + + + + +template +class Binder : public Rebaser +{ +public: + struct CStringEquals { + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map*, __gnu_cxx::hash, CStringEquals> Map; + + + Binder(const MachOLayoutAbstraction&, uint64_t dyldBaseAddress); + virtual ~Binder() {} + + const char* getDylibID() const; + void setDependentBinders(const Map& map); + void bind(); + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + struct BinderAndReExportFlag { Binder* binder; bool reExport; }; + typedef __gnu_cxx::hash_map*, __gnu_cxx::hash, CStringEquals> NameToSymbolMap; + + void doBindExternalRelocations(); + void doBindIndirectSymbols(); + void doSetUpDyldSection(); + void doSetPreboundUndefines(); + pint_t resolveUndefined(const macho_nlist

* undefinedSymbol); + const macho_nlist

* findExportedSymbol(const char* name); + void bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value); + const char* parentUmbrella(); + + static uint8_t pointerRelocSize(); + static uint8_t pointerRelocType(); + + std::vector fDependentDylibs; + NameToSymbolMap fHashTable; + uint64_t fDyldBaseAddress; + const macho_nlist

* fSymbolTable; + const char* fStrings; + const macho_dysymtab_command

* fDynamicInfo; + const macho_segment_command

* fFristWritableSegment; + const macho_dylib_command

* fDylibID; + const macho_dylib_command

* fParentUmbrella; + bool fOriginallyPrebound; +}; + + +template +Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress) + : Rebaser(layout), fDyldBaseAddress(dyldBaseAddress), + fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL), + fFristWritableSegment(NULL), fDylibID(NULL), + fParentUmbrella(NULL) +{ + fOriginallyPrebound = ((this->fHeader->flags() & MH_PREBOUND) != 0); + // update header flags so the cache looks prebound split-seg (0x80000000 is in-shared-cache bit) + ((macho_header

*)this->fHeader)->set_flags(this->fHeader->flags() | MH_PREBOUND | MH_SPLIT_SEGS | 0x80000000); + + // calculate fDynamicInfo, fStrings, fSymbolTable + const macho_load_command

* const cmds = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + fSymbolTable = (macho_nlist

*)(&this->fLinkEditBase[symtab->symoff()]); + fStrings = (const char*)&this->fLinkEditBase[symtab->stroff()]; + break; + case LC_DYSYMTAB: + fDynamicInfo = (macho_dysymtab_command

*)cmd; + break; + case LC_ID_DYLIB: + ((macho_dylib_command

*)cmd)->set_timestamp(0); + fDylibID = (macho_dylib_command

*)cmd; + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + ((macho_dylib_command

*)cmd)->set_timestamp(0); + break; + case LC_SUB_FRAMEWORK: + fParentUmbrella = (macho_dylib_command

*)cmd; + break; + default: + if ( cmd->cmd() & LC_REQ_DYLD ) + throwf("unknown required load command %d", cmd->cmd()); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( fDynamicInfo == NULL ) + throw "no LC_DYSYMTAB"; + 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; + } + } + 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; + } + } + +} + +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::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 +const char* Binder::getDylibID() const +{ + if ( fDylibID != NULL ) + return fDylibID->name(); + else + return NULL; +} + +template +const char* Binder::parentUmbrella() +{ + if ( fParentUmbrella != NULL ) + return fParentUmbrella->name(); + else + return NULL; +} + + + +template +void Binder::setDependentBinders(const Map& map) +{ + // first pass to build vector of dylibs + const macho_load_command

* const cmds = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + const char* path = ((struct macho_dylib_command

*)cmd)->name(); + typename Map::const_iterator pos = map.find(path); + if ( pos != map.end() ) { + BinderAndReExportFlag entry; + entry.binder = pos->second; + entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB ); + fDependentDylibs.push_back(entry); + } + else { + // the load command string does not match the install name of any loaded dylib + // this could happen if there was not a world build and some dylib changed its + // install path to be some symlinked path + + // use realpath() and walk map looking for a realpath match + bool found = false; + char targetPath[PATH_MAX]; + if ( realpath(path, targetPath) != NULL ) { + for(typename Map::const_iterator it=map.begin(); it != map.end(); ++it) { + char aPath[PATH_MAX]; + if ( realpath(it->first, aPath) != NULL ) { + if ( strcmp(targetPath, aPath) == 0 ) { + BinderAndReExportFlag entry; + entry.binder = it->second; + entry.reExport = ( cmd->cmd() == LC_REEXPORT_DYLIB ); + fDependentDylibs.push_back(entry); + found = true; + fprintf(stderr, "update_dyld_shared_cache: warning mismatched install path in %s for %s\n", + this->getDylibID(), path); + break; + } + } + } + } + if ( ! found ) + throwf("in %s can't find dylib %s", this->getDylibID(), path); + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + // handle pre-10.5 re-exports + if ( (this->fHeader->flags() & MH_NO_REEXPORTED_DYLIBS) == 0 ) { + cmd = cmds; + // LC_SUB_LIBRARY means re-export one with matching leaf name + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd() ) { + case LC_SUB_LIBRARY: + const char* dylibBaseName = ((macho_sub_library_command

*)cmd)->sub_library(); + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + const char* dylibName = it->binder->getDylibID(); + const char* lastSlash = strrchr(dylibName, '/'); + const char* leafStart = &lastSlash[1]; + if ( lastSlash == NULL ) + leafStart = dylibName; + const char* firstDot = strchr(leafStart, '.'); + int len = strlen(leafStart); + if ( firstDot != NULL ) + len = firstDot - leafStart; + if ( strncmp(leafStart, dylibBaseName, len) == 0 ) + it->reExport = true; + } + break; + case LC_SUB_UMBRELLA: + const char* frameworkLeafName = ((macho_sub_umbrella_command

*)cmd)->sub_umbrella(); + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + const char* dylibName = it->binder->getDylibID(); + const char* lastSlash = strrchr(dylibName, '/'); + if ( (lastSlash != NULL) && (strcmp(&lastSlash[1], frameworkLeafName) == 0) ) + it->reExport = true; + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + // ask dependents if they re-export through me + const char* thisName = this->getDylibID(); + if ( thisName != NULL ) { + const char* thisLeafName = strrchr(thisName, '/'); + if ( thisLeafName != NULL ) + ++thisLeafName; + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + if ( ! it->reExport ) { + const char* parentUmbrellaName = it->binder->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + if ( strcmp(parentUmbrellaName, thisLeafName) == 0 ) + it->reExport = true; + } + } + } + } + } +} + +template +void Binder::bind() +{ + this->doSetUpDyldSection(); + this->doBindExternalRelocations(); + this->doBindIndirectSymbols(); + this->doSetPreboundUndefines(); +} + + +template +void Binder::doSetUpDyldSection() +{ + // find __DATA __dyld section + const macho_load_command

* const cmds = (macho_load_command

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

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

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

::CMD ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + if ( strcmp(seg->segname(), "__DATA") == 0 ) { + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)seg + sizeof(macho_segment_command

)); + const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (strcmp(sect->sectname(), "__dyld") == 0) && (sect->size() >= 2*sizeof(pint_t)) ) { + // set two values in __dyld section to point into dyld + pint_t* lazyBinder = this->mappedAddressForNewAddress(sect->addr()); + pint_t* dyldFuncLookup = this->mappedAddressForNewAddress(sect->addr()+sizeof(pint_t)); + A::P::setP(*lazyBinder, fDyldBaseAddress + 0x1000); + A::P::setP(*dyldFuncLookup, fDyldBaseAddress + 0x1008); + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void Binder::doSetPreboundUndefines() +{ + 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*)this->fHeader + sizeof(macho_header

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd()) { + case LC_SYMTAB: + { + const macho_symtab_command

* symtab = (macho_symtab_command

*)cmd; + symbolTable = (macho_nlist

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

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // walk all undefines and set their prebound n_value + 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()); + entry->set_n_value(pbaddr); + } + } +} + + +template +void Binder::doBindExternalRelocations() +{ + // get where reloc addresses start + // these address are always relative to first writable segment because they are in cache which always + // has writable segments far from read-only segments + pint_t firstWritableSegmentBaseAddress = 0; + const std::vector& segments = this->fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( seg.writable() ) { + firstWritableSegmentBaseAddress = seg.newAddress(); + break; + } + } + + // loop through all external relocation records and bind each + const macho_relocation_info

* const relocsStart = (macho_relocation_info

*)(&this->fLinkEditBase[fDynamicInfo->extreloff()]); + const macho_relocation_info

* const relocsEnd = &relocsStart[fDynamicInfo->nextrel()]; + for (const macho_relocation_info

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( reloc->r_length() != pointerRelocSize() ) + throw "bad external relocation length"; + if ( reloc->r_type() != pointerRelocType() ) + throw "unknown external relocation type"; + if ( reloc->r_pcrel() ) + throw "r_pcrel external relocaiton not supported"; + + const macho_nlist

* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum()]; + pint_t* location; + try { + location = mappedAddressForNewAddress(reloc->r_address() + firstWritableSegmentBaseAddress); + } + catch (const char* msg) { + throwf("%s processesing external relocation r_address 0x%08X", msg, reloc->r_address()); + } + pint_t addend = P::getP(*location); + if ( fOriginallyPrebound ) { + // in a prebound binary, 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. + addend -= undefinedSymbol->n_value(); + // To further complicate things, if this is defined symbol, then its n_value has already been adjust to the + // new base address, so we need to back off the slide too.. + if ( (undefinedSymbol->n_type() & N_TYPE) == N_SECT ) { + addend += this->getSlideForNewAddress(undefinedSymbol->n_value()); + } + } + pint_t symbolAddr = this->resolveUndefined(undefinedSymbol); + //fprintf(stderr, "external reloc: r_address=0x%08X, r_sym=%s, symAddr=0x%08llX, addend=0x%08llX in %s\n", + // reloc->r_address(), &fStrings[undefinedSymbol->n_strx()], (uint64_t)symbolAddr, (uint64_t)addend, this->getDylibID()); + P::setP(*location, symbolAddr + addend); + } +} + + +// most architectures use pure code, unmodifiable stubs +template +void Binder::bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value) +{ + // do nothing +} + +// x86 supports fast stubs +template <> +void Binder::bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value) +{ + // if the stub is not 5-bytes, it is an old slow stub + if ( elementSize == 5 ) { + uint32_t rel32 = value - (vmlocation + 5); + location[0] = 0xE9; // JMP rel32 + location[1] = rel32 & 0xFF; + location[2] = (rel32 >> 8) & 0xFF; + location[3] = (rel32 >> 16) & 0xFF; + location[4] = (rel32 >> 24) & 0xFF; + } +} + +template +void Binder::doBindIndirectSymbols() +{ + const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()]; + const macho_load_command

* const cmds = (macho_load_command

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

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

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

::CMD ) { + const macho_segment_command

* seg = (macho_segment_command

*)cmd; + const macho_section

* const sectionsStart = (macho_section

*)((uint8_t*)seg + sizeof(macho_segment_command

)); + const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + uint8_t elementSize = 0; + uint8_t sectionType = sect->flags() & SECTION_TYPE; + switch ( sectionType ) { + case S_SYMBOL_STUBS: + elementSize = sect->reserved2(); + break; + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LAZY_SYMBOL_POINTERS: + elementSize = sizeof(pint_t); + break; + } + if ( elementSize != 0 ) { + uint32_t elementCount = sect->size() / elementSize; + const uint32_t indirectTableOffset = sect->reserved1(); + uint8_t* location = NULL; + if ( sect->size() != 0 ) + location = (uint8_t*)this->mappedAddressForNewAddress(sect->addr()); + pint_t vmlocation = sect->addr(); + for (uint32_t j=0; j < elementCount; ++j, location += elementSize, vmlocation += elementSize) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + const macho_nlist

* undefinedSymbol = &fSymbolTable[symbolIndex]; + pint_t symbolAddr = this->resolveUndefined(undefinedSymbol); + switch ( sectionType ) { + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LAZY_SYMBOL_POINTERS: + P::setP(*((pint_t*)location), symbolAddr); + break; + case S_SYMBOL_STUBS: + this->bindStub(elementSize, location, vmlocation, symbolAddr); + break; + } + break; + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + + + +template +typename A::P::uint_t Binder::resolveUndefined(const macho_nlist

* undefinedSymbol) +{ + if ( (undefinedSymbol->n_type() & N_TYPE) == N_SECT ) { + if ( (undefinedSymbol->n_type() & N_PEXT) != 0 ) { + // is a multi-module private_extern internal reference that the linker did not optimize away + return undefinedSymbol->n_value(); + } + if ( (undefinedSymbol->n_desc() & N_WEAK_DEF) != 0 ) { + // is a weak definition, we should prebind to this one in the same linkage unit + return undefinedSymbol->n_value(); + } + } + const char* symbolName = &fStrings[undefinedSymbol->n_strx()]; + if ( (this->fHeader->flags() & MH_TWOLEVEL) == 0 ) { + // flat namespace binding + throw "flat namespace not supported"; + } + else { + uint8_t ordinal = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc()); + Binder* binder = NULL; + switch ( ordinal ) { + case EXECUTABLE_ORDINAL: + case DYNAMIC_LOOKUP_ORDINAL: + throw "magic ordineal not supported"; + case SELF_LIBRARY_ORDINAL: + binder = this; + break; + default: + if ( ordinal > fDependentDylibs.size() ) + 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(); + } +} + +template +const macho_nlist* Binder::findExportedSymbol(const char* name) +{ + //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; + + // 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; + } + } + return NULL; +} + + + +#endif // __MACHO_BINDER__ + + + + diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp new file mode 100644 index 0000000..dadec7b --- /dev/null +++ b/launch-cache/MachOFileAbstraction.hpp @@ -0,0 +1,738 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include + +// suport older versions of mach-o/loader.h +#ifndef LC_UUID +#define LC_UUID 0x1b +struct uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; +#endif + +#ifndef S_16BYTE_LITERALS + #define S_16BYTE_LITERALS 0xE +#endif + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + + + +// +// 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 +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

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

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

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

::CMD + }; +private: + macho_routines_content

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

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + + + + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp new file mode 100644 index 0000000..1a3017f --- /dev/null +++ b/launch-cache/MachOLayout.hpp @@ -0,0 +1,463 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __MACHO_LAYOUT__ +#define __MACHO_LAYOUT__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" + + +void throwf(const char* format, ...) __attribute__((format(printf, 1, 2))); + +__attribute__((noreturn)) +void throwf(const char* format, ...) +{ + va_list list; + char* p; + va_start(list, format); + vasprintf(&p, format, list); + va_end(list); + + const char* t = p; + throw t; +} + + +class MachOLayoutAbstraction +{ +public: + struct Segment + { + public: + Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, + uint32_t prot, const char* segName) : fAddress(addr), fSize(vmsize), + fFileOffset(offset), fFileSize(file_size), fPermissions(prot), + fNewAddress(0), fMappedAddress(NULL) { + strlcpy(fName, segName, 16); + } + + uint64_t address() const { return fAddress; } + uint64_t size() const { return fSize; } + uint64_t fileOffset() const { return fFileOffset; } + uint64_t fileSize() const { return fFileSize; } + uint32_t permissions() const { return fPermissions; } + bool readable() const { return fPermissions & VM_PROT_READ; } + bool writable() const { return fPermissions & VM_PROT_WRITE; } + bool executable() const { return fPermissions & VM_PROT_EXECUTE; } + const char* name() const { return fName; } + uint64_t newAddress() const { return fNewAddress; } + void* mappedAddress() const { return fMappedAddress; } + void setNewAddress(uint64_t addr) { fNewAddress = addr; } + void setMappedAddress(void* addr) { fMappedAddress = addr; } + void setSize(uint64_t new_size) { fSize = new_size; } + void setFileOffset(uint64_t new_off) { fFileOffset = new_off; } + void setFileSize(uint64_t new_size) { fFileSize = new_size; } + void setWritable(bool writable) { if (writable) fPermissions |= VM_PROT_WRITE; else fPermissions &= ~VM_PROT_WRITE; } + private: + uint64_t fAddress; + uint64_t fSize; + uint64_t fFileOffset; + uint64_t fFileSize; + uint64_t fNewAddress; + void* fMappedAddress; + uint32_t fPermissions; + char fName[16]; + }; + + struct Library + { + const char* name; + uint32_t currentVersion; + uint32_t compatibilityVersion; + }; + + + virtual cpu_type_t getArchitecture() const = 0; + virtual const char* getFilePath() const = 0; + virtual uint64_t getOffsetInUniversalFile() const = 0; + virtual uint32_t getFileType() const = 0; + virtual uint32_t getFlags() const = 0; + virtual Library getID() const = 0; + virtual bool isSplitSeg() const = 0; + virtual bool hasSplitSegInfo() const = 0; + virtual uint32_t getNameFileOffset() const = 0; + virtual time_t getLastModTime() const = 0; + virtual ino_t getInode() const = 0; + virtual std::vector& getSegments() = 0; + virtual const std::vector& getSegments() const = 0; + virtual const std::vector& getLibraries() const = 0; + virtual uint64_t getBaseAddress() const = 0; + virtual uint64_t getVMSize() const = 0; + virtual uint64_t getBaseExecutableAddress() const = 0; + virtual uint64_t getBaseWritableAddress() const = 0; + virtual uint64_t getBaseReadOnlyAddress() const = 0; + virtual uint64_t getExecutableVMSize() const = 0; + virtual uint64_t getWritableVMSize() const = 0; + virtual uint64_t getReadOnlyVMSize() const = 0; +}; + + + + +template +class MachOLayout : public MachOLayoutAbstraction +{ +public: + MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime); + virtual ~MachOLayout() {} + + virtual cpu_type_t getArchitecture() const; + virtual const char* getFilePath() const { return fPath; } + virtual uint64_t getOffsetInUniversalFile() const { return fOffset; } + virtual uint32_t getFileType() const { return fFileType; } + virtual uint32_t getFlags() const { return fFlags; } + virtual Library getID() const { return fDylibID; } + virtual bool isSplitSeg() const; + virtual bool hasSplitSegInfo() const { return fHasSplitSegInfo; } + virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; } + virtual time_t getLastModTime() const { return fMTime; } + virtual ino_t getInode() const { return fInode; } + virtual std::vector& getSegments() { return fSegments; } + virtual const std::vector& getSegments() const { return fSegments; } + virtual const std::vector& getLibraries() const { return fLibraries; } + virtual uint64_t getBaseAddress() const { return fLowSegment->address(); } + virtual uint64_t getVMSize() const { return fVMSize; } + virtual uint64_t getBaseExecutableAddress() const { return fLowExecutableSegment->address(); } + virtual uint64_t getBaseWritableAddress() const { return fLowWritableSegment->address(); } + virtual uint64_t getBaseReadOnlyAddress() const { return fLowReadOnlySegment->address(); } + virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; } + virtual uint64_t getWritableVMSize() const { return fVMWritablSize; } + virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; } + +private: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + const char* fPath; + uint64_t fOffset; + uint32_t fFileType; + uint32_t fFlags; + std::vector fSegments; + std::vector fLibraries; + const Segment* fLowSegment; + const Segment* fLowExecutableSegment; + const Segment* fLowWritableSegment; + const Segment* fLowReadOnlySegment; + Library fDylibID; + uint32_t fNameFileOffset; + time_t fMTime; + ino_t fInode; + uint64_t fVMSize; + uint64_t fVMExecutableSize; + uint64_t fVMWritablSize; + uint64_t fVMReadOnlySize; + bool fHasSplitSegInfo; +}; + + + +class UniversalMachOLayout +{ +public: + 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; } + +private: + struct CStringEquals { + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; + + static PathToNode fgLayoutCache; + const char* fPath; + std::vector fLayouts; +}; + +UniversalMachOLayout::PathToNode UniversalMachOLayout::fgLayoutCache; + + +const MachOLayoutAbstraction* UniversalMachOLayout::getArch(cpu_type_t arch) const +{ + for(std::vector::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) { + const MachOLayoutAbstraction* layout = *it; + if ( layout->getArchitecture() == arch ) + return layout; + } + return NULL; +} + + +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; + + // create UniversalMachOLayout + const UniversalMachOLayout* result = new UniversalMachOLayout(path, onlyArchs); + + // add it to cache + fgLayoutCache[result->fPath] = result; + + return result; +} + + +UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set* onlyArchs) + : fPath(strdup(path)) +{ + // map in whole file + int fd = ::open(path, O_RDONLY, 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", path, errno); + if ( stat_buf.st_size < 20 ) + throwf("file too small %s", path); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file %s, errno=%d", path, errno); + ::close(fd); + + try { + // if fat file, process each architecture + const fat_header* fh = (fat_header*)p; + const mach_header* mh = (mach_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + // Fat header is always big-endian + const struct fat_arch* 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); + try { + if ( (onlyArchs == NULL) || (onlyArchs->count(curArch) != 0) ) { + switch ( curArch ) { + 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)); + break; + case CPU_TYPE_I386: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + break; + case CPU_TYPE_X86_64: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime)); + break; + default: + throw "unknown file format"; + } + } + } + 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)); + } + 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)); + } + 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)); + } + else { + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "warning: %s for %s\n", msg, path); + } + } + } + catch (...) { + ::munmap(p, stat_buf.st_size); + throw; + } +} + + + +template +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) +{ + fDylibID.name = NULL; + fDylibID.currentVersion = 0; + fDylibID.compatibilityVersion = 0; + + const macho_header

* mh = (const macho_header

*)machHeader; + if ( mh->cputype() != getArchitecture() ) + throw "wrong architecture"; + switch ( mh->filetype() ) { + case MH_DYLIB: + case MH_BUNDLE: + case MH_EXECUTE: + case MH_DYLIB_STUB: + case MH_DYLINKER: + break; + default: + throw "file is not a mach-o final linked image"; + } + fFlags = mh->flags(); + fFileType = mh->filetype(); + + const macho_load_command

* const cmds = (macho_load_command

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

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

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

* dylib = (macho_dylib_command

*)cmd; + fDylibID.name = strdup(dylib->name()); + fDylibID.currentVersion = dylib->current_version(); + fDylibID.compatibilityVersion = dylib->compatibility_version(); + fNameFileOffset = dylib->name() - (char*)machHeader; + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + { + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + Library lib; + lib.name = strdup(dylib->name()); + lib.currentVersion = dylib->current_version(); + lib.compatibilityVersion = dylib->compatibility_version(); + fLibraries.push_back(lib); + } + break; + case LC_SEGMENT_SPLIT_INFO: + fHasSplitSegInfo = true; + break; + case macho_segment_command

::CMD: + { + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + fSegments.push_back(Segment(segCmd->vmaddr(), segCmd->vmsize(), segCmd->fileoff(), + segCmd->filesize(), segCmd->initprot(), segCmd->segname())); + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + fLowSegment = NULL; + fLowExecutableSegment = NULL; + fLowWritableSegment = NULL; + fLowReadOnlySegment = NULL; + fVMExecutableSize = 0; + fVMWritablSize = 0; + fVMReadOnlySize = 0; + fVMSize = 0; + const Segment* highSegment = NULL; + for(std::vector::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) { + const Segment& seg = *it; + if ( (fLowSegment == NULL) || (seg.address() < fLowSegment->address()) ) + fLowSegment = &seg; + if ( (highSegment == NULL) || (seg.address() > highSegment->address()) ) + highSegment = &seg; + if ( seg.executable() ) { + if ( (fLowExecutableSegment == NULL) || (seg.address() < fLowExecutableSegment->address()) ) + fLowExecutableSegment = &seg; + fVMExecutableSize += seg.size(); + } + else if ( seg.writable()) { + if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) ) + fLowWritableSegment = &seg; + fVMWritablSize += seg.size(); + } + else { + if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) ) + fLowReadOnlySegment = &seg; + fVMReadOnlySize += seg.size(); + } + } + if ( (highSegment != NULL) && (fLowSegment != NULL) ) + fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096); +} + +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 <> +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 +{ + return false; +} + + +#endif // __MACHO_LAYOUT__ + + + diff --git a/launch-cache/MachORebaser.hpp b/launch-cache/MachORebaser.hpp new file mode 100644 index 0000000..4cf81f3 --- /dev/null +++ b/launch-cache/MachORebaser.hpp @@ -0,0 +1,812 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __MACHO_REBASER__ +#define __MACHO_REBASER__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Architectures.hpp" +#include "MachOLayout.hpp" + + + +class AbstractRebaser +{ +public: + virtual cpu_type_t getArchitecture() const = 0; + virtual uint64_t getBaseAddress() const = 0; + virtual uint64_t getVMSize() const = 0; + virtual void rebase() = 0; +}; + + +template +class Rebaser : public AbstractRebaser +{ +public: + Rebaser(const MachOLayoutAbstraction&); + virtual ~Rebaser() {} + + virtual cpu_type_t getArchitecture() const; + virtual uint64_t getBaseAddress() const; + virtual uint64_t getVMSize() const; + virtual void rebase(); + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + + pint_t* mappedAddressForNewAddress(pint_t vmaddress); + pint_t getSlideForNewAddress(pint_t newAddress); + +private: + pint_t calculateRelocBase(); + void adjustLoadCommands(); + void adjustSymbolTable(); + void adjustDATA(); + void adjustCode(); + void adjustSegmentLoadCommand(macho_segment_command

* seg); + pint_t getSlideForVMAddress(pint_t vmaddress); + pint_t* mappedAddressForVMAddress(pint_t vmaddress); + pint_t* mappedAddressForRelocAddress(pint_t r_address); + void adjustRelocBaseAddresses(); + const uint8_t* doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta); + void doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta); + void doLocalRelocation(const macho_relocation_info

* reloc); + bool unequalSlides() const; + +protected: + const macho_header

* fHeader; + uint8_t* fLinkEditBase; // add file offset to this to get linkedit content + const MachOLayoutAbstraction& fLayout; +private: + pint_t fOrignalVMRelocBaseAddress; // add reloc address to this to get original address reloc referred to + bool fSplittingSegments; +}; + + + +template +Rebaser::Rebaser(const MachOLayoutAbstraction& layout) + : fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL), fSplittingSegments(false) +{ + fHeader = (const macho_header

*)fLayout.getSegments()[0].mappedAddress(); + switch ( fHeader->filetype() ) { + case MH_DYLIB: + case MH_BUNDLE: + break; + default: + throw "file is not a dylib or bundle"; + } + + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( strcmp(seg.name(), "__LINKEDIT") == 0 ) { + fLinkEditBase = (uint8_t*)seg.mappedAddress() - seg.fileOffset(); + break; + } + } + if ( fLinkEditBase == NULL ) + throw "no __LINKEDIT segment"; + + fOrignalVMRelocBaseAddress = 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 +bool Rebaser::unequalSlides() const +{ + const std::vector& segments = fLayout.getSegments(); + uint64_t slide = segments[0].newAddress() - segments[0].address(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.newAddress() - seg.address()) != slide ) + return true; + } + return false; +} + +template +uint64_t Rebaser::getBaseAddress() const +{ + return fLayout.getSegments()[0].address(); +} + +template +uint64_t Rebaser::getVMSize() const +{ + uint64_t highestVMAddress = 0; + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( seg.address() > highestVMAddress ) + highestVMAddress = seg.address(); + } + return (((highestVMAddress - getBaseAddress()) + 4095) & (-4096)); +} + + + +template +void Rebaser::rebase() +{ + // update writable segments that have internal pointers + this->adjustDATA(); + + // if splitting segments, update code-to-data references + this->adjustCode(); + + // change address on relocs now that segments are split + this->adjustRelocBaseAddresses(); + + // update load commands + this->adjustLoadCommands(); + + // update symbol table + this->adjustSymbolTable(); +} + +template <> +void Rebaser::adjustSegmentLoadCommand(macho_segment_command

* seg) +{ + // __IMPORT segments are not-writable in shared cache + if ( strcmp(seg->segname(), "__IMPORT") == 0 ) + seg->set_initprot(VM_PROT_READ|VM_PROT_EXECUTE); +} + +template +void Rebaser::adjustSegmentLoadCommand(macho_segment_command

* seg) +{ +} + + +template +void Rebaser::adjustLoadCommands() +{ + 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_ID_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear timestamp so that any prebound clients are invalidated + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(1); + } + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + if ( (fHeader->flags() & MH_PREBOUND) != 0 ) { + // clear expected timestamps so that this image will load with invalid prebinding + macho_dylib_command

* dylib = (macho_dylib_command

*)cmd; + dylib->set_timestamp(2); + } + break; + case macho_routines_command

::CMD: + // update -init command + { + struct macho_routines_command

* routines = (struct macho_routines_command

*)cmd; + routines->set_init_address(routines->init_address() + this->getSlideForVMAddress(routines->init_address())); + } + break; + case macho_segment_command

::CMD: + // update segment commands + { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + this->adjustSegmentLoadCommand(seg); + pint_t slide = this->getSlideForVMAddress(seg->vmaddr()); + seg->set_vmaddr(seg->vmaddr() + slide); + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + sect->set_addr(sect->addr() + slide); + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + + +template +typename A::P::uint_t Rebaser::getSlideForVMAddress(pint_t vmaddress) +{ + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.address() <= vmaddress) && (seg.size() != 0) && ((vmaddress < (seg.address()+seg.size())) || (seg.address() == vmaddress)) ) { + return seg.newAddress() - seg.address(); + } + } + throwf("vm address 0x%08llX not found", (uint64_t)vmaddress); +} + + +template +typename A::P::uint_t* Rebaser::mappedAddressForVMAddress(pint_t vmaddress) +{ + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.address() <= vmaddress) && (vmaddress < (seg.address()+seg.size())) ) { + return (pint_t*)((vmaddress - seg.address()) + (uint8_t*)seg.mappedAddress()); + } + } + throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddress); +} + +template +typename A::P::uint_t* Rebaser::mappedAddressForNewAddress(pint_t vmaddress) +{ + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.newAddress() <= vmaddress) && (vmaddress < (seg.newAddress()+seg.size())) ) { + return (pint_t*)((vmaddress - seg.newAddress()) + (uint8_t*)seg.mappedAddress()); + } + } + throwf("mappedAddressForNewAddress(0x%08llX) not found", (uint64_t)vmaddress); +} + +template +typename A::P::uint_t Rebaser::getSlideForNewAddress(pint_t newAddress) +{ + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( (seg.newAddress() <= newAddress) && (newAddress < (seg.newAddress()+seg.size())) ) { + return seg.newAddress() - seg.address(); + } + } + throwf("new address 0x%08llX not found", (uint64_t)newAddress); +} + +template +typename A::P::uint_t* Rebaser::mappedAddressForRelocAddress(pint_t r_address) +{ + return this->mappedAddressForVMAddress(r_address + fOrignalVMRelocBaseAddress); +} + + +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()); + } + + // 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) { + 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) { + 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::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta) +{ + //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX)\n", kind, address, codeToDataDelta, codeToImportDelta); + uint32_t* p; + uint32_t instruction; + uint32_t value; + uint64_t value64; + switch (kind) { + case 1: // 32-bit pointer + p = (uint32_t*)mappedAddressForVMAddress(address); + value = A::P::E::get32(*p); + value += codeToDataDelta; + A::P::E::set32(*p, value); + break; + case 2: // 64-bit pointer + p = (uint32_t*)mappedAddressForVMAddress(address); + value64 = A::P::E::get64(*(uint64_t*)p); + value64 += codeToDataDelta; + A::P::E::set64(*(uint64_t*)p, value64); + break; + case 3: // used only for ppc/ppc64, 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); + p = (uint32_t*)mappedAddressForVMAddress(address); + instruction = BigEndian::get32(*p); + uint16_t originalLo16 = instruction & 0x0000FFFF; + uint16_t delta64Ks = codeToDataDelta >> 16; + instruction = (instruction & 0xFFFF0000) | ((originalLo16+delta64Ks) & 0x0000FFFF); + BigEndian::set32(*p, instruction); + break; + case 4: // only used for i386, a reference to something in the IMPORT segment + p = (uint32_t*)mappedAddressForVMAddress(address); + value = A::P::E::get32(*p); + value += codeToImportDelta; + A::P::E::set32(*p, value); + break; + default: + throwf("invalid kind=%d in split seg info", kind); + } +} + +template +const uint8_t* Rebaser::doCodeUpdateForEachULEB128Address(const uint8_t* p, uint8_t kind, uint64_t orgBaseAddress, int64_t codeToDataDelta, int64_t codeToImportDelta) +{ + uint64_t address = 0; + uint64_t delta = 0; + uint32_t shift = 0; + bool more = true; + do { + uint8_t byte = *p++; + delta |= ((byte & 0x7F) << shift); + shift += 7; + if ( byte < 0x80 ) { + if ( delta != 0 ) { + address += delta; + doCodeUpdate(kind, address+orgBaseAddress, codeToDataDelta, codeToImportDelta); + delta = 0; + shift = 0; + } + else { + more = false; + } + } + } while (more); + return p; +} + +template +void Rebaser::adjustCode() +{ + if ( fSplittingSegments ) { + // get uleb128 compressed runs of code addresses to update + const uint8_t* infoStart = NULL; + const uint8_t* infoEnd = 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_SEGMENT_SPLIT_INFO: + { + const macho_linkedit_data_command

* segInfo = (macho_linkedit_data_command

*)cmd; + infoStart = &fLinkEditBase[segInfo->dataoff()]; + infoEnd = &infoStart[segInfo->datasize()]; + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + // calculate how much we need to slide writable segments + const uint64_t orgBaseAddress = this->getBaseAddress(); + int64_t codeToDataDelta = 0; + int64_t codeToImportDelta = 0; + const std::vector& segments = fLayout.getSegments(); + const MachOLayoutAbstraction::Segment& codeSeg = segments[0]; + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& dataSeg = *it; + if ( strcmp(dataSeg.name(), "__IMPORT") == 0 ) + codeToImportDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); + else if ( dataSeg.writable() ) + codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); + } + // decompress and call doCodeUpdate() on each address + for(const uint8_t* p = infoStart; *p != 0;) { + uint8_t kind = *p++; + p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta); + } + } +} + + +template +void Rebaser::adjustDATA() +{ + const macho_dysymtab_command

* dysymtab = 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_DYSYMTAB: + dysymtab = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + + // 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()]; + 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; + 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()]); + 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(); + uint32_t pointerCount = sect->size() / sizeof(pint_t); + pint_t* nonLazyPointerAddr = this->mappedAddressForVMAddress(sect->addr()); + for (uint32_t j=0; j < pointerCount; ++j, ++nonLazyPointerAddr) { + if ( E::get32(indirectTable[indirectTableOffset + j]) == INDIRECT_SYMBOL_LOCAL ) { + pint_t value = A::P::getP(*nonLazyPointerAddr); + P::setP(*nonLazyPointerAddr, value + this->getSlideForVMAddress(value)); + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +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()); + } + + // get amount to adjust reloc address + int32_t relocAddressAdjust = 0; + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( seg.writable() ) { + relocAddressAdjust = seg.address() - segments[0].address(); + break; + } + } + + // 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()]; + 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()]; + for (macho_relocation_info

* reloc=externRelocsStart; reloc < externRelocsEnd; ++reloc) { + reloc->set_r_address(reloc->r_address()-relocAddressAdjust); + } + } +} + +template <> +void Rebaser::adjustRelocBaseAddresses() +{ + // x86_64 already have reloc base of first writable segment +} + + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info* reloc) +{ + if ( reloc->r_type() == X86_64_RELOC_UNSIGNED ) { + pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address()); + pint_t value = P::getP(*addr); + P::setP(*addr, value + this->getSlideForVMAddress(value)); + } + else { + throw "invalid relocation type"; + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address()); + pint_t value = P::getP(*addr); + P::setP(*addr, value + this->getSlideForVMAddress(value)); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == PPC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + this->getSlideForVMAddress(sreloc->r_value()) ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template <> +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address()); + pint_t value = P::getP(*addr); + P::setP(*addr, value + this->getSlideForVMAddress(value)); + } + } + else { + macho_scattered_relocation_info

* sreloc = (macho_scattered_relocation_info

*)reloc; + if ( sreloc->r_type() == GENERIC_RELOC_PB_LA_PTR ) { + sreloc->set_r_value( sreloc->r_value() + this->getSlideForVMAddress(sreloc->r_value()) ); + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } + } +} + +template +void Rebaser::doLocalRelocation(const macho_relocation_info

* reloc) +{ + if ( (reloc->r_address() & R_SCATTERED) == 0 ) { + if ( reloc->r_type() == GENERIC_RELOC_VANILLA ) { + pint_t* addr = this->mappedAddressForRelocAddress(reloc->r_address()); + pint_t value = P::getP(*addr); + P::setP(*addr, value + this->getSlideForVMAddress(value)); + } + } + else { + throw "cannot rebase final linked image with scattered relocations"; + } +} + + +template +typename A::P::uint_t Rebaser::calculateRelocBase() +{ + const std::vector& segments = fLayout.getSegments(); + if ( fHeader->flags() & MH_SPLIT_SEGS ) { + // reloc addresses are from the start of the first writable segment + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( seg.writable() ) { + // found first writable segment + return seg.address(); + } + } + throw "no writable segment"; + } + else { + // reloc addresses are from the start of the mapped file (base address) + return segments[0].address(); + } +} + +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() +{ + // reloc addresses are always based from the start of the first writable segment + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( seg.writable() ) { + // found first writable segment + return seg.address(); + } + } + throw "no writable segment"; +} + + +#if 0 +class MultiArchRebaser +{ +public: + MultiArchRebaser::MultiArchRebaser(const char* path, bool writable=false) + : fMappingAddress(0), fFileSize(0) + { + // map in whole file + int fd = ::open(path, (writable ? O_RDWR : O_RDONLY), 0); + if ( fd == -1 ) + throwf("can't open file, errno=%d", errno); + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) + throwf("can't stat open file %s, errno=%d", path, errno); + if ( stat_buf.st_size < 20 ) + throwf("file too small %s", path); + const int prot = writable ? (PROT_READ | PROT_WRITE) : PROT_READ; + const int flags = writable ? (MAP_FILE | MAP_SHARED) : (MAP_FILE | MAP_PRIVATE); + uint8_t* p = (uint8_t*)::mmap(NULL, stat_buf.st_size, prot, flags, fd, 0); + if ( p == (uint8_t*)(-1) ) + throwf("can't map file %s, errno=%d", path, errno); + ::close(fd); + + // if fat file, process each architecture + const fat_header* fh = (fat_header*)p; + const mach_header* mh = (mach_header*)p; + if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + // Fat header is always big-endian + const struct fat_arch* 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); + try { + switch ( OSSwapBigToHostInt32(archs[i].cputype) ) { + 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; + default: + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + } + else { + try { + 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 { + throw "unknown file format"; + } + } + catch (const char* msg) { + fprintf(stderr, "rebase warning: %s for %s\n", msg, path); + } + } + + fMappingAddress = p; + fFileSize = stat_buf.st_size; + } + + + ~MultiArchRebaser() {::munmap(fMappingAddress, fFileSize); } + + const std::vector& getArchs() const { return fRebasers; } + void commit() { ::msync(fMappingAddress, fFileSize, MS_ASYNC); } + +private: + std::vector fRebasers; + void* fMappingAddress; + uint64_t fFileSize; +}; +#endif + + +#endif // __MACHO_REBASER__ + + + + diff --git a/launch-cache/com.apple.dyld.plist b/launch-cache/com.apple.dyld.plist new file mode 100644 index 0000000..7481c33 --- /dev/null +++ b/launch-cache/com.apple.dyld.plist @@ -0,0 +1,26 @@ + + + + + Label + com.apple.dyld + Program + /usr/bin/update_dyld_shared_cache + MachServices + + com.apple.dyld + + + Nice + 10 + LowPriorityIO + + EnvironmentVariables + + DYLD_NO_FIX_PREBINDING + 1 + + ThrottleInterval + 60 + + diff --git a/launch-cache/dyld_cache_format.h b/launch-cache/dyld_cache_format.h new file mode 100644 index 0000000..c13ff7f --- /dev/null +++ b/launch-cache/dyld_cache_format.h @@ -0,0 +1,58 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_FORMAT__ +#define __DYLD_CACHE_FORMAT__ + +#include +#include +#include + + + struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 ppc" + uint32_t mappingOffset; // file offset to first shared_file_mapping_np + uint32_t mappingCount; // number of shared_file_mapping_np entries + uint32_t imagesOffset; // file offset to first dyld_cache_image_info + uint32_t imagesCount; // number of dyld_cache_image_info entries + uint64_t dyldBaseAddress; // base address of dyld when cache was built +}; + +struct dyld_cache_image_info +{ + uint64_t address; + uint64_t modTime; + uint64_t inode; + uint32_t pathFileOffset; + uint32_t pad; +}; + +#define DYLD_SHARED_CACHE_DIR "/var/db/dyld/" +#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" + + + +#endif // __DYLD_CACHE_FORMAT__ + + diff --git a/launch-cache/dyld_shared_cache.defs b/launch-cache/dyld_shared_cache.defs new file mode 100644 index 0000000..4914364 --- /dev/null +++ b/launch-cache/dyld_shared_cache.defs @@ -0,0 +1,19 @@ +#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 new file mode 100644 index 0000000..4ec8cf8 --- /dev/null +++ b/launch-cache/update_dyld_shared_cache.cpp @@ -0,0 +1,1845 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_cache_format.h" + +#include +#include +#include +#include + +#include "Architectures.hpp" +#include "MachOLayout.hpp" +#include "MachORebaser.hpp" +#include "MachOBinder.hpp" +#include "CacheFileAbstraction.hpp" + +extern "C" { + #include "dyld_shared_cache_server.h" +} + + +static bool verbose = false; +static std::vector warnings; + + +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); + + cpu_type_t getArch() { return fArch; } + std::set& getSharedDylibs() { return fSharedDylibs; } + +private: + + class DependencyNode + { + public: + DependencyNode(ArchGraph*, const char* path, const MachOLayoutAbstraction* layout); + void loadDependencies(const MachOLayoutAbstraction*); + void markNeededByRoot(DependencyNode*); + const char* getPath() const { return fPath; } + const MachOLayoutAbstraction* getLayout() const { return fLayout; } + size_t useCount() const { return fRootsDependentOnThis.size(); } + bool allDependentsFound() const { return !fDependentMissing; } + private: + ArchGraph* fGraph; + const char* fPath; + const MachOLayoutAbstraction* fLayout; + bool fDependenciesLoaded; + bool fDependentMissing; + std::set fDependsOn; + std::set fRootsDependentOnThis; + }; + + struct CStringEquals { + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } + }; + typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; + + + ArchGraph(cpu_type_t arch) : fArch(arch) {} + static void addRootForArch(const char* path, const MachOLayoutAbstraction*); + 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 std::map fgPerArchGraph; + static const char* fgFileSystemRoot; + + cpu_type_t fArch; + std::set fRoots; + PathToNode fNodes; + std::set fSharedDylibs; // use set to avoid duplicates when installname!=realpath +}; +std::map ArchGraph::fgPerArchGraph; +const char* ArchGraph::fgFileSystemRoot = ""; + +void ArchGraph::addArch(cpu_type_t arch) +{ + //fprintf(stderr, "adding arch 0x%08X\n", arch); + fgPerArchGraph[arch] = new ArchGraph(arch); +} + +void ArchGraph::addRoot(const char* vpath, const std::set& archs) +{ + char completePath[strlen(fgFileSystemRoot)+strlen(vpath)+2]; + const char* path; + if ( strlen(fgFileSystemRoot) == 0 ) { + path = vpath; + } + else { + strcpy(completePath, fgFileSystemRoot); + strcat(completePath, vpath); // assumes vpath starts with '/' + 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); + } + // 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) +{ + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: adding root: %s\n", path); + DependencyNode* node = this->getNode(path); + fRoots.insert(node); + const MachOLayoutAbstraction* mainExecutableLayout = NULL; + if ( layout->getFileType() == MH_EXECUTE ) + mainExecutableLayout = layout; + node->loadDependencies(mainExecutableLayout); + node->markNeededByRoot(node); + if ( layout->getFileType() == MH_DYLIB ) + node->markNeededByRoot(NULL); +} + +// a virtual path does not have the fgFileSystemRoot prefix +ArchGraph::DependencyNode* ArchGraph::getNodeForVirtualPath(const char* vpath) +{ + if ( fgFileSystemRoot == NULL ) { + return this->getNode(vpath); + } + else { + char completePath[strlen(fgFileSystemRoot)+strlen(vpath)+2]; + strcpy(completePath, fgFileSystemRoot); + strcat(completePath, vpath); // assumes vpath starts with '/' + return this->getNode(completePath); + } +} + +ArchGraph::DependencyNode* ArchGraph::getNode(const char* path) +{ + // look up supplied path to see if node already exists + PathToNode::iterator pos = fNodes.find(path); + if ( pos != fNodes.end() ) + return pos->second; + + // get real path + char realPath[MAXPATHLEN]; + if ( realpath(path, realPath) == NULL ) + throwf("realpath() failed on %s\n", path); + + // look up real path to see if node already exists + pos = fNodes.find(realPath); + if ( pos != fNodes.end() ) + 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)); + if ( node->getLayout() == NULL ) { + throwf("%s is missing arch %s", realPath, archName(fArch)); + } + // add realpath to node map + fNodes[node->getPath()] = node; + // if install name is not real path, add install name to node map + if ( (node->getLayout()->getFileType() == MH_DYLIB) && (strcmp(realPath, node->getLayout()->getID().name) != 0) ) { + //fprintf(stderr, "adding node alias 0x%08X %s for %s\n", fArch, node->getLayout()->getID().name, realPath); + fNodes[node->getLayout()->getID().name] = node; + } + return node; +} + + +void ArchGraph::DependencyNode::loadDependencies(const MachOLayoutAbstraction* mainExecutableLayout) +{ + if ( !fDependenciesLoaded ) { + fDependenciesLoaded = true; + // add dependencies + const std::vector& dependsOn = fLayout->getLibraries(); + for(std::vector::const_iterator it = dependsOn.begin(); it != dependsOn.end(); ++it) { + try { + const char* dependentPath = it->name; + if ( strncmp(dependentPath, "@executable_path/", 17) == 0 ) { + if ( mainExecutableLayout == NULL ) + throw "@executable_path without main executable"; + // expand @executable_path path prefix + const char* executablePath = mainExecutableLayout->getFilePath(); + char newPath[strlen(executablePath) + strlen(dependentPath)+2]; + strcpy(newPath, executablePath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &dependentPath[17]); + else + strcpy(newPath, &dependentPath[17]); + dependentPath = strdup(newPath); + } + else if ( strncmp(dependentPath, "@loader_path/", 13) == 0 ) { + // expand @loader_path path prefix + char newPath[strlen(fPath) + strlen(dependentPath)+2]; + strcpy(newPath, fPath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &dependentPath[13]); + else + strcpy(newPath, &dependentPath[13]); + dependentPath = strdup(newPath); + } + else if ( strncmp(dependentPath, "@rpath/", 7) == 0 ) { + throw "@rpath not supported in dyld shared cache"; + } + fDependsOn.insert(fGraph->getNodeForVirtualPath(dependentPath)); + } + catch (const char* msg) { + fprintf(stderr, "warning, could not bind %s because %s\n", fPath, msg); + fDependentMissing = true; + } + } + // recurse + for(std::set::iterator it = fDependsOn.begin(); it != fDependsOn.end(); ++it) { + (*it)->loadDependencies(mainExecutableLayout); + } + } +} + +void ArchGraph::DependencyNode::markNeededByRoot(ArchGraph::DependencyNode* rootNode) +{ + if ( fRootsDependentOnThis.count(rootNode) == 0 ) { + fRootsDependentOnThis.insert(rootNode); + for(std::set::iterator it = fDependsOn.begin(); it != fDependsOn.end(); ++it) { + (*it)->markNeededByRoot(rootNode); + } + } +} + + +ArchGraph::DependencyNode::DependencyNode(ArchGraph* graph, const char* path, const MachOLayoutAbstraction* layout) + : fGraph(graph), fPath(strdup(path)), fLayout(layout), fDependenciesLoaded(false), fDependentMissing(false) +{ + //fprintf(stderr, "new DependencyNode(0x%08X, %s)\n", graph->fArch, path); +} + +void ArchGraph::findSharedDylibs(cpu_type_t arch) +{ + const PathToNode& nodes = fgPerArchGraph[arch]->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()); + //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::map shareableMap; + for (std::set::iterator lit = possibleLibs.begin(); lit != possibleLibs.end(); ++lit) { + if ( canBeShared(*lit, arch, possibleLibs, shareableMap) ) + sharedLibs.insert(*lit); + } +} + +const char* ArchGraph::archName(cpu_type_t arch) +{ + switch ( 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"; + default: + return "unknown"; + } +} + +bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, cpu_type_t arch, const std::set& possibleLibs, std::map& shareableMap) +{ + // check map which is a cache of results + std::map::iterator mapPos = shareableMap.find(layout); + if ( mapPos != shareableMap.end() ) { + return mapPos->second; + } + // see if possible + if ( possibleLibs.count(layout) == 0 ) { + shareableMap[layout] = false; + char* msg; + if ( ! layout->hasSplitSegInfo() ) + asprintf(&msg, "can't put %s in shared cache because it was not built for 10.5", 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); + return false; + } + // look recursively + shareableMap[layout] = true; // mark this shareable early in case of circular references + const PathToNode& nodes = fgPerArchGraph[arch]->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() ) { + 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); + return false; + } + else { + if ( ! canBeShared(pos->second->getLayout(), arch, 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); + return false; + } + } + } + return true; +} + + +template +class SharedCache +{ +public: + SharedCache(ArchGraph* graph, bool alphaSort, uint64_t dyldBaseAddress); + bool update(const char* rootPath, const char* cacheDir, bool force, bool optimize, int archIndex, int archCount); + static const char* filename(bool optimized); + +private: + typedef typename A::P::E E; + + bool notUpToDate(const char* cachePath); + bool notUpToDate(const void* cache); + uint8_t* optimizeLINKEDIT(); + + static void getSharedCacheBasAddresses(cpu_type_t arch, uint64_t* baseReadOnly, uint64_t* baseWritable); + static cpu_type_t arch(); + static const char* archName(); + static uint64_t sharedRegionReadOnlyStartAddress(); + static uint64_t sharedRegionWritableStartAddress(); + static uint64_t sharedRegionReadOnlySize(); + static uint64_t sharedRegionWritableSize(); + static uint64_t getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide); + + + void assignNewBaseAddresses(); + uint64_t cacheFileOffsetForAddress(uint64_t addr); + + struct LayoutInfo { + const MachOLayoutAbstraction* layout; + dyld_cache_image_info info; + }; + + struct ByNameSorter { + bool operator()(const LayoutInfo& left, const LayoutInfo& right) + { 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(); + } + bool operator()(const LayoutInfo& left, const LayoutInfo& right) { + return (fMap[left.layout] < fMap[right.layout]); + } + private: + std::map fMap; + }; + + + ArchGraph* fArchGraph; + std::vector fDylibs; + std::vector fMappings; + uint32_t fHeaderSize; + uint8_t* fMappedCacheFile; + uint64_t fDyldBaseAddress; + uint64_t fLinkEditsTotalUnoptimizedSize; + uint64_t fLinkEditsStartAddress; + MachOLayoutAbstraction::Segment* fFirstLinkEditSegment; +}; + + + + +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 <> 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::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::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::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 <> 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::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 +SharedCache::SharedCache(ArchGraph* graph, bool alphaSort, uint64_t dyldBaseAddress) + : fArchGraph(graph), fDyldBaseAddress(dyldBaseAddress) +{ + if ( fArchGraph->getArch() != arch() ) + throw "wrong architecture"; + + // build vector of all shared dylibs + std::set& dylibs = fArchGraph->getSharedDylibs(); + for(std::set::iterator it = dylibs.begin(); it != dylibs.end(); ++it) { + const MachOLayoutAbstraction* lib = *it; + LayoutInfo temp; + temp.layout = lib; + temp.info.address = 0; + temp.info.modTime = lib->getLastModTime(); + temp.info.inode = lib->getInode(); + temp.info.pathFileOffset = lib->getNameFileOffset(); + fDylibs.push_back(temp); + } + + // sort shared dylibs + if ( alphaSort ) + std::sort(fDylibs.begin(), fDylibs.end(), ByNameSorter()); + else + std::sort(fDylibs.begin(), fDylibs.end(), RandomSorter(fDylibs)); + + + // assign segments in each dylib a new address + this->assignNewBaseAddresses(); + + // calculate cache file header size + fHeaderSize = pageAlign(sizeof(dyld_cache_header) + + fMappings.size()*sizeof(shared_file_mapping_np) + + fDylibs.size()*sizeof(dyld_cache_image_info) ); + //+ fDependencyPool.size()*sizeof(uint16_t)); + + if ( fHeaderSize > 0x3000 ) + throwf("header size miscalculation 0x%08X", fHeaderSize); +} + + +template +uint64_t SharedCache::getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide) +{ + return 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) + 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) + return (((executableSlide & 0x000000000000F000ULL) - ((proposedNewAddress - originalAddress) & 0x000000000000F000ULL)) & 0x000000000000F000ULL) + proposedNewAddress; +} + + +template +void SharedCache::assignNewBaseAddresses() +{ + // first layout TEXT and DATA for split-seg (or can be split-seg) dylibs + uint64_t currentExecuteAddress = sharedRegionReadOnlyStartAddress() + 0x3000; + uint64_t currentWritableAddress = sharedRegionWritableStartAddress(); + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); + MachOLayoutAbstraction::Segment* executableSegment = NULL; + for (int i=0; i < segs.size(); ++i) { + MachOLayoutAbstraction::Segment& seg = segs[i]; + if ( seg.writable() ) { + if ( seg.executable() && it->layout->hasSplitSegInfo() ) { + // skip __IMPORT segments in this pass + } + else { + // __DATA segment + // for ppc, writable segments have to move in 64K increments + if ( it->layout->hasSplitSegInfo() ) { + if ( executableSegment == NULL ) + throwf("first segment in dylib is not executable for %s", it->layout->getID().name); + seg.setNewAddress(getWritableSegmentNewAddress(currentWritableAddress, seg.address(), executableSegment->newAddress() - executableSegment->address())); + } + else + seg.setNewAddress(currentWritableAddress); + currentWritableAddress = pageAlign(seg.newAddress() + seg.size()); + } + } + else { + if ( seg.executable() ) { + // __TEXT segment + if ( it->info.address == 0 ) + it->info.address = currentExecuteAddress; + executableSegment = &seg; + seg.setNewAddress(currentExecuteAddress); + currentExecuteAddress += pageAlign(seg.size()); + } + 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()); + } + } + } + } + } + + // append all read-only (but not LINKEDIT) segments at end of all TEXT segments + // append all IMPORT segments at end of all DATA segments rounded to next 2MB + uint64_t currentReadOnlyAddress = currentExecuteAddress; + uint64_t startWritableExecutableAddress = (currentWritableAddress + 0x200000 - 1) & (-0x200000); + uint64_t currentWritableExecutableAddress = startWritableExecutableAddress; + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); + for(int i=0; i < segs.size(); ++i) { + MachOLayoutAbstraction::Segment& seg = segs[i]; + if ( !seg.writable() && !seg.executable() && (strcmp(seg.name(), "__LINKEDIT") != 0) ) { + // allocate non-executable,read-only segments from end of read only shared region + seg.setNewAddress(currentReadOnlyAddress); + currentReadOnlyAddress += pageAlign(seg.size()); + } + 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()); + } + } + } + + // append all LINKEDIT segments at end of all read-only segments + fLinkEditsStartAddress = currentReadOnlyAddress; + fFirstLinkEditSegment = NULL; + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); + for(int i=0; i < segs.size(); ++i) { + MachOLayoutAbstraction::Segment& seg = segs[i]; + if ( !seg.writable() && !seg.executable() && (strcmp(seg.name(), "__LINKEDIT") == 0) ) { + if ( fFirstLinkEditSegment == NULL ) + fFirstLinkEditSegment = &seg; + // allocate non-executable,read-only segments from end of read only shared region + seg.setNewAddress(currentReadOnlyAddress); + currentReadOnlyAddress += pageAlign(seg.size()); + } + } + } + fLinkEditsTotalUnoptimizedSize = (currentReadOnlyAddress - fLinkEditsStartAddress + 4095) & (-4096); + + + // populate large mappings + uint64_t cacheFileOffset = 0; + if ( currentExecuteAddress > sharedRegionReadOnlyStartAddress() + 0x3000 ) { + shared_file_mapping_np executeMapping; + executeMapping.sfm_address = sharedRegionReadOnlyStartAddress(); + executeMapping.sfm_size = currentExecuteAddress - sharedRegionReadOnlyStartAddress(); + executeMapping.sfm_file_offset = cacheFileOffset; + executeMapping.sfm_max_prot = VM_PROT_READ | VM_PROT_EXECUTE; + executeMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_EXECUTE; + fMappings.push_back(executeMapping); + cacheFileOffset += executeMapping.sfm_size; + + shared_file_mapping_np writableMapping; + writableMapping.sfm_address = sharedRegionWritableStartAddress(); + writableMapping.sfm_size = currentWritableAddress - sharedRegionWritableStartAddress(); + writableMapping.sfm_file_offset = cacheFileOffset; + writableMapping.sfm_max_prot = VM_PROT_READ | VM_PROT_WRITE; + writableMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_WRITE; + fMappings.push_back(writableMapping); + cacheFileOffset += writableMapping.sfm_size; + + if ( currentWritableExecutableAddress > startWritableExecutableAddress ) { + shared_file_mapping_np writableExecutableMapping; + writableExecutableMapping.sfm_address = startWritableExecutableAddress; + 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; + fMappings.push_back(writableExecutableMapping); + cacheFileOffset += writableExecutableMapping.sfm_size; + } + + // make read-only (contains LINKEDIT segments) last, so it can be cut back when optimized + shared_file_mapping_np readOnlyMapping; + readOnlyMapping.sfm_address = currentExecuteAddress; + readOnlyMapping.sfm_size = currentReadOnlyAddress - currentExecuteAddress; + readOnlyMapping.sfm_file_offset = cacheFileOffset; + readOnlyMapping.sfm_max_prot = VM_PROT_READ; + readOnlyMapping.sfm_init_prot = VM_PROT_READ; + fMappings.push_back(readOnlyMapping); + cacheFileOffset += readOnlyMapping.sfm_size; + } + else { + // empty cache + shared_file_mapping_np cacheHeaderMapping; + cacheHeaderMapping.sfm_address = sharedRegionWritableStartAddress(); + cacheHeaderMapping.sfm_size = 0x3000; + cacheHeaderMapping.sfm_file_offset = cacheFileOffset; + cacheHeaderMapping.sfm_max_prot = VM_PROT_READ; + cacheHeaderMapping.sfm_init_prot = VM_PROT_READ; + fMappings.push_back(cacheHeaderMapping); + cacheFileOffset += cacheHeaderMapping.sfm_size; + } +} + + +template +uint64_t SharedCache::cacheFileOffsetForAddress(uint64_t addr) +{ + for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { + if ( (it->sfm_address <= addr) && (addr < it->sfm_address+it->sfm_size) ) + return it->sfm_file_offset + addr - it->sfm_address; + } + throwf("address 0x%0llX is not in cache", addr); +} + + +template +bool SharedCache::notUpToDate(const void* cache) +{ + dyldCacheHeader* header = (dyldCacheHeader*)cache; + // not valid if header signature is wrong + char temp[16]; + strcpy(temp, "dyld_v1 "); + strcpy(&temp[15-strlen(archName())], archName()); + if ( strcmp(header->magic(), temp) != 0 ) + return true; + // not valid if count of images does not match current images needed + if ( header->imagesCount() != fDylibs.size() ) + return true; + // verify every dylib in constructed graph is in existing cache with same inode and modTime + 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) ) { + 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; + } + } + return false; +} + + +template +bool SharedCache::notUpToDate(const char* cachePath) +{ + // mmap existing cache file + int fd = ::open(cachePath, O_RDONLY); + if ( fd == -1 ) + return true; + struct stat stat_buf; + ::fstat(fd, &stat_buf); + uint8_t* mappingAddr = (uint8_t*)mmap(NULL, stat_buf.st_size, PROT_READ , MAP_FILE | MAP_PRIVATE, fd, 0); + ::close(fd); + if ( mappingAddr == (uint8_t*)(-1) ) + return true; + + // 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); + + return result; +} + +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + +class StringPool +{ +public: + StringPool(); + const char* getBuffer(); + uint32_t size(); + uint32_t add(const char* str); + uint32_t addUnique(const char* str); + const char* stringAtIndex(uint32_t) const; +private: + typedef __gnu_cxx::hash_map, CStringEquals> StringToOffset; + + char* fBuffer; + uint32_t fBufferAllocated; + uint32_t fBufferUsed; + StringToOffset fUniqueStrings; +}; + + +StringPool::StringPool() + : fBufferUsed(0), fBufferAllocated(4*1024*1024) +{ + fBuffer = (char*)malloc(fBufferAllocated); +} + +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); + } + strcpy(&fBuffer[fBufferUsed], str); + uint32_t result = fBufferUsed; + fUniqueStrings[&fBuffer[fBufferUsed]] = result; + fBufferUsed += len+1; + return result; +} + +uint32_t StringPool::addUnique(const char* str) +{ + StringToOffset::iterator pos = fUniqueStrings.find(str); + if ( pos != fUniqueStrings.end() ) + return pos->second; + else { + //fprintf(stderr, "StringPool::addUnique() new string: %s\n", str); + return this->add(str); + } +} + +uint32_t StringPool::size() +{ + return fBufferUsed; +} + +const char* StringPool::getBuffer() +{ + return fBuffer; +} + +const char* StringPool::stringAtIndex(uint32_t index) const +{ + return &fBuffer[index]; +} + + +template +class LinkEditOptimizer +{ +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); + + +protected: + typedef typename A::P P; + typedef typename A::P::E E; + typedef typename A::P::uint_t pint_t; + +private: + + const macho_header

* fHeader; + uint8_t* fNewLinkEditStart; + uint8_t* fLinkEditBase; + const MachOLayoutAbstraction& fLayout; + macho_dysymtab_command

* fDynamicSymbolTable; + macho_symtab_command

* fSymbolTableLoadCommand; + const macho_nlist

* fSymbolTable; + const char* fStrings; + StringPool& fNewStringPool; + std::map fOldToNewSymbolIndexes; + uint32_t fLocalSymbolsStartIndexInNewLinkEdit; + uint32_t fLocalSymbolsCountInNewLinkEdit; + uint32_t fExportedSymbolsStartIndexInNewLinkEdit; + uint32_t fExportedSymbolsCountInNewLinkEdit; + uint32_t fImportSymbolsStartIndexInNewLinkEdit; + 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), + fDynamicSymbolTable(NULL), fSymbolTableLoadCommand(NULL), fSymbolTable(NULL), fStrings(NULL), fNewStringPool(stringPool), + fLocalSymbolsStartIndexInNewLinkEdit(0), fLocalSymbolsCountInNewLinkEdit(0), + fExportedSymbolsStartIndexInNewLinkEdit(0), fExportedSymbolsCountInNewLinkEdit(0), + fImportSymbolsStartIndexInNewLinkEdit(0), fImportedSymbolsCountInNewLinkEdit(0), + fExternalRelocationsOffsetIntoNewLinkEdit(0), fIndirectSymbolTableOffsetInfoNewLinkEdit(0) + +{ + fHeader = (const macho_header

*)fLayout.getSegments()[0].mappedAddress(); + + const std::vector& segments = fLayout.getSegments(); + for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { + const MachOLayoutAbstraction::Segment& seg = *it; + if ( strcmp(seg.name(), "__LINKEDIT") == 0 ) + fLinkEditBase = (uint8_t*)seg.mappedAddress() - seg.fileOffset(); + } + if ( fLinkEditBase == NULL ) + throw "no __LINKEDIT segment"; + + 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: + { + fSymbolTableLoadCommand = (macho_symtab_command

*)cmd; + fSymbolTable = (macho_nlist

*)(&fLinkEditBase[fSymbolTableLoadCommand->symoff()]); + fStrings = (char*)&fLinkEditBase[fSymbolTableLoadCommand->stroff()]; + } + break; + case LC_DYSYMTAB: + fDynamicSymbolTable = (macho_dysymtab_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + if ( fSymbolTable == NULL ) + throw "no LC_SYMTAB"; + if ( fDynamicSymbolTable == NULL ) + throw "no LC_DYSYMTAB"; + +} + + +template +class SymbolSorter +{ +public: + typedef typename A::P P; + SymbolSorter(const StringPool& pool) : fStringPool(pool) {} + bool operator()(const macho_nlist

& left, const macho_nlist

& right) { + return (strcmp(fStringPool.stringAtIndex(left.n_strx()) , fStringPool.stringAtIndex(right.n_strx())) < 0); + } + +private: + const StringPool& fStringPool; +}; + + +template +void LinkEditOptimizer::makeDummyLocalSymbol(uint32_t& symbolIndex, uint8_t* storage, StringPool& pool) +{ + 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; +} + +template +void LinkEditOptimizer::copyLocalSymbols() +{ + 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; + } +} + + +template +void LinkEditOptimizer::copyExportedSymbols(uint32_t& symbolIndex) +{ + fExportedSymbolsStartIndexInNewLinkEdit = symbolIndex; + 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]; + *newSymbolEntry = *entry; + newSymbolEntry->set_n_strx(fNewStringPool.add(&fStrings[entry->n_strx()])); + fOldToNewSymbolIndexes[oldIndex] = symbolIndex; + ++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]; + 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())); +} + + +template +void LinkEditOptimizer::copyImportedSymbols(uint32_t& symbolIndex) +{ + fImportSymbolsStartIndexInNewLinkEdit = symbolIndex; + 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]; + *newSymbolEntry = *entry; + newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()])); + fOldToNewSymbolIndexes[oldIndex] = symbolIndex; + ++symbolIndex; + } + } + fImportedSymbolsCountInNewLinkEdit = symbolIndex - fImportSymbolsStartIndexInNewLinkEdit; + //fprintf(stderr, "%u imports starting at %u for %s\n", fImportedSymbolsCountInNewLinkEdit, fImportSymbolsStartIndexInNewLinkEdit, fLayout.getFilePath()); + //macho_nlist

* newSymbolsStart = &((macho_nlist

*)fNewLinkEditStart)[fImportSymbolsStartIndexInNewLinkEdit]; + //macho_nlist

* newSymbolsEnd = &((macho_nlist

*)fNewLinkEditStart)[fImportSymbolsStartIndexInNewLinkEdit+fImportedSymbolsCountInNewLinkEdit]; + //for (macho_nlist

* entry = newSymbolsStart; entry < newSymbolsEnd; ++entry) + // fprintf(stderr, "\t%u\t%s\n", (entry-newSymbolsStart)+fImportSymbolsStartIndexInNewLinkEdit, fNewStringPool.stringAtIndex(entry->n_strx())); +} + + +template +void LinkEditOptimizer::copyExternalRelocations(uint32_t& offset) +{ + fExternalRelocationsOffsetIntoNewLinkEdit = offset; + const macho_relocation_info

* const relocsStart = (macho_relocation_info

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

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

* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + macho_relocation_info

* newReloc = (macho_relocation_info

*)(&fNewLinkEditStart[offset]); + *newReloc = *reloc; + uint32_t newSymbolIndex = fOldToNewSymbolIndexes[reloc->r_symbolnum()]; + //fprintf(stderr, "copyExternalRelocations() old=%d, new=%u name=%s in %s\n", reloc->r_symbolnum(), newSymbolIndex, + // &fStrings[fSymbolTable[reloc->r_symbolnum()].n_strx()], fLayout.getFilePath()); + newReloc->set_r_symbolnum(newSymbolIndex); + offset += sizeof(macho_relocation_info

); + } +} + +template +void LinkEditOptimizer::copyIndirectSymbolTable(uint32_t& offset) +{ + fIndirectSymbolTableOffsetInfoNewLinkEdit = offset; + const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicSymbolTable->indirectsymoff()]; + uint32_t* newIndirectTable = (uint32_t*)&fNewLinkEditStart[offset]; + for (int i=0; i < fDynamicSymbolTable->nindirectsyms(); ++i) { + uint32_t oldSymbolIndex = E::get32(indirectTable[i]); + uint32_t newSymbolIndex = oldSymbolIndex; + if ( (oldSymbolIndex != INDIRECT_SYMBOL_ABS) && (oldSymbolIndex != INDIRECT_SYMBOL_LOCAL) ) { + newSymbolIndex = fOldToNewSymbolIndexes[oldSymbolIndex]; + //fprintf(stderr, "copyIndirectSymbolTable() old=%d, new=%u name=%s in %s\n", oldSymbolIndex, newSymbolIndex, + // &fStrings[fSymbolTable[oldSymbolIndex].n_strx()], fLayout.getFilePath()); + } + E::set32(newIndirectTable[i], newSymbolIndex); + } + offset += (fDynamicSymbolTable->nindirectsyms() * 4); +} + +template +void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset) +{ + // 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; + if ( strcmp(seg->segname(), "__LINKEDIT") == 0 ) { + seg->set_vmaddr(newVMAddress); + seg->set_vmsize(size); + seg->set_filesize(size); + linkEditStartFileOffset = seg->fileoff(); + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + // 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_strsize(fNewStringPool.size()); + fDynamicSymbolTable->set_ilocalsym(fLocalSymbolsStartIndexInNewLinkEdit); + fDynamicSymbolTable->set_nlocalsym(fLocalSymbolsCountInNewLinkEdit); + fDynamicSymbolTable->set_iextdefsym(fExportedSymbolsStartIndexInNewLinkEdit); + fDynamicSymbolTable->set_nextdefsym(fExportedSymbolsCountInNewLinkEdit); + fDynamicSymbolTable->set_iundefsym(fImportSymbolsStartIndexInNewLinkEdit); + 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_locreloff(0); + fDynamicSymbolTable->set_nlocrel(0); +} + + + +template +uint8_t* SharedCache::optimizeLINKEDIT() +{ + // allocate space for optimized LINKEDIT area + uint8_t* newLinkEdit = new uint8_t[fLinkEditsTotalUnoptimizedSize]; + bzero(newLinkEdit, fLinkEditsTotalUnoptimizedSize); + + // make a string pool + StringPool stringPool; + + // create optimizer object for each LINKEDIT segment + std::vector*> optimizers; + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + optimizers.push_back(new LinkEditOptimizer(*it->layout, newLinkEdit, stringPool)); + } + + // copy local symbol table entries + uint32_t symbolTableIndex = 0; + LinkEditOptimizer::makeDummyLocalSymbol(symbolTableIndex, newLinkEdit, stringPool); + for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { + (*it)->copyLocalSymbols(); + } + + // copy exported symbol table entries + for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { + (*it)->copyExportedSymbols(symbolTableIndex); + } + //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 + for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { + (*it)->copyImportedSymbols(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 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; + for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { + (*it)->copyIndirectSymbolTable(indirectSymbolTableOffset); + } + + // copy string pool + uint32_t stringPoolOffset = indirectSymbolTableOffset; + memcpy(&newLinkEdit[stringPoolOffset], stringPool.getBuffer(), stringPool.size()); + + // find new size + uint32_t linkEditsTotalOptimizedSize = (stringPoolOffset + stringPool.size() + 4095) & (-4096); + + // 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); + } + + //fprintf(stderr, "fLinkEditsTotalUnoptimizedSize=%llu, linkEditsTotalOptimizedSize=%u\n", fLinkEditsTotalUnoptimizedSize, linkEditsTotalOptimizedSize); + //fprintf(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); + + // update all LINKEDIT Segment objects to point to same merged LINKEDIT area + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); + for(int i=0; i < segs.size(); ++i) { + MachOLayoutAbstraction::Segment& seg = segs[i]; + 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); + } + } + } + + // return new end of cache + return (uint8_t*)fFirstLinkEditSegment->mappedAddress() + linkEditsTotalOptimizedSize; +} + + +template +bool SharedCache::update(const char* rootPath, const char* cacheDir, bool force, bool optimize, int archIndex, int archCount) +{ + 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 ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: regenerating %s\n", cachePath); + if ( fDylibs.size() == 0 ) { + fprintf(stderr, "update_dyld_shared_cache: warning, empty cache not generated for arch %s\n", archName()); + return false; + } + char tempCachePath[strlen(cachePath)+16]; + sprintf(tempCachePath, "%s.tmp%u", cachePath, getpid()); + try { + 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 + uint32_t cacheFileSize = 0; + for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { + uint32_t end = it->sfm_file_offset + it->sfm_size; + if ( end > cacheFileSize ) + cacheFileSize = end; + } + fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, cacheFileSize, 0 }; + fcntl(fd, F_PREALLOCATE, &fcntlSpec); + + // fill in cache header memory buffer + uint8_t buffer[pageAlign(fHeaderSize)]; + bzero(buffer, sizeof(buffer)); + + // fill in header + dyldCacheHeader* header = (dyldCacheHeader*)buffer; + char temp[16]; + strcpy(temp, "dyld_v1 "); + strcpy(&temp[15-strlen(archName())], archName()); + header->set_magic(temp); + //header->set_architecture(arch()); + header->set_mappingOffset(sizeof(dyldCacheHeader)); + header->set_mappingCount(fMappings.size()); + header->set_imagesOffset(header->mappingOffset() + fMappings.size()*sizeof(dyldCacheFileMapping)); + header->set_imagesCount(fDylibs.size()); + header->set_dyldBaseAddress(fDyldBaseAddress); + //header->set_dependenciesOffset(sizeof(dyldCacheHeader) + fMappings.size()*sizeof(dyldCacheFileMapping) + fDylibs.size()*sizeof(dyldCacheImageInfo)); + //header->set_dependenciesCount(fDependencyPool.size()); + + // fill in mappings + dyldCacheFileMapping* mapping = (dyldCacheFileMapping*)&buffer[sizeof(dyldCacheHeader)]; + for(std::vector::iterator it = fMappings.begin(); it != fMappings.end(); ++it) { + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: cache mappings: address=0x%0llX, size=0x%0llX, fileOffset=0x%0llX, prot=0x%X\n", + it->sfm_address, it->sfm_size, it->sfm_file_offset, it->sfm_init_prot); + mapping->set_address(it->sfm_address); + mapping->set_size(it->sfm_size); + mapping->set_file_offset(it->sfm_file_offset); + mapping->set_max_prot(it->sfm_max_prot); + mapping->set_init_prot(it->sfm_init_prot); + ++mapping; + } + + // fill in image table + dyldCacheImageInfo* image = (dyldCacheImageInfo*)mapping; + for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + image->set_address(it->info.address); + image->set_modTime(it->info.modTime); + image->set_inode(it->info.inode); + image->set_pathFileOffset(cacheFileOffsetForAddress(it->info.address+it->info.pathFileOffset)); + //image->set_dependenciesStartOffset(it->info.dependenciesStartOffset); + ++image; + } + + // write whole header to disk + pwrite(fd, buffer, sizeof(buffer), 0); + + // allocate copy buffer + const uint64_t kCopyBufferSize = 256*1024; + uint8_t* copyBuffer; + vm_address_t addr = 0; + if ( vm_allocate(mach_task_self(), &addr, kCopyBufferSize, VM_FLAGS_ANYWHERE) == KERN_SUCCESS ) + copyBuffer = (uint8_t*)addr; + else + throw "can't allcoate copy buffer"; + + // make zero-fill buffer + uint8_t zerofill[4096]; + bzero(zerofill, sizeof(zerofill)); + + // write each segment to cache file + int dylibIndex = 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); + if ( src == -1 ) + throwf("can't open file %s, errnor=%d", it->layout->getID().name, errno); + // mark source as "don't cache" + (void)fcntl(src, F_NOCACHE, 1); + + if ( verbose ) + fprintf(stderr, "update_prebinding: copying %s to cache\n", it->layout->getID().name); + try { + const std::vector& segs = it->layout->getSegments(); + for (int i=0; i < segs.size(); ++i) { + const MachOLayoutAbstraction::Segment& seg = segs[i]; + if ( verbose ) + fprintf(stderr, "\t\tsegment %s, size=0x%0llX, cache address=0x%0llX\n", seg.name(), seg.fileSize(), seg.newAddress()); + if ( seg.size() > 0 ) { + const uint64_t segmentSrcStartOffset = it->layout->getOffsetInUniversalFile()+seg.fileOffset(); + const uint64_t segmentSize = seg.fileSize(); + const uint64_t segmentDstStartOffset = cacheFileOffsetForAddress(seg.newAddress()); + for(uint64_t copiedAmount=0; copiedAmount < segmentSize; copiedAmount += kCopyBufferSize) { + uint64_t amount = std::min(segmentSize-copiedAmount, kCopyBufferSize); + //fprintf(stderr, "copy 0x%08llX bytes at offset 0x%08llX for segment %s in %s to cache offset 0x%08llX\n", + // amount, segmentSrcStartOffset+copiedAmount, seg.name(), it->layout->getID().name, segmentDstStartOffset+copiedAmount); + if ( ::pread(src, copyBuffer, amount, segmentSrcStartOffset+copiedAmount) != amount ) + throwf("read failure copying dylib errno=%d for %s", errno, it->layout->getID().name); + if ( ::pwrite(fd, copyBuffer, amount, segmentDstStartOffset+copiedAmount) != amount ) + throwf("write failure copying dylib errno=%d for %s", errno, it->layout->getID().name); + } + if ( seg.size() > seg.fileSize() ) { + // write zero-filled area + for(uint64_t copiedAmount=seg.fileSize(); copiedAmount < seg.size(); copiedAmount += sizeof(zerofill)) { + uint64_t amount = std::min(seg.size()-copiedAmount, (uint64_t)(sizeof(zerofill))); + if ( ::pwrite(fd, zerofill, amount, segmentDstStartOffset+copiedAmount) != amount ) + throwf("write failure copying dylib errno=%d for %s", errno, it->layout->getID().name); + } + } + } + } + } + catch (const char* msg) { + throwf("%s while copying %s to shared cache", msg, it->layout->getID().name); + } + ::close(src); + } + + // free copy buffer + vm_deallocate(mach_task_self(), addr, kCopyBufferSize); + + // map cache file + fMappedCacheFile = (uint8_t*)mmap(NULL, cacheFileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if ( fMappedCacheFile == (uint8_t*)(-1) ) + throw "can't mmap cache file"; + + // close cache file + ::fsync(fd); + ::close(fd); + + // set mapped address for each segment + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); + for (int i=0; i < segs.size(); ++i) { + MachOLayoutAbstraction::Segment& seg = segs[i]; + if ( seg.size() > 0 ) + seg.setMappedAddress(fMappedCacheFile + cacheFileOffsetForAddress(seg.newAddress())); + //fprintf(stderr, "%s at %p to %p for %s\n", seg.name(), seg.mappedAddress(), (char*)seg.mappedAddress()+ seg.size(), it->layout->getID().name); + } + } + + // rebase each dylib in shared cache + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + try { + Rebaser r(*it->layout); + r.rebase(); + //if ( verbose ) + // fprintf(stderr, "update_dyld_shared_cache: for %s, rebasing dylib into cache for %s\n", archName(), it->layout->getID().name); + } + catch (const char* msg) { + throwf("%s in %s", msg, it->layout->getID().name); + } + } + + // 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() - fMappedCacheFile); + //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024)); + // update header to reduce mapping size + dyldCacheHeader* cacheHeader = (dyldCacheHeader*)fMappedCacheFile; + dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&fMappedCacheFile[sizeof(dyldCacheHeader)]; + dyldCacheFileMapping* lastMapping = &mappings[cacheHeader->mappingCount()-1]; + lastMapping->set_size(cacheFileSize-lastMapping->file_offset()); + // update fMappings so .map file will print correctly + fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset; + } + + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information for %lu files:\n", archName(), fDylibs.size()); + // instantiate a Binder for each image and add to map + typename Binder::Map map; + std::vector*> binders; + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { + //fprintf(stderr, "binding %s\n", it->layout->getID().name); + Binder* binder = new Binder(*it->layout, fDyldBaseAddress); + binders.push_back(binder); + // only add dylibs to map + 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); + } + // perform binding + for(typename std::vector*>::iterator it = binders.begin(); it != binders.end(); ++it) { + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information in cache for %s\n", archName(), (*it)->getDylibID()); + try { + (*it)->bind(); + } + catch (const char* msg) { + throwf("%s in %s", msg, (*it)->getDylibID()); + } + } + // delete binders + for(typename std::vector*>::iterator it = binders.begin(); it != binders.end(); ++it) { + delete *it; + } + + // close mapping + int result = ::msync(fMappedCacheFile, cacheFileSize, MS_SYNC); + if ( result != 0 ) + throw "error syncing cache file"; + result = ::munmap(fMappedCacheFile, cacheFileSize); + if ( result != 0 ) + throw "error unmapping cache file"; + + // cut back cache file to match optmized size + if ( optimize ) { + if ( ::truncate(tempCachePath, cacheFileSize) != 0 ) + throw "error truncating cache file"; + } + + // commit + ::sync(); + // flush everything to disk, otherwise if kernel panics before the cache file is completely written to disk + // then next reboot will use a corrupted cache and die + 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); + // flush everything to disk to assure rename() gets recorded + ::sync(); + didUpdate = true; + + // generate human readable "map" file that shows the layout of the cache file + 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); + } + 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()); + } + } + 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); + } + } + catch (...){ + // remove temp cache + ::unlink(tempCachePath); + throw; + } + } + return didUpdate; +} + + + +// +// The shared cache is driven by /var/db/dyld/shared_region_roots which contains +// the paths used to search for dylibs that should go in the shared cache +// +// Leading and trailing white space is ignored +// Blank lines are ignored +// Lines starting with # are ignored +// +static void parsePathsFile(const char* filePath, std::vector& paths) +{ + // read in whole file + int fd = open(filePath, O_RDONLY, 0); + if ( fd == -1 ) { + fprintf(stderr, "update_dyld_shared_cache: can't open file: %s\n", filePath); + exit(1); + } + struct stat stat_buf; + fstat(fd, &stat_buf); + char* p = (char*)malloc(stat_buf.st_size); + if ( p == NULL ) { + fprintf(stderr, "update_dyld_shared_cache: malloc failure\n"); + exit(1); + } + if ( read(fd, p, stat_buf.st_size) != stat_buf.st_size ) { + fprintf(stderr, "update_dyld_shared_cache: can't read file: %s\n", filePath); + exit(1); + } + ::close(fd); + + // parse into paths and add to vector + char * const end = &p[stat_buf.st_size]; + enum { lineStart, inSymbol, inComment } state = lineStart; + char* symbolStart = NULL; + for (char* s = p; s < end; ++s ) { + switch ( state ) { + case lineStart: + if ( *s =='#' ) { + state = inComment; + } + else if ( !isspace(*s) ) { + state = inSymbol; + symbolStart = s; + } + break; + case inSymbol: + if ( *s == '\n' ) { + *s = '\0'; + // removing any trailing spaces + char* last = s-1; + while ( isspace(*last) ) { + *last = '\0'; + --last; + } + paths.push_back(symbolStart); + symbolStart = NULL; + state = lineStart; + } + break; + case inComment: + if ( *s == '\n' ) + state = lineStart; + break; + } + } + // Note: we do not free() the malloc buffer, because the strings in it are used by exec() +} + + +static void scanForSharedDylibs(const char* rootPath, const char* dirOfPathFiles, const std::set& onlyArchs) +{ + char rootDirOfPathFiles[strlen(rootPath)+strlen(dirOfPathFiles)+2]; + if ( strlen(rootPath) != 0 ) { + strcpy(rootDirOfPathFiles, rootPath); + strcat(rootDirOfPathFiles, dirOfPathFiles); + dirOfPathFiles = rootDirOfPathFiles; + } + + // extract all root paths from files in "/var/db/dyld/shared_region_roots/" + if ( verbose ) + fprintf(stderr, "update_dyld_shared_cache: finding roots in: %s\n", dirOfPathFiles); + std::vector rootsPaths; + DIR* dir = ::opendir(dirOfPathFiles); + if ( dir == NULL ) + throwf("%s does not exist, errno=%d\n", dirOfPathFiles, errno); + for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) { + if ( entry->d_type == DT_REG ) { + // only look at files ending in .paths + if ( strcmp(&entry->d_name[entry->d_namlen-6], ".paths") == 0 ) { + char fullPath[strlen(dirOfPathFiles)+entry->d_namlen+2]; + strcpy(fullPath, dirOfPathFiles); + strcat(fullPath, "/"); + strcat(fullPath, entry->d_name); + parsePathsFile(fullPath, rootsPaths); + } + else { + fprintf(stderr, "update_dyld_shared_cache: warning, ignore file with wrong extension: %s\n", entry->d_name); + } + } + } + ::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"); +} + + + +static bool updateSharedeCacheFile(const char* rootPath, const char* cacheDir, const std::set& onlyArchs, + bool force, bool alphaSort, bool optimize) +{ + bool didUpdate = false; + // get dyld load address info + UniversalMachOLayout* 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 ) { + case CPU_TYPE_POWERPC: + { + SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); + #if __i386__ + // Rosetta does not work with optimized dyld shared cache + didUpdate |= cache.update(rootPath, cacheDir, force, false, index, archCount); + #else + didUpdate |= cache.update(rootPath, cacheDir, force, optimize, index, archCount); + #endif + } + break; + case CPU_TYPE_POWERPC64: + { + SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); + didUpdate |= cache.update(rootPath, cacheDir, force, optimize, index, archCount); + } + break; + case CPU_TYPE_I386: + { + SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); + didUpdate |= cache.update(rootPath, cacheDir, force, optimize, index, archCount); + } + break; + case CPU_TYPE_X86_64: + { + SharedCache cache(ArchGraph::getArch(*a), alphaSort, dyldBaseAddress); + didUpdate |= cache.update(rootPath, cacheDir, force, optimize, index, archCount); + } + break; + } + } + return didUpdate; +} + + +static void usage() +{ + fprintf(stderr, "update_dyld_shared_cache [-force] [-root dir] [-arch arch] [-debug]\n"); +} + + +kern_return_t do_dyld_shared_cache_missing(mach_port_t dyld_port, cpu_type_t arch) +{ + 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) ) + 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; + } + return KERN_SUCCESS; +} + + +kern_return_t do_dyld_shared_cache_out_of_date(mach_port_t dyld_port, cpu_type_t arch) +{ + // reduce priority of this process so it only runs at the lowest priority + setpriority(PRIO_PROCESS, 0, PRIO_MAX); + + // and then rebuild cache + return do_dyld_shared_cache_missing(dyld_port, arch); +} + + +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 + // Just process one message and quit + mach_msg_size_t mxmsgsz = sizeof(union __RequestUnion__do_dyld_server_subsystem) + MAX_TRAILER_SIZE; + mach_msg_server_once(dyld_server_server, mxmsgsz, mp, MACH_RCV_TIMEOUT); + // The problem with staying alive and processing messages is that the rest of this + // tool leaks mapped memory and file descriptors. Quiting will clean that up. + // 9A516 - Keep getting disk full errors + 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; + + 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); + } + } + 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 + } + + 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); + + // 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); + } + } + } + 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 61f49ef..7a84b13 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -33,64 +33,72 @@ #include #include #include +#include #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::fgTotalRebaseFixups = 0; uint32_t ImageLoader::fgTotalBindFixups = 0; +uint32_t ImageLoader::fgTotalBindSymbolsResolved = 0; +uint32_t ImageLoader::fgTotalBindImageSearches = 0; uint32_t ImageLoader::fgTotalLazyBindFixups = 0; uint32_t ImageLoader::fgTotalPossibleLazyBindFixups = 0; +uint32_t ImageLoader::fgTotalSegmentsMapped = 0; +uint64_t ImageLoader::fgTotalBytesMapped = 0; +uint64_t ImageLoader::fgTotalBytesPreFetched = 0; uint64_t ImageLoader::fgTotalLoadLibrariesTime; uint64_t ImageLoader::fgTotalRebaseTime; uint64_t ImageLoader::fgTotalBindTime; -uint64_t ImageLoader::fgTotalNotifyTime; uint64_t ImageLoader::fgTotalInitTime; uintptr_t ImageLoader::fgNextSplitSegAddress = 0x90000000; -uintptr_t Segment::fgNextNonSplitSegAddress = 0x8F000000; +uint16_t ImageLoader::fgLoadOrdinal = 0; +uintptr_t Segment::fgNextPIEDylibAddress = 0; - -__attribute__((noreturn)) -void throwf(const char* format, ...) -{ - va_list list; - char* p; - va_start(list, format); - vasprintf(&p, format, list); - va_end(list); - - const char* t = p; - throw t; -} - void ImageLoader::init(const char* path, uint64_t offsetInFat, dev_t device, ino_t inode, time_t modDate) { fPathHash = 0; - fPath = NULL; - if ( path != NULL ) - this->setPath(path); + fPath = path; fLogicalPath = NULL; fDevice = device; fInode = inode; fLastModified = modDate; fOffsetInFatFile = offsetInFat; - //fSegments = NULL; fLibraries = NULL; fLibrariesCount = 0; - fReferenceCount = 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; - fLibrariesLoaded = false; - fBased = false; - fBoundAllNonLazy = false; - fBoundAllLazy = false; + fRegisteredDOF = false; +#if IMAGE_NOTIFY_SUPPORT fAnnounced = false; - fInitialized = false; - fNextAddImageIndex = 0; +#endif + fAllLazyPointersBound = false; + fBeingRemoved = false; + fPathOwnedByImage = false; +#if RECURSIVE_INITIALIZER_LOCK + fInitializerRecursiveLock = NULL; +#else + fInitializerLock = 0; +#endif + if ( fPath != NULL ) + fPathHash = hash(fPath); } @@ -107,29 +115,77 @@ ImageLoader::ImageLoader(const char* moduleName) ImageLoader::~ImageLoader() { - // need to read up on STL and see if this is right way to destruct vector contents - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; - delete seg; - } - if ( fPath != NULL ) + 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--; + } + delete fDynamicReferences; + } +} + +void ImageLoader::setMapped(const LinkContext& context) +{ + fState = dyld_image_state_mapped; + context.notifySingle(dyld_image_state_mapped, this->machHeader(), fPath, fLastModified); +} + +void ImageLoader::addDynamicReference(const ImageLoader* target) +{ + if ( fDynamicReferences == NULL ) + fDynamicReferences = new std::set(); + if ( fDynamicReferences->count(target) == 0 ) { + fDynamicReferences->insert(target); + const_cast(target)->fDynamicReferenceCount++; + } + //dyld::log("dyld: addDynamicReference() from %s to %s, fDynamicReferences->size()=%lu\n", this->getPath(), target->getPath(), fDynamicReferences->size()); +} + +int ImageLoader::compare(const ImageLoader* right) const +{ + if ( this->fDepth == right->fDepth ) { + if ( this->fLoadOrder == right->fLoadOrder ) + return 0; + else if ( this->fLoadOrder < right->fLoadOrder ) + return -1; + else + return 1; + } + else { + if ( this->fDepth < right->fDepth ) + return -1; + else + return 1; + } } - void ImageLoader::setPath(const char* path) { - if ( fPath != NULL ) { - // if duplicate path, do nothing - if ( strcmp(path, fPath) == 0 ) - return; + if ( fPathOwnedByImage && (fPath != NULL) ) delete [] fPath; - } fPath = new char[strlen(path)+1]; strcpy((char*)fPath, path); + fPathOwnedByImage = true; // delete fPath when this image is destructed + fPathHash = hash(fPath); +} + +void ImageLoader::setPathUnowned(const char* path) +{ + if ( fPathOwnedByImage && (fPath != NULL) ) { + delete [] fPath; + } + fPath = path; + fPathOwnedByImage = false; fPathHash = hash(fPath); } @@ -202,10 +258,6 @@ uint64_t ImageLoader::getOffsetInFatFile() const void ImageLoader::setLeaveMapped() { fLeaveMapped = true; - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i){ - fSegments[i]->setUnMapWhenDestructed(false); - } } void ImageLoader::setHideExports(bool hide) @@ -220,20 +272,21 @@ bool ImageLoader::hasHiddenExports() const bool ImageLoader::isLinked() const { - return fBoundAllNonLazy; + return (fState >= dyld_image_state_bound); } -time_t ImageLoader::lastModified() +time_t ImageLoader::lastModified() const { return fLastModified; } bool ImageLoader::containsAddress(const void* addr) const { - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; - const uint8_t* start = (const uint8_t*)seg->getActualLoadAddress(); + 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() ) return true; @@ -241,45 +294,61 @@ bool ImageLoader::containsAddress(const void* addr) const return false; } -void ImageLoader::addMappedRegions(RegionsVector& regions) 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(); + if ( (start <= segStart) && (segStart < end) ) + return true; + if ( (start <= segEnd) && (segEnd < end) ) + return true; + if ( (segStart < start) && (end < segEnd) ) + return true; + } + return false; +} + +void ImageLoader::getMappedRegions(MappedRegion*& regions) const { - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; + for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { + Segment* seg = *it; MappedRegion region; - region.address = seg->getActualLoadAddress(); + region.address = seg->getActualLoadAddress(this); region.size = seg->getSize(); - regions.push_back(region); + *regions++ = region; } } -void ImageLoader::incrementReferenceCount() +static bool notInImgageList(const ImageLoader* image, const ImageLoader** dsiStart, const ImageLoader** dsiCur) { - ++fReferenceCount; + for (const ImageLoader** p = dsiStart; p < dsiCur; ++p) + if ( *p == image ) + return false; + return true; } -bool ImageLoader::decrementReferenceCount() -{ - return ( --fReferenceCount == 0 ); -} // private method that handles circular dependencies by only search any image once -const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name, std::set& dontSearchImages, ImageLoader** foundIn) const +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name, + const ImageLoader** dsiStart, const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const { const ImageLoader::Symbol* sym; + // search self - if ( dontSearchImages.count(this) == 0 ) { + if ( notInImgageList(this, dsiStart, dsiCur) ) { sym = this->findExportedSymbol(name, NULL, false, foundIn); if ( sym != NULL ) return sym; - dontSearchImages.insert(this); + *dsiCur++ = this; } // search directly dependent libraries for (uint32_t i=0; i < fLibrariesCount; ++i) { ImageLoader* dependentImage = fLibraries[i].image; - if ( (dependentImage != NULL) && (dontSearchImages.count(dependentImage) == 0) ) { + if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { const ImageLoader::Symbol* sym = dependentImage->findExportedSymbol(name, NULL, false, foundIn); if ( sym != NULL ) return sym; @@ -289,11 +358,11 @@ const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcep // search indirectly dependent libraries for (uint32_t i=0; i < fLibrariesCount; ++i) { ImageLoader* dependentImage = fLibraries[i].image; - if ( (dependentImage != NULL) && (dontSearchImages.count(dependentImage) == 0) ) { - const ImageLoader::Symbol* sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dontSearchImages, foundIn); + if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { + *dsiCur++ = dependentImage; + const ImageLoader::Symbol* sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dsiStart, dsiCur, dsiEnd, foundIn); if ( sym != NULL ) return sym; - dontSearchImages.insert(dependentImage); } } @@ -301,90 +370,132 @@ const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcep } -const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImages(const char* name, ImageLoader** foundIn) const +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const { - std::set dontSearchImages; - dontSearchImages.insert(this); // don't search this image - return this->findExportedSymbolInDependentImagesExcept(name, dontSearchImages, foundIn); + unsigned int imageCount = context.imageCount(); + const ImageLoader* dontSearchImages[imageCount]; + dontSearchImages[0] = this; // don't search this image + const ImageLoader** cur = &dontSearchImages[1]; + return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); } -const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name, ImageLoader** foundIn) const +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const { - std::set dontSearchImages; - return this->findExportedSymbolInDependentImagesExcept(name, dontSearchImages, foundIn); + unsigned int imageCount = context.imageCount(); + const ImageLoader* dontSearchImages[imageCount]; + const ImageLoader** cur = &dontSearchImages[0]; + return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); } -void ImageLoader::link(const LinkContext& context, BindingLaziness bindness, InitializerRunning inits, uint32_t notifyCount) +void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, const RPathChain& loaderRPaths) { - uint64_t t1 = mach_absolute_time(); - this->recursiveLoadLibraries(context); + //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", this->getPath(), fStaticReferenceCount, fNeverUnload); + + uint64_t t0 = mach_absolute_time(); + this->recursiveLoadLibraries(context,loaderRPaths); + context.notifyBatch(dyld_image_state_dependents_mapped); + // we only do the loading step for preflights + if ( preflightOnly ) + return; + + uint64_t t1 = mach_absolute_time(); + context.clearAllDepths(); + this->recursiveUpdateDepth(context.imageCount()); + uint64_t t2 = mach_absolute_time(); this->recursiveRebase(context); + context.notifyBatch(dyld_image_state_rebased); uint64_t t3 = mach_absolute_time(); - this->recursiveBind(context, bindness); - + this->recursiveBind(context, forceLazysBound); + context.notifyBatch(dyld_image_state_bound); + uint64_t t4 = mach_absolute_time(); - this->recursiveImageNotification(context, notifyCount); - - if ( (inits == kRunInitializers) || (inits == kDontRunInitializersButTellObjc) ) { - std::vector newImages; - this->recursiveImageAnnouncement(context, newImages); // build bottom up list images being added - context.notifyAdding(newImages); // tell gdb or anyone who cares about these - } + std::vector dofs; + this->recursiveGetDOFSections(context, dofs); + context.registerDOFs(dofs); + - uint64_t t5 = mach_absolute_time(); - if ( inits == kRunInitializers ) { - this->recursiveInitialization(context); - uint64_t t6 = mach_absolute_time(); - fgTotalInitTime += t6 - t5; - } - fgTotalLoadLibrariesTime += t2 - t1; + fgTotalLoadLibrariesTime += t1 - t0; fgTotalRebaseTime += t3 - t2; fgTotalBindTime += t4 - t3; - fgTotalNotifyTime += t5 - t4; + + // done with initial dylib loads + Segment::fgNextPIEDylibAddress = 0; +} + + +void ImageLoader::printReferenceCounts() +{ + dyld::log(" dlopen=%d, static=%d, dynamic=%d for %s\n", + fDlopenReferenceCount, fStaticReferenceCount, fDynamicReferenceCount, getPath() ); } -// only called pre-main on main executable -// if crt.c is ever cleaned up, this could go away +bool ImageLoader::decrementDlopenReferenceCount() +{ + if ( fDlopenReferenceCount == 0 ) + return true; + --fDlopenReferenceCount; + return false; +} + void ImageLoader::runInitializers(const LinkContext& context) { - std::vector newImages; - this->recursiveImageAnnouncement(context, newImages); // build bottom up list images being added - context.notifyAdding(newImages); // tell gdb or anyone who cares about these +#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 - this->recursiveInitialization(context); + uint64_t t1 = mach_absolute_time(); + this->recursiveInitialization(context, mach_thread_self()); + context.notifyBatch(dyld_image_state_initialized); + uint64_t t2 = mach_absolute_time(); + fgTotalInitTime += (t2 - t1); } -// called inside _dyld_register_func_for_add_image() -void ImageLoader::runNotification(const LinkContext& context, uint32_t notifyCount) + +void ImageLoader::bindAllLazyPointers(const LinkContext& context, bool recursive) { - this->recursiveImageNotification(context, notifyCount); + if ( ! fAllLazyPointersBound ) { + fAllLazyPointersBound = true; + + 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); + } + } + // bind lazys in this image + this->doBind(context, true); + } } intptr_t ImageLoader::assignSegmentAddresses(const LinkContext& context) { // preflight and calculate slide if needed - const unsigned int segmentCount = fSegments.size(); intptr_t slide = 0; if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) { bool needsToSlide = false; uintptr_t lowAddr = UINTPTR_MAX; uintptr_t highAddr = 0; - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; + 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(); + const uintptr_t segHigh = (segLow + seg->getSize() + 4095) & -4096; if ( segLow < lowAddr ) lowAddr = segLow; if ( segHigh > highAddr ) highAddr = segHigh; - if ( context.slideAndPackDylibs || !seg->hasPreferredLoadAddress() || !Segment::reserveAddressRange(seg->getPreferredLoadAddress(), seg->getSize()) ) + if ( !seg->hasPreferredLoadAddress() || !Segment::reserveAddressRange(seg->getPreferredLoadAddress(), seg->getSize()) ) needsToSlide = true; } if ( needsToSlide ) { @@ -394,8 +505,8 @@ intptr_t ImageLoader::assignSegmentAddresses(const LinkContext& context) } } else if ( ! this->segmentsCanSlide() ) { - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; + 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()) ) @@ -412,42 +523,35 @@ intptr_t ImageLoader::assignSegmentAddresses(const LinkContext& context) void ImageLoader::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) { if ( context.verboseMapping ) - fprintf(stderr, "dyld: Mapping %s\n", this->getPath()); + dyld::log("dyld: Mapping %s\n", this->getPath()); // find address range for image intptr_t slide = this->assignSegmentAddresses(context); // map in all segments - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; - seg->map(fd, offsetInFat, slide, context); + 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); - - // now that it is mapped and slide is set, mark that we should unmap it when done - for(unsigned int i=0; i < segmentCount; ++i){ - fSegments[i]->setUnMapWhenDestructed(true); - } } void ImageLoader::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context) { if ( context.verboseMapping ) - fprintf(stderr, "dyld: Mapping memory %p\n", memoryImage); + dyld::log("dyld: Mapping memory %p\n", memoryImage); // find address range for image intptr_t slide = this->assignSegmentAddresses(context); // map in all segments - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; - seg->map(memoryImage, slide, context); + 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(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; - seg->setPermissions(); + for(ImageLoader::SegmentIterator it = this->beginSegments(); it != this->endSegments(); ++it ) { + Segment* seg = *it; + seg->setPermissions(context, this); } } @@ -457,35 +561,81 @@ bool ImageLoader::allDependentLibrariesAsWhenPreBound() const } -void ImageLoader::recursiveLoadLibraries(const LinkContext& context) +unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) +{ + // the purpose of this phase is to make the images sortable such that + // in a sort list of images, every image that an image depends on + // occurs in the list before it. + if ( fDepth == 0 ) { + // break cycles + fDepth = 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); + if ( d < minDependentDepth ) + minDependentDepth = d; + } + } + + // make me less deep then all my dependents + fDepth = minDependentDepth - 1; + } + + return fDepth; +} + + +void ImageLoader::recursiveLoadLibraries(const LinkContext& context, const RPathChain& loaderRPaths) { - if ( ! fLibrariesLoaded ) { + if ( fState < dyld_image_state_dependents_mapped ) { // break cycles - fLibrariesLoaded = true; + fState = dyld_image_state_dependents_mapped; // get list of libraries this image needs fLibrariesCount = this->doGetDependentLibraryCount(); fLibraries = new DependentLibrary[fLibrariesCount]; - this->doGetDependentLibraries(fLibraries); + bzero(fLibraries, sizeof(DependentLibrary)*fLibrariesCount); + DependentLibraryInfo libraryInfos[fLibrariesCount]; + this->doGetDependentLibraries(libraryInfos); + + // get list of rpaths that this image adds + std::vector rpathsFromThisImage; + this->getRPaths(context, rpathsFromThisImage); + const RPathChain thisRPaths(&loaderRPaths, &rpathsFromThisImage); // try to load each bool canUsePrelinkingInfo = true; for(unsigned int i=0; i < fLibrariesCount; ++i){ DependentLibrary& requiredLib = fLibraries[i]; + DependentLibraryInfo& requiredLibInfo = libraryInfos[i]; try { - requiredLib.image = context.loadLibrary(requiredLib.name, true, this->getPath(), NULL); + bool depNamespace = false; + requiredLib.image = context.loadLibrary(requiredLibInfo.name, true, depNamespace, this->getPath(), &thisRPaths); if ( requiredLib.image == this ) { // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168 - requiredLib.image = context.loadLibrary(requiredLib.name, false, NULL, NULL); + requiredLib.image = context.loadLibrary(requiredLibInfo.name, false, depNamespace, NULL, NULL); if ( requiredLib.image != this ) - fprintf(stderr, "dyld: warning DYLD_ setting caused circular dependency in %s\n", this->getPath()); + 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.checksumMatches = ( actualInfo.checksum == requiredLib.info.checksum ); + 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); + } // check found library version is compatible - if ( actualInfo.minVersion < requiredLib.info.minVersion ) { - throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d", - this->getShortName(), requiredLib.info.minVersion >> 16, (requiredLib.info.minVersion >> 8) & 0xff, requiredLib.info.minVersion & 0xff, + 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); } // prebinding for this image disabled if any dependent library changed or slid @@ -493,7 +643,8 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context) canUsePrelinkingInfo = false; //if ( context.verbosePrebinding ) { // if ( !requiredLib.checksumMatches ) - // fprintf(stderr, "dyld: checksum mismatch, (%lld v %lld) for %s referencing %s\n", requiredLib.info.checksum, actualInfo.checksum, this->getPath(), requiredLib.image->getPath()); + // 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()); //} @@ -501,13 +652,9 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context) catch (const char* msg) { //if ( context.verbosePrebinding ) // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), requiredLib.image->getPath()); - if ( requiredLib.required ) { - const char* formatString = "Library not loaded: %s\n Referenced from: %s\n Reason: %s"; - const char* referencedFrom = this->getPath(); - char buf[strlen(requiredLib.name)+strlen(referencedFrom)+strlen(formatString)+strlen(msg)+2]; - sprintf(buf, formatString, requiredLib.name, referencedFrom, msg); - fLibrariesLoaded = false; - throw strdup(buf); // this is a leak if exception doesn't halt program + 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; @@ -518,13 +665,9 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context) // tell each to load its dependents for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) { - libInfo.isSubFramework = libInfo.image->isSubframeworkOf(context, this); - libInfo.isReExported = libInfo.isSubFramework || this->hasSubLibrary(context, libInfo.image); - //if ( libInfo.isReExported ) - // fprintf(stderr, "%s re-exports %s\n", strrchr(this->getPath(), '/'), strrchr(libInfo.image->getPath(),'/')); - libInfo.image->recursiveLoadLibraries(context); + DependentLibrary& lib = fLibraries[i]; + if ( lib.image != NULL ) { + lib.image->recursiveLoadLibraries(context, thisRPaths); } } @@ -539,14 +682,20 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context) } } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromThisImage.begin(); it != rpathsFromThisImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + } } void ImageLoader::recursiveRebase(const LinkContext& context) { - if ( ! fBased ) { + if ( fState < dyld_image_state_rebased ) { // break cycles - fBased = true; + fState = dyld_image_state_rebased; try { // rebase lower level libraries first @@ -558,11 +707,14 @@ void ImageLoader::recursiveRebase(const LinkContext& context) // rebase this image doRebase(context); + + // notify + context.notifySingle(dyld_image_state_rebased, this->machHeader(), fPath, fLastModified); } catch (const char* msg) { // this image is not rebased - fBased = false; - throw msg; + fState = dyld_image_state_dependents_mapped; + throw; } } } @@ -570,89 +722,43 @@ void ImageLoader::recursiveRebase(const LinkContext& context) -void ImageLoader::recursiveBind(const LinkContext& context, BindingLaziness bindness) +void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound) { - // normally just non-lazy pointers are bound up front, - // but DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound up from - // and some dyld API's bind all lazys at runtime - bool nonLazy = false; - bool lazy = false; - switch( bindness ) { - case kNonLazyOnly: - nonLazy = true; - break; - case kLazyAndNonLazy: - nonLazy = true; - lazy = true; - break; - case kLazyOnly: - case kLazyOnlyNoDependents: - lazy = true; - break; - } - const bool doNonLazy = nonLazy && !fBoundAllNonLazy; - const bool doLazy = lazy && !fBoundAllLazy; - if ( doNonLazy || doLazy ) { + // Normally just non-lazy pointers are bound immediately. + // The exceptions are: + // 1) DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound immediately + // 2) some API's (e.g. RTLD_NOW) can cause lazy pointers to be bound immediately + if ( fState < dyld_image_state_bound ) { // break cycles - bool oldBoundAllNonLazy = fBoundAllNonLazy; - bool oldBoundAllLazy = fBoundAllLazy; - fBoundAllNonLazy = fBoundAllNonLazy || nonLazy; - fBoundAllLazy = fBoundAllLazy || lazy; - + fState = dyld_image_state_bound; + try { // bind lower level libraries first - if ( bindness != kLazyOnlyNoDependents ) { - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) - libInfo.image->recursiveBind(context, bindness); - } + for(unsigned int i=0; i < fLibrariesCount; ++i){ + DependentLibrary& libInfo = fLibraries[i]; + if ( libInfo.image != NULL ) + libInfo.image->recursiveBind(context, forceLazysBound); } // bind this image - if ( doLazy && !doNonLazy ) - doBind(context, kLazyOnly); - else if ( !doLazy && doNonLazy ) - doBind(context, kNonLazyOnly); - else - doBind(context, kLazyAndNonLazy); + 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); } catch (const char* msg) { // restore state - fBoundAllNonLazy = oldBoundAllNonLazy; - fBoundAllLazy = oldBoundAllLazy; - throw msg; - } - } -} - -// -// This is complex because _dyld_register_func_for_add_image() is defined to not only -// notify you of future image loads, but also of all currently loaded images. Therefore -// each image needs to track that all add-image-funcs have been notified about it. -// Since add-image-funcs cannot be removed, each has a unique index and each image -// records the thru which index notificiation has already been done. -// -void ImageLoader::recursiveImageNotification(const LinkContext& context, uint32_t addImageCount) -{ - if ( fNextAddImageIndex < addImageCount ) { - // break cycles - const uint32_t initIndex = fNextAddImageIndex; - fNextAddImageIndex = addImageCount; - - // notify all requestors about this image - context.imageNotification(this, initIndex); - - // notify about lower level libraries first - for(unsigned int i=0; i < fLibrariesCount; ++i){ - DependentLibrary& libInfo = fLibraries[i]; - if ( libInfo.image != NULL ) - libInfo.image->recursiveImageNotification(context, addImageCount); + fState = dyld_image_state_rebased; + throw; } } } -void ImageLoader::recursiveImageAnnouncement(const LinkContext& context, std::vector& newImages) +#if IMAGE_NOTIFY_SUPPORT +void ImageLoader::recursiveImageAnnouncement(const LinkContext& context, ImageLoader**& newImages) { if ( ! fAnnounced ) { // break cycles @@ -665,199 +771,110 @@ void ImageLoader::recursiveImageAnnouncement(const LinkContext& context, std::ve libInfo.image->recursiveImageAnnouncement(context, newImages); } - // add to list of images to notify gdb about - newImages.push_back(this); - //fprintf(stderr, "next size = %d\n", newImages.size()); + // add to list of images to notify about + *newImages++ = this; + //dyld::log("next size = %d\n", newImages.size()); // remember that this image wants to be notified about other images if ( this->hasImageNotification() ) context.addImageNeedingNotification(this); } } +#endif +void ImageLoader::recursiveGetDOFSections(const LinkContext& context, std::vector& dofs) +{ + if ( ! fRegisteredDOF ) { + // break cycles + 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); + } + this->doGetDOFSections(context, dofs); + } +} + + +void ImageLoader::recursiveSpinLock(recursive_lock& rlock) +{ + // try to set image's ivar fInitializerRecursiveLock to point to this lock_info + // keep trying until success (spin) + while ( ! OSAtomicCompareAndSwapPtrBarrier(NULL, &rlock, (void**)&fInitializerRecursiveLock) ) { + // if fInitializerRecursiveLock already points to a different lock_info, if it is for + // the same thread we are on, the increment the lock count, otherwise continue to spin + if ( (fInitializerRecursiveLock != NULL) && (fInitializerRecursiveLock->thread == rlock.thread) ) + break; + } + ++(fInitializerRecursiveLock->count); +} + +void ImageLoader::recursiveSpinUnLock() +{ + if ( --(fInitializerRecursiveLock->count) == 0 ) + fInitializerRecursiveLock = NULL; +} -void ImageLoader::recursiveInitialization(const LinkContext& context) +void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread) { - if ( ! fInitialized ) { +#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 - fInitialized = true; + 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]; - if ( libInfo.image != NULL ) - libInfo.image->recursiveInitialization(context); + // don't try to initialize stuff "above" me + if ( (libInfo.image != NULL) && (libInfo.image->fDepth >= fDepth) ) + libInfo.image->recursiveInitialization(context, this_thread); } // record termination order if ( this->needsTermination() ) context.terminationRecorder(this); + // 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); + // 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); } catch (const char* msg) { // this image is not initialized - fInitialized = false; - throw msg; - } - } -} - -void ImageLoader::reprebindCommit(const LinkContext& context, bool commit, bool unmapOld) -{ - // do nothing on unprebound images - if ( ! this->isPrebindable() ) - return; - - // do nothing if prebinding is up to date - if ( this->usablePrebinding(context) ) - return; - - // make sure we are not replacing a symlink with a file - char realFilePath[PATH_MAX]; - if ( realpath(this->getPath(), realFilePath) == NULL ) { - throwf("realpath() failed on %s, errno=%d", this->getPath(), errno); - } - // recreate temp file name - char tempFilePath[PATH_MAX]; - char suffix[64]; - sprintf(suffix, "_redoprebinding%d", getpid()); - ImageLoader::addSuffix(realFilePath, suffix, tempFilePath); - - if ( commit ) { - // all files successfully reprebound, so do swap - int result = rename(tempFilePath, realFilePath); - if ( result != 0 ) { - // if there are two dylibs with the same install path, the second will fail to prebind - // because the _redoprebinding temp file is gone. In that case, log and go on. - if ( errno == ENOENT ) - fprintf(stderr, "update_prebinding: temp file missing: %s\n", tempFilePath); - else - throwf("can't swap temporary re-prebound file: rename(%s,%s) returned errno=%d", tempFilePath, realFilePath, errno); - } - else if ( unmapOld ) { - this->prebindUnmap(context); + fState = oldState; + #if RECURSIVE_INITIALIZER_LOCK + recursiveSpinUnLock(); + #else + _spin_unlock(&fInitializerLock); + #endif + throw; } } - else { - // something went wrong during prebinding, delete the temp files - unlink(tempFilePath); - } -} - -uint64_t ImageLoader::reprebind(const LinkContext& context, time_t timestamp) -{ - // do nothing on unprebound images - if ( ! this->isPrebindable() ) - return INT64_MAX; - - // do nothing if prebinding is up to date - if ( this->usablePrebinding(context) ) { - if ( context.verbosePrebinding ) - fprintf(stderr, "dyld: no need to re-prebind: %s\n", this->getPath()); - return INT64_MAX; - } - // recreate temp file name - char realFilePath[PATH_MAX]; - if ( realpath(this->getPath(), realFilePath) == NULL ) { - throwf("realpath() failed on %s, errno=%d", this->getPath(), errno); - } - char tempFilePath[PATH_MAX]; - char suffix[64]; - sprintf(suffix, "_redoprebinding%d", getpid()); - ImageLoader::addSuffix(realFilePath, suffix, tempFilePath); - - // make copy of file and map it in - uint8_t* fileToPrebind; - uint64_t fileToPrebindSize; - uint64_t freespace = this->copyAndMap(tempFilePath, &fileToPrebind, &fileToPrebindSize); - - // do format specific prebinding - this->doPrebinding(context, timestamp, fileToPrebind); - // flush and swap files - int result = msync(fileToPrebind, fileToPrebindSize, MS_ASYNC); - if ( result != 0 ) - throw "error syncing re-prebound file"; - result = munmap(fileToPrebind, fileToPrebindSize); - if ( result != 0 ) - throw "error unmapping re-prebound file"; - - // log - if ( context.verbosePrebinding ) - fprintf(stderr, "dyld: re-prebound: %p %s\n", this->machHeader(), this->getPath()); - - return freespace; -} - -uint64_t ImageLoader::copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize) -{ - // reopen dylib - int src = open(this->getPath(), O_RDONLY); - if ( src == -1 ) - throw "can't open image"; - struct stat stat_buf; - if ( fstat(src, &stat_buf) == -1) - throw "can't stat image"; - if ( stat_buf.st_mtime != fLastModified ) - throw "image file changed since it was loaded"; - - // create new file with all same permissions to hold copy of dylib - unlink(tempFile); - int dst = open(tempFile, O_CREAT | O_RDWR | O_TRUNC, stat_buf.st_mode); - if ( dst == -1 ) - throw "can't create temp image"; - - // mark source as "don't cache" - (void)fcntl(src, F_NOCACHE, 1); - // we want to cache the dst because we are about to map it in and modify it - - // copy permission bits - if ( chmod(tempFile, stat_buf.st_mode & 07777) == -1 ) - throwf("can't chmod temp image. errno=%d for %s", errno, this->getPath()); - if ( chown(tempFile, stat_buf.st_uid, stat_buf.st_gid) == -1) - throwf("can't chown temp image. errno=%d for %s", errno, this->getPath()); - - // copy contents - ssize_t len; - const uint32_t kBufferSize = 128*1024; - static uint8_t* buffer = NULL; - if ( buffer == NULL ) { - vm_address_t addr = 0; - if ( vm_allocate(mach_task_self(), &addr, kBufferSize, true /*find range*/) == KERN_SUCCESS ) - buffer = (uint8_t*)addr; - else - throw "can't allcoate copy buffer"; - } - while ( (len = read(src, buffer, kBufferSize)) > 0 ) { - if ( write(dst, buffer, len) == -1 ) - throwf("write failure copying dylib errno=%d for %s", errno, this->getPath()); - } - - // map in dst file - *fileToPrebindSize = stat_buf.st_size - fOffsetInFatFile; // this may map in too much, but it does not matter - *fileToPrebind = (uint8_t*)mmap(NULL, *fileToPrebindSize, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, dst, fOffsetInFatFile); - if ( *fileToPrebind == (uint8_t*)(-1) ) - throw "can't mmap temp image"; - - // get free space remaining on dst volume - struct statfs statfs_buf; - if ( fstatfs(dst, &statfs_buf) != 0 ) - throwf("can't fstatfs(), errno=%d for %s", errno, tempFile); - uint64_t freespace = (uint64_t)statfs_buf.f_bavail * (uint64_t)statfs_buf.f_bsize; - - // closing notes: - // ok to close file after mapped in - // ok to throw above without closing file because the throw will terminate update_prebinding - int result1 = close(dst); - int result2 = close(src); - if ( (result1 != 0) || (result2 != 0) ) - throw "can't close file"; - - return freespace; +#if RECURSIVE_INITIALIZER_LOCK + recursiveSpinUnLock(); +#else + _spin_unlock(&fInitializerLock); +#endif } @@ -875,49 +892,62 @@ static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) uint32_t milliSeconds = milliSecondsTimeTen/10; uint32_t percentTimesTen = (partTime*1000)/totalTime; uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); + dyld::log("%s: %u.%u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimeTen-milliSeconds*10, percent, percentTimesTen-percent*10); } else { uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; uint32_t seconds = secondsTimeTen/10; uint32_t percentTimesTen = (partTime*1000)/totalTime; uint32_t percent = percentTimesTen/10; - fprintf(stderr, "%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + dyld::log("%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); } } static char* commatize(uint64_t in, char* out) { - char* result = out; - char rawNum[30]; - sprintf(rawNum, "%llu", in); - const int rawNumLen = strlen(rawNum); - for(int i=0; i < rawNumLen-1; ++i) { - *out++ = rawNum[i]; - if ( ((rawNumLen-i) % 3) == 1 ) - *out++ = ','; + uint64_t div10 = in / 10; + uint8_t delta = in - div10*10; + char* s = &out[32]; + int digitCount = 1; + *s = '\0'; + *(--s) = '0' + delta; + in = div10; + while ( in != 0 ) { + if ( (digitCount % 3) == 0 ) + *(--s) = ','; + div10 = in / 10; + delta = in - div10*10; + *(--s) = '0' + delta; + in = div10; + ++digitCount; } - *out++ = rawNum[rawNumLen-1]; - *out = '\0'; - return result; -} + return s; +} + void ImageLoader::printStatistics(unsigned int imageCount) { - uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalNotifyTime + fgTotalInitTime; + uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalInitTime; char commaNum1[40]; char commaNum2[40]; printTime("total time", totalTime, totalTime); - fprintf(stderr, "total images loaded: %d (%d used prebinding)\n", imageCount, fgImagesWithUsedPrebinding); + 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); - fprintf(stderr, "total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1)); + dyld::log("total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1)); printTime("total rebase fixups time", fgTotalRebaseTime, totalTime); - fprintf(stderr, "total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1)); + dyld::log("total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1)); + if ( fgTotalBindSymbolsResolved != 0 ) { + uint32_t avgTimesTen = (fgTotalBindImageSearches * 10) / fgTotalBindSymbolsResolved; + uint32_t avgInt = fgTotalBindImageSearches / fgTotalBindSymbolsResolved; + uint32_t avgTenths = avgTimesTen - (avgInt*10); + dyld::log("total binding symbol lookups: %s, average images searched per symbol: %u.%u\n", + commatize(fgTotalBindSymbolsResolved, commaNum1), avgInt, avgTenths); + } printTime("total binding fixups time", fgTotalBindTime, totalTime); - fprintf(stderr, "total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2)); - printTime("total notify time time", fgTotalNotifyTime, 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); } @@ -952,7 +982,7 @@ void ImageLoader::addSuffix(const char* path, const char* suffix, char* result) } -void Segment::map(int fd, uint64_t offsetInFatWrapper, intptr_t slide, const ImageLoader::LinkContext& context) +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(); @@ -966,15 +996,26 @@ void Segment::map(int fd, uint64_t offsetInFatWrapper, intptr_t slide, const Ima if ( this->writeable() ) protection |= PROT_WRITE; } - void* loadAddress = mmap(requestedLoadAddress, size, protection, MAP_FILE | MAP_FIXED | MAP_PRIVATE, fd, fileOffset); - if ( loadAddress == ((void*)(-1)) ) - throwf("mmap() error %d at address=0x%08lX, size=0x%08lX in Segment::map() mapping %s", errno, (uintptr_t)requestedLoadAddress, (uintptr_t)size, this->getImage()->getPath()); - +#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 ) - fprintf(stderr, "%18s at %p->%p\n", this->getName(), loadAddress, (char*)loadAddress+this->getFileSize()-1); + 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::LinkContext& context) +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(); @@ -984,10 +1025,10 @@ void Segment::map(const void* memoryImage, intptr_t slide, const ImageLoader::Li throw "can't map segment"; if ( context.verboseMapping ) - fprintf(stderr, "%18s at %p->%p\n", this->getName(), (char*)loadAddress, (char*)loadAddress+this->getFileSize()-1); + dyld::log("%18s at %p->%p\n", this->getName(), (char*)loadAddress, (char*)loadAddress+this->getFileSize()-1); } -void Segment::setPermissions() +void Segment::setPermissions(const ImageLoader::LinkContext& context, const ImageLoader* image) { vm_prot_t protection = 0; if ( !this->unaccessible() ) { @@ -998,22 +1039,33 @@ void Segment::setPermissions() if ( this->writeable() ) protection |= VM_PROT_WRITE; } - vm_address_t addr = this->getActualLoadAddress(); + 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() +void Segment::tempWritable(const ImageLoader::LinkContext& context, const ImageLoader* image) { - vm_address_t addr = this->getActualLoadAddress(); + 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, VM_PROT_WRITE | VM_PROT_READ); + 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' : '.' ); + } } @@ -1027,18 +1079,20 @@ uintptr_t Segment::reserveAnAddressRange(size_t length, const ImageLoader::LinkC { vm_address_t addr = 0; vm_size_t size = length; - if ( context.slideAndPackDylibs ) { - addr = (fgNextNonSplitSegAddress - length) & (-4096); // page align - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, false /*use this range*/); - if ( r != KERN_SUCCESS ) - throw "out of address space"; - fgNextNonSplitSegAddress = addr; - } - else { - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, true /*find range*/); - if ( r != KERN_SUCCESS ) - throw "out of address space"; + // 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; } diff --git a/src/ImageLoader.h b/src/ImageLoader.h index 4a8a9d8..e7795d0 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 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,15 +28,43 @@ #include #include // struct mach_timebase_info +#include // struct mach_thread_self #include #include #include -#include "mach-o/dyld_gdb.h" +#include "mach-o/dyld_images.h" +#include "mach-o/dyld_priv.h" + + +#define SPLIT_SEG_SHARED_REGION_SUPPORT 0 +#define SPLIT_SEG_DYLIB_SUPPORT (__ppc__ || __i386__) +#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__) + + +// utilities +namespace dyld { + extern __attribute__((noreturn)) void throwf(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern void log(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern void warn(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern const char* mkstringf(const char* format, ...) __attribute__((format(printf, 1, 2))); +}; + + +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + const char*** NXArgvPtr; + const char*** environPtr; + const char** __prognamePtr; +}; -// utility -__attribute__((noreturn)) void throwf(const char* format, ...); // // ImageLoader is an abstract base class. To support loading a particular executable @@ -60,12 +88,9 @@ public: static const ReferenceFlags kWeakReference = 1; static const ReferenceFlags kTentativeDefinition = 2; - - enum BindingLaziness { kNonLazyOnly, kLazyAndNonLazy, kLazyOnly, kLazyOnlyNoDependents }; - enum InitializerRunning { kDontRunInitializers, kRunInitializers, kDontRunInitializersButTellObjc }; enum PrebindMode { kUseAllPrebinding, kUseSplitSegPrebinding, kUseAllButAppPredbinding, kUseNoPrebinding }; enum BindingOptions { kBindingNone, kBindingLazyPointers, kBindingNeverSetLazyPointers }; - enum SharedRegionMode { kUseSharedRegion, kUsePrivateSharedRegion, kDontUseSharedRegion }; + enum SharedRegionMode { kUseSharedRegion, kUsePrivateSharedRegion, kDontUseSharedRegion, kSharedRegionIsSharedCache }; struct Symbol; // abstact symbol @@ -73,37 +98,68 @@ public: uintptr_t address; size_t size; }; - typedef std::vector RegionsVector; + + struct RPathChain { + RPathChain(const RPathChain* n, std::vector* p) : next(n), paths(p) {}; + const RPathChain* next; + std::vector* paths; + }; + + struct DOFInfo { + void* dof; + const mach_header* imageHeader; + const char* imageShortName; + }; struct LinkContext { - ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const char* rpath[]); - uint32_t (*imageNotification)(ImageLoader* image, uint32_t startIndex); + ImageLoader* (*loadLibrary)(const char* libraryName, bool search, bool findDLL, const char* origin, const RPathChain* rpaths); void (*terminationRecorder)(ImageLoader* image); - bool (*flatExportFinder)(const char* name, const Symbol** sym, ImageLoader** image); - bool (*coalescedExportFinder)(const char* name, const Symbol** sym, ImageLoader** image); + bool (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); + bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); void (*undefinedHandler)(const char* name); +#if IMAGE_NOTIFY_SUPPORT void (*addImageNeedingNotification)(ImageLoader* image); - void (*notifyAdding)(std::vector& images); - void (*getAllMappedRegions)(RegionsVector&); + 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 (*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&); +#if SUPPORT_OLD_CRT_INITIALIZATION + void (*setRunInitialzersOldWay)(); +#endif BindingOptions bindingOptions; int argc; const char** argv; const char** envp; const char** apple; + const char* progname; + ProgramVars programVars; ImageLoader* mainExecutable; const char* imageSuffix; PrebindMode prebindUsage; SharedRegionMode sharedRegionMode; + bool dyldLoadedAtSameAddressNeededBySharedCache; + bool preFetchDisabled; bool prebinding; bool bindFlat; - bool slideAndPackDylibs; + bool linkingMainExecutable; bool verboseOpts; bool verboseEnv; bool verboseMapping; bool verboseRebase; bool verboseBind; bool verboseInit; + bool verboseDOF; bool verbosePrebinding; bool verboseWarnings; }; @@ -113,16 +169,15 @@ public: // link() takes a newly instantiated ImageLoader and does all // fixups needed to make it usable by the process - void link(const LinkContext& context, BindingLaziness mode, InitializerRunning inits, uint32_t notifyCount); + void link(const LinkContext& context, bool forceLazysBound, bool preflight, const RPathChain& loaderRPaths); // runInitializers() is normally called in link() but the main executable must // run crt code before initializers void runInitializers(const LinkContext& context); - // runNotification() is normally called in link() but the main executable must - // run crt code before initializers - void runNotification(const LinkContext& context, uint32_t notifyCount); - + // called after link() forces all lazy pointers to be bound + void bindAllLazyPointers(const LinkContext& context, bool recursive); + // used by dyld to see if a requested library is already loaded (might be symlink) bool statMatch(const struct stat& stat_buf) const; @@ -159,21 +214,21 @@ public: // even if image is deleted, leave segments mapped in void setLeaveMapped(); + // even if image is deleted, leave segments mapped in + bool leaveMapped() { return fLeaveMapped; } + // checks if the specifed address is within one of this image's segments virtual bool containsAddress(const void* addr) const; + // checks if the specifed address range overlaps any of this image's segments + virtual bool overlapsWithAddressRange(const void* start, const void* end) const; + // adds to list of ranges of memory mapped in - void addMappedRegions(RegionsVector& regions) const; + void getMappedRegions(MappedRegion*& region) const; // st_mtime from stat() on file - time_t lastModified(); + time_t lastModified() const; - // image should create prebound version of itself and return freespace remaining on disk - uint64_t reprebind(const LinkContext& context, time_t timestamp); - - // if 'commit', the prebound version should be swapped in, otherwise deleted - void reprebindCommit(const LinkContext& context, bool commit, bool unmapOld); - // only valid for main executables, returns a pointer its entry point virtual void* getMain() const = 0; @@ -183,17 +238,17 @@ public: // dyld API's require each image to have a slide (actual load address minus preferred load address) virtual uintptr_t getSlide() const = 0; - // dyld API's require each image to have a slide (actual load address minus preferred load address) - virtual const void* getBaseAddress() const = 0; + // last address mapped by image + virtual const void* getEnd() const = 0; // image has exports that participate in runtime coalescing 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, ImageLoader** foundIn) const = 0; + virtual const Symbol* findExportedSymbol(const char* name, const void* hint, 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 = 0; + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor=NULL) const = 0; // gets attributes of the specified exported symbol virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0; @@ -209,11 +264,11 @@ public: // find exported symbol as if imported by this image // used by RTLD_NEXT - virtual const Symbol* findExportedSymbolInDependentImages(const char* name, ImageLoader** foundIn) const; + virtual const Symbol* findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const; // find exported symbol as if imported by this image // used by RTLD_SELF - virtual const Symbol* findExportedSymbolInImageOrDependentImages(const char* name, ImageLoader** foundIn) const; + virtual const Symbol* findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const; // gets how many symbols are imported by this image virtual uint32_t getImportedSymbolCount() const = 0; @@ -242,15 +297,17 @@ public: // 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; @@ -263,9 +320,27 @@ public: // the image is prebindable and its prebinding is valid virtual bool usablePrebinding(const LinkContext& context) const = 0; - // used to implement refernce counting of images - void incrementReferenceCount(); - bool decrementReferenceCount(); + // add all RPATH paths this image contains + virtual void getRPaths(const LinkContext& context, std::vector&) const = 0; + + + + dyld_image_states getState() { return (dyld_image_states)fState; } + + // used to sort images bottom-up + int compare(const ImageLoader* right) const; + + void incrementDlopenReferenceCount() { ++fDlopenReferenceCount; } + + bool decrementDlopenReferenceCount(); + + void printReferenceCounts(); + + uint32_t referenceCount() const { return fDlopenReferenceCount + fStaticReferenceCount + fDynamicReferenceCount; } + + bool neverUnload() const { return fNeverUnload; } + + void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; } // triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast static void printStatistics(unsigned int imageCount); @@ -274,11 +349,41 @@ public: static void addSuffix(const char* path, const char* suffix, char* result); static uint32_t hash(const char*); - - + 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; } + bool isBeingRemoved() const { return fBeingRemoved; } + + +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: // abstract base class so all constructors protected @@ -289,42 +394,59 @@ protected: struct LibraryInfo { - uint64_t checksum; + uint32_t checksum; uint32_t minVersion; uint32_t maxVersion; }; struct DependentLibrary { - const char* name; ImageLoader* image; - LibraryInfo info; - bool required; - bool checksumMatches; - bool isReExported; - bool isSubFramework; + uint32_t required : 1, + checksumMatches : 1, + isReExported : 1, + isSubFramework : 1; }; - typedef void (*Initializer)(int argc, const char* argv[], const char* envp[],const char* apple[]); + struct DependentLibraryInfo { + const char* name; + LibraryInfo info; + bool required; + 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); // 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); + void recursiveLoadLibraries(const LinkContext& context, 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, BindingLaziness bindness); - void recursiveImageAnnouncement(const LinkContext& context, std::vector& newImages); - void recursiveImageNotification(const LinkContext& context, uint32_t addImageCount); - void recursiveInitialization(const LinkContext& context); + void recursiveBind(const LinkContext& context, bool forceLazysBound); + 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); - // map any segments this image has into memory and build fSegments - // this is called before doGetDependentLibraryCount so if metadata is in segments it is mapped in - virtual void instantiateSegments(const uint8_t* fileData) = 0; - // return how many libraries this image depends on virtual uint32_t doGetDependentLibraryCount() = 0; // fill in information about dependent libraries (array length is doGetDependentLibraryCount()) - virtual void doGetDependentLibraries(DependentLibrary libs[]) = 0; + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0; // called on images that are libraries, returns info about itself virtual LibraryInfo doGetLibraryInfo() = 0; @@ -333,14 +455,17 @@ protected: virtual void doRebase(const LinkContext& context) = 0; // do any symbolic fix ups in this image - virtual void doBind(const LinkContext& context, BindingLaziness bindness) = 0; + virtual void doBind(const LinkContext& context, bool forceLazysBound) = 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; // run any initialization routines in this image virtual void doInitialization(const LinkContext& context) = 0; - // write prebinding updates to mapped file fileToPrebind - virtual void doPrebinding(const LinkContext& context, time_t timestamp, uint8_t* fileToPrebind) = 0; - // return if this image has termination routines virtual bool needsTermination() = 0; @@ -368,18 +493,34 @@ protected: // in mach-o a parent library knows name of sub libraries it re-exports.. virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0; - // file has been reprebound on disk, unmap this file so original file is released - virtual void prebindUnmap(const LinkContext& context) = 0; + // set fState to dyld_image_state_memory_mapped + void setMapped(const LinkContext& context); + // 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; + + static uint32_t fgImagesWithUsedPrebinding; + static uint32_t fgImagesUsedFromSharedCache; + static uint32_t fgImagesRequiringNoFixups; static uint32_t fgTotalRebaseFixups; static uint32_t fgTotalBindFixups; + static uint32_t fgTotalBindSymbolsResolved; + static uint32_t fgTotalBindImageSearches; static uint32_t fgTotalLazyBindFixups; static uint32_t fgTotalPossibleLazyBindFixups; + static uint32_t fgTotalSegmentsMapped; + static uint64_t fgTotalBytesMapped; + static uint64_t fgTotalBytesPreFetched; static uint64_t fgTotalLoadLibrariesTime; static uint64_t fgTotalRebaseTime; static uint64_t fgTotalBindTime; - static uint64_t fgTotalNotifyTime; static uint64_t fgTotalInitTime; static uintptr_t fgNextSplitSegAddress; const char* fPath; @@ -388,31 +529,54 @@ protected: ino_t fInode; time_t fLastModified; uint64_t fOffsetInFatFile; - std::vector fSegments; DependentLibrary* fLibraries; uint32_t fLibrariesCount; uint32_t fPathHash; - uint32_t fReferenceCount; - bool fAllLibraryChecksumsAndLoadAddressesMatch; - bool fLeaveMapped; // when unloaded, leave image mapped in cause some other code may have pointers into it - + uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image + uint32_t fStaticReferenceCount; // count of images that have a fLibraries entry pointing to this image + uint32_t fDynamicReferenceCount; // count of images that have a fDynamicReferences entry pointer to this image + std::set* fDynamicReferences; // list of all images this image used because of a flat/coalesced lookup private: +#if RECURSIVE_INITIALIZER_LOCK + struct recursive_lock { + recursive_lock(mach_port_t t) : thread(t), count(0) {} + mach_port_t thread; + int count; + }; + 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); - uint64_t copyAndMap(const char* tempFile, uint8_t** fileToPrebind, uint64_t* fileToPrebindSize); - const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, std::set& dontSearchImages, ImageLoader** foundIn) const; - - - bool fHideSymbols; // ignore this image's exported symbols when linking other images - bool fMatchByInstallName;// look at image's install-path not its load path - bool fLibrariesLoaded; - bool fBased; - bool fBoundAllNonLazy; - bool fBoundAllLazy; - bool fAnnounced; - bool fInitialized; - uint16_t fNextAddImageIndex; + const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart, + const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const; + + + + uint16_t fDepth; + uint16_t fLoadOrder; + uint32_t fState : 8, + 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, + fPathOwnedByImage : 1; + +#if RECURSIVE_INITIALIZER_LOCK + recursive_lock* fInitializerRecursiveLock; +#else + uint32_t fInitializerLock; +#endif + static uint16_t fgLoadOrdinal; }; @@ -426,7 +590,6 @@ class Segment { public: virtual ~Segment() {} - virtual const ImageLoader* getImage() = 0; virtual const char* getName() = 0; virtual uintptr_t getSize() = 0; virtual uintptr_t getFileSize() = 0; @@ -436,10 +599,14 @@ public: virtual bool writeable() = 0; virtual bool executable() = 0; virtual bool unaccessible() = 0; - virtual bool hasFixUps() = 0; - virtual uintptr_t getActualLoadAddress() = 0; + virtual uintptr_t getActualLoadAddress(const ImageLoader*) = 0; virtual uintptr_t getPreferredLoadAddress() = 0; - virtual void setUnMapWhenDestructed(bool unmap) = 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 @@ -449,21 +616,24 @@ protected: 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 ImageLoader::LinkContext& context); - static uintptr_t fgNextNonSplitSegAddress; + static uintptr_t reserveAnAddressRange(size_t length, const class ImageLoader::LinkContext& context); + static uintptr_t fgNextPIEDylibAddress; private: - void map(int fd, uint64_t offsetInFatWrapper, intptr_t slide, const ImageLoader::LinkContext& context); - void map(const void* memoryImage, intptr_t slide, const ImageLoader::LinkContext& context); - void setPermissions(); - void tempWritable(); + 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 2d2cb44..fcf7cd7 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-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,9 +22,16 @@ * @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 @@ -35,6 +42,7 @@ #include #include #include +#include #if __ppc__ || __ppc64__ #include #endif @@ -42,15 +50,20 @@ #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 "mach-o/dyld_gdb.h" - -// no header for this yet, rdar://problem/3850825 -extern "C" void sys_icache_invalidate(void *, size_t); +#include "mach-o/dyld_images.h" // optimize strcmp for ppc #if __ppc__ @@ -59,6 +72,11 @@ extern "C" void sys_icache_invalidate(void *, size_t); #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 @@ -90,6 +108,9 @@ uint32_t ImageLoaderMachO::fgHintedBinaryTreeSearchs = 0; uint32_t ImageLoaderMachO::fgUnhintedBinaryTreeSearchs = 0; uint32_t ImageLoaderMachO::fgCountOfImagesWithWeakExports = 0; +#if __i386__ +uint32_t ImageLoaderMachO::fgReadOnlyImportSpinLock = 0; +#endif //#define LINKEDIT_USAGE_DEBUG 1 @@ -104,8 +125,11 @@ uint32_t ImageLoaderMachO::fgCountOfImagesWithWeakExports = 0; 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); - fprintf(stderr, "dyld: accessing page 0x%08lX in __LINKEDIT of %s\n", page, dyld::findImageContainingAddress(addr)->getPath()); } #endif @@ -118,20 +142,79 @@ void ImageLoaderMachO::init() fStrings = NULL; fDynamicInfo = NULL; fSlide = 0; - fIsSplitSeg = false; - fHasSubLibraries= false; - fHasSubUmbrella = false; - fDashInit = NULL; - fModInitSection = NULL; - fModTermSection = NULL; - fDATAdyld = NULL; - fImageNotifySection = NULL; fTwoLevelHints = NULL; fDylibID = NULL; - fReExportThruFramework = NULL; +#if TEXT_RELOC_SUPPORT fTextSegmentWithFixups = NULL; +#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; +#endif +} + +// create image for main executable +ImageLoaderMachO::ImageLoaderMachO(const struct mach_header* mh, uintptr_t slide, const char* path, const LinkContext& context) + : ImageLoader(path) +{ + // 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(); + + // 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()); + } + } } + // 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) @@ -145,12 +228,68 @@ ImageLoaderMachO::ImageLoaderMachO(const char* moduleName, const struct mach_hea // create segments this->instantiateSegments((const uint8_t*)mh); - // map segments - if ( mh->filetype != MH_EXECUTE ) + // map segments + if ( mh->filetype == MH_EXECUTE ) { + throw "can't load another MH_EXECUTE"; + } + else { ImageLoader::mapSegments((const void*)mh, len, context); + } + + // 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); + +} + +// 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(); + + // 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()); + } + } + // 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); } @@ -185,16 +324,62 @@ ImageLoaderMachO::ImageLoaderMachO(const char* path, int fd, const uint8_t first // 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); + } + + 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); } @@ -204,18 +389,114 @@ void ImageLoaderMachO::instantiateSegments(const uint8_t* fileData) const uint32_t cmd_count = ((macho_header*)fileData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fileData[sizeof(macho_header)]; - // construct Segment object for each LC_SEGMENT cmd and add to list + // 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 ) { - if ( (((struct macho_segment_command*)cmd)->vmsize != 0) || !fIsSplitSeg ) - fSegments.push_back(new SegmentMachO((struct macho_segment_command*)cmd, this, fileData)); + // ignore zero-sized segments + if ( ((struct macho_segment_command*)cmd)->vmsize != 0 ) + ++segCount; + } + 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); } } +void ImageLoaderMachO::adjustSegments() +{ + // tell each segment where is load command is finally mapped + 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; + } + } + 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); + } + + // 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 { @@ -270,9 +551,12 @@ bool ImageLoaderMachO::needsCoalescing() const return ( (mh->flags & MH_BINDS_TO_WEAK) != 0 ); } -#if !__LP64__ // split segs not supported for 64-bits -#if 1 // hack until kernel headers and glue are in system + + + + +// hack until kernel headers and glue are in system struct _shared_region_mapping_np { mach_vm_address_t address; mach_vm_size_t size; @@ -285,6 +569,7 @@ struct _shared_region_range_np { 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). @@ -304,13 +589,13 @@ _shared_region_map_file_np( 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 { - //fprintf(stderr, "%s(%i, %u, %8p, %8p)\n", __func__, fd, regionCount, regions, slide); + //dyld::log("%s(%i, %u, %8p, %8p)\n", __func__, fd, regionCount, regions, slide); //for ( unsigned int i=0; i < regionCount; ++i) { - // fprintf(stderr, "\taddress=0x%08llX, size=0x%08llX\n", regions[i].address, regions[i].size); + // 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) -// fprintf(stderr, "%s(%i, %u, %8p, %8p) errno=%i (%s)\n", __func__, fd, regionCount, regions, slide, errno, strerror(errno)); +// 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. @@ -321,21 +606,14 @@ _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 { - //fprintf(stderr, "%s(%u, %8p)\n", __func__, rangeCount, ranges); + //dyld::log("%s(%u, %8p)\n", __func__, rangeCount, ranges); int r = syscall(300, rangeCount, ranges); // if(0 != r) -// fprintf(stderr, "%s(%u, %8p) errno=%i (%s)\n", __func__, rangeCount, ranges, errno, strerror(errno)); +// dyld::log("%s(%u, %8p) errno=%i (%s)\n", __func__, rangeCount, ranges, errno, strerror(errno)); return r; } #define KERN_SHREG_PRIVATIZABLE 54 -#endif // hack until kernel headers and glue are in system -static uintptr_t sNextAltLoadAddress -#if __ppc_ - = 0xC0000000; -#else - = 0; -#endif static int _shared_region_map_file_with_mmap( @@ -359,8 +637,8 @@ _shared_region_map_file_with_mmap( if ( regions[i].init_prot & VM_PROT_WRITE ) protection |= PROT_WRITE; off_t offset = regions[i].file_offset; - //fprintf(stderr, "mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath); - mmapAddress = mmap(mmapAddress, size, protection, MAP_FILE | MAP_FIXED | MAP_PRIVATE, fd, 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"; } @@ -388,6 +666,56 @@ hasSharedRegionMapFile(void) return 0 != value; } +#endif // SPLIT_SEG_SHARED_REGION_SUPPORT + + +#if SPLIT_SEG_DYLIB_SUPPORT +unsigned int +ImageLoaderMachO::getExtraZeroFillEntriesCount() +{ + // 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; + } + } +} + int ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd, uint64_t offsetInFat, @@ -395,7 +723,14 @@ ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd, uint64_t fileLen, const LinkContext& context) { - const unsigned int segmentCount = fSegments.size(); + static uintptr_t sNextAltLoadAddress + #if __ppc_ + = 0xC0000000; + #else + = 0; + #endif + + const unsigned int segmentCount = fSegmentsArrayCount; const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); const unsigned int regionCount = segmentCount+extraZeroFillEntries; _shared_region_mapping_np regions[regionCount]; @@ -435,8 +770,8 @@ ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd, 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 ) { - // do nothing vm_allocate() zero-fills by default + 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); @@ -449,8 +784,8 @@ ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd, if ( regions[i].init_prot & VM_PROT_WRITE ) protection |= PROT_WRITE; off_t offset = regions[i].file_offset; - //fprintf(stderr, "mmap(%p, 0x%08lX, block=0x%08X, %s\n", mmapAddress, size, biggestDiff, fPath); - mmapAddress = mmap(mmapAddress, size, protection, MAP_FILE | MAP_FIXED | MAP_PRIVATE, fd, 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"; } @@ -461,19 +796,19 @@ ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd, // logging if ( context.verboseMapping ) { - fprintf(stderr, "dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide, this->getPath()); + 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 = fSegments[segIndex]; + Segment* seg = &fSegmentsArray[segIndex]; const _shared_region_mapping_np* entry = ®ions[entryIndex]; if ( (entry->init_prot & VM_PROT_ZF) == 0 ) - fprintf(stderr, "%18s at 0x%08lX->0x%08lX\n", - seg->getName(), seg->getActualLoadAddress(), seg->getActualLoadAddress()+seg->getFileSize()-1); + 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; - fprintf(stderr, "%18s at 0x%08lX->0x%08lX (zerofill)\n", - seg->getName(), (uintptr_t)(seg->getActualLoadAddress() + segOffset), (uintptr_t)(seg->getActualLoadAddress() + segOffset + nextEntry->size - 1)); + 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; } } @@ -486,10 +821,14 @@ ImageLoaderMachO::sharedRegionMapFilePrivateOutside(int fd, void ImageLoaderMachO::mapSegments(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 ImageLoader::mapSegments(fd, offsetInFat, lenInFat, fileLen, context); + +#if SPLIT_SEG_SHARED_REGION_SUPPORT enum SharedRegionState { kSharedRegionStartState = 0, - kSharedRegionLoadFileState, kSharedRegionMapFileState, kSharedRegionMapFilePrivateState, kSharedRegionMapFilePrivateMMapState, @@ -497,42 +836,27 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF }; static SharedRegionState sSharedRegionState = kSharedRegionStartState; - // non-split segment libraries handled by super class - if ( !fIsSplitSeg ) - return ImageLoader::mapSegments(fd, offsetInFat, lenInFat, fileLen, context); - if ( kSharedRegionStartState == sSharedRegionState ) { if ( hasSharedRegionMapFile() ) { - if ( context.slideAndPackDylibs ) { - sharedRegionMakePrivate(context); - // remove underlying submap and block out 0x90000000 to 0xAFFFFFFF - vm_address_t addr = (vm_address_t)0x90000000; - vm_deallocate(mach_task_self(), addr, 0x20000000); - vm_allocate(mach_task_self(), &addr, 0x20000000, false); - sSharedRegionState = kSharedRegionMapFilePrivateMMapState; - } - else if ( context.sharedRegionMode == kUsePrivateSharedRegion ) { + if ( context.sharedRegionMode == kUsePrivateSharedRegion ) { sharedRegionMakePrivate(context); sSharedRegionState = kSharedRegionMapFilePrivateState; } else if ( context.sharedRegionMode == kDontUseSharedRegion ) { sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; } + else if ( context.sharedRegionMode == kSharedRegionIsSharedCache ) { + sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; + } else { sSharedRegionState = kSharedRegionMapFileState; } } else { - sSharedRegionState = kSharedRegionLoadFileState; - } - } - - if ( kSharedRegionLoadFileState == sSharedRegionState ) { - if ( 0 != sharedRegionLoadFile(fd, offsetInFat, lenInFat, fileLen, context) ) { sSharedRegionState = kSharedRegionMapFilePrivateOutsideState; } } - else + if ( kSharedRegionMapFileState == sSharedRegionState ) { if ( 0 != sharedRegionMapFile(fd, offsetInFat, lenInFat, fileLen, context) ) { sharedRegionMakePrivate(context); @@ -551,77 +875,37 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF throw "mapping error"; } } -} - -unsigned int -ImageLoaderMachO::getExtraZeroFillEntriesCount() -{ - // calculate mapping entries - const unsigned int segmentCount = fSegments.size(); - unsigned int extraZeroFillEntries = 0; - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; - if ( seg->hasTrailingZeroFill() ) - ++extraZeroFillEntries; +#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"; } - - return extraZeroFillEntries; +#endif } +#endif // SPLIT_SEG_DYLIB_SUPPORT -void -ImageLoaderMachO::initMappingTable(uint64_t offsetInFat, - _shared_region_mapping_np *mappingTable) -{ - unsigned int segmentCount = fSegments.size(); - for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = fSegments[segIndex]; - _shared_region_mapping_np* entry = &mappingTable[entryIndex]; - entry->address = seg->getActualLoadAddress(); - 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; - } - } -} - -int -ImageLoaderMachO::sharedRegionMakePrivate(const LinkContext& context) + +#if SPLIT_SEG_SHARED_REGION_SUPPORT +int ImageLoaderMachO::sharedRegionMakePrivate(const LinkContext& context) { if ( context.verboseMapping ) - fprintf(stderr, "dyld: making shared regions private\n"); + dyld::log("dyld: making shared regions private\n"); // shared mapping failed, so make private copy of shared region and try mapping private - RegionsVector allRegions; - context.getAllMappedRegions(allRegions); - std::vector<_shared_region_range_np> splitSegRegions; - const unsigned int allRegiontCount = allRegions.size(); - for(unsigned int i=0; i < allRegiontCount; ++i){ - MappedRegion region = allRegions[i]; - uint8_t highByte = region.address >> 28; + 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 = region.address; - splitRegion.size = region.size; - splitSegRegions.push_back(splitRegion); + splitRegion.address = p->address; + splitRegion.size = p->size; + *sp++ = splitRegion; } } - int result = _shared_region_make_private_np(splitSegRegions.size(), &splitSegRegions[0]); + 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; @@ -635,7 +919,7 @@ ImageLoaderMachO::sharedRegionMapFile(int fd, const LinkContext& context) { // build table of segments to map - const unsigned int segmentCount = fSegments.size(); + const unsigned int segmentCount = fSegmentsArrayCount; const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; _shared_region_mapping_np mappingTable[mappingTableCount]; @@ -649,20 +933,21 @@ ImageLoaderMachO::sharedRegionMapFile(int fd, if(NULL != slidep && 0 != *slidep) { // update with actual load addresses } + this->setNeverUnload(); if ( context.verboseMapping ) { - fprintf(stderr, "dyld: Mapping split-seg shared %s\n", this->getPath()); + dyld::log("dyld: Mapping split-seg shared %s\n", this->getPath()); for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = fSegments[segIndex]; + Segment* seg = &fSegmentsArray[segIndex]; const _shared_region_mapping_np* entry = &mappingTable[entryIndex]; if ( (entry->init_prot & VM_PROT_ZF) == 0 ) - fprintf(stderr, "%18s at 0x%08lX->0x%08lX\n", - seg->getName(), seg->getActualLoadAddress(), seg->getActualLoadAddress()+seg->getFileSize()-1); + 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; - fprintf(stderr, "%18s at 0x%08lX->0x%08lX\n", - seg->getName(), (uintptr_t)(seg->getActualLoadAddress() + segOffset), (uintptr_t)(seg->getActualLoadAddress() + segOffset + nextEntry->size - 1)); + 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; } } @@ -681,33 +966,8 @@ ImageLoaderMachO::sharedRegionMapFilePrivate(int fd, const LinkContext& context, bool usemmap) { - const unsigned int segmentCount = fSegments.size(); - - // adjust base address of segments to pack next to last dylib - if ( context.slideAndPackDylibs ) { - uintptr_t lowestReadOnly = (uintptr_t)(-1); - uintptr_t lowestWritable = (uintptr_t)(-1); - for(unsigned int segIndex=0; segIndex < segmentCount; ++segIndex){ - Segment* seg = fSegments[segIndex]; - uintptr_t segEnd = seg->getActualLoadAddress(); - if ( seg->writeable() ) { - if ( segEnd < lowestWritable ) - lowestWritable = segEnd; - } - else { - if ( segEnd < lowestReadOnly ) - lowestReadOnly = segEnd; - } - } - uintptr_t baseAddress; - if ( lowestWritable - 256*1024*1024 < lowestReadOnly ) - baseAddress = lowestWritable - 256*1024*1024; - else - baseAddress = lowestReadOnly; - // record that we want dylb slid to fgNextSplitSegAddress - this->setSlide(fgNextSplitSegAddress - baseAddress); - } - + const unsigned int segmentCount = fSegmentsArrayCount; + // build table of segments to map const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; @@ -720,150 +980,50 @@ ImageLoaderMachO::sharedRegionMapFilePrivate(int fd, if ( usemmap ) r = _shared_region_map_file_with_mmap(fd, mappingTableCount, mappingTable); else - r = _shared_region_map_file_np(fd, mappingTableCount, mappingTable, context.slideAndPackDylibs ? NULL : &slide); + 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 ) - fprintf(stderr, "dyld: Mapping split-seg un-shared %s\n", this->getPath()); + dyld::log("dyld: Mapping split-seg un-shared %s\n", this->getPath()); else - fprintf(stderr, "dyld: Mapping split-seg un-shared slid by 0x%08llX %s\n", slide, this->getPath()); + 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 = fSegments[segIndex]; + Segment* seg = &fSegmentsArray[segIndex]; const _shared_region_mapping_np* entry = &mappingTable[entryIndex]; if ( (entry->init_prot & VM_PROT_ZF) == 0 ) - fprintf(stderr, "%18s at 0x%08lX->0x%08lX\n", - seg->getName(), seg->getActualLoadAddress(), seg->getActualLoadAddress()+seg->getFileSize()-1); + 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; - fprintf(stderr, "%18s at 0x%08lX->0x%08lX (zerofill)\n", - seg->getName(), (uintptr_t)(seg->getActualLoadAddress() + segOffset), (uintptr_t)(seg->getActualLoadAddress() + segOffset + nextEntry->size - 1)); + 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 ( context.slideAndPackDylibs ) { - // calculate where next split-seg dylib can load - uintptr_t largestReadOnly = 0; - uintptr_t largestWritable = 0; - for (unsigned int segIndex=0; segIndex < segmentCount; ++segIndex) { - Segment* seg = fSegments[segIndex]; - uintptr_t segEnd = seg->getActualLoadAddress()+seg->getSize(); - segEnd = (segEnd+4095) & (-4096); // page align - if ( seg->writeable() ) { - if ( segEnd > largestWritable ) - largestWritable = segEnd; - } - else { - if ( segEnd > largestReadOnly ) - largestReadOnly = segEnd; - } - } - if ( largestWritable - 256*1024*1024 > largestReadOnly ) - fgNextSplitSegAddress = largestWritable - 256*1024*1024; - else - fgNextSplitSegAddress = largestReadOnly; - } } - if ( context.slideAndPackDylibs && (r != 0) ) - throwf("can't rebase split-seg dylib %s because shared_region_map_file_np() returned %d", this->getPath(), r); + 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; } - -int -ImageLoaderMachO::sharedRegionLoadFile(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) +void +ImageLoaderMachO::initMappingTable(uint64_t offsetInFat, + sf_mapping *mappingTable, + uintptr_t baseAddress) { - - // map in split segment file at random address, then tell kernel to share it - void* loadAddress = 0; - loadAddress = mmap(NULL, fileLen, PROT_READ, MAP_FILE, fd, 0); - if ( loadAddress == ((void*)(-1)) ) - throw "mmap error"; - - // calculate mapping entries - const unsigned int segmentCount = fSegments.size(); - unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); - - // build table of segments to map - const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; - const uintptr_t baseAddress = fSegments[0]->getPreferredLoadAddress(); - sf_mapping mappingTable[mappingTableCount]; - initMappingTable(offsetInFat, mappingTable, baseAddress); - - - // use load_shared_file() to map all segments at once - int flags = 0; // might need to set NEW_LOCAL_SHARED_REGIONS on first use - static bool firstTime = true; - if ( firstTime ) { - // when NEW_LOCAL_SHARED_REGIONS bit is set, this process will get is own shared region - // this is used by Xcode to prevent development libraries from polluting the global shared segment - if ( context.sharedRegionMode == kUsePrivateSharedRegion ) - flags |= NEW_LOCAL_SHARED_REGIONS; - firstTime = false; - } - - caddr_t base_address = (caddr_t)baseAddress; - kern_return_t r; - r = load_shared_file( (char*)fPath, // path of file to map shared - (char*)loadAddress, // beginning of local copy of sharable pages in file - fileLen, // end of shareable pages in file - &base_address, // beginning of address range to map - mappingTableCount, // number of entres in array of sf_mapping - mappingTable, // the array of sf_mapping - &flags); // in/out flags - if ( 0 != r ) { - // try again but tell kernel it is ok to slide - flags |= ALTERNATE_LOAD_SITE; - r = load_shared_file((char*)fPath,(char*)loadAddress, fileLen, &base_address, - mappingTableCount, mappingTable, &flags); - } - - // unmap file from random address now that they are (hopefully) mapped into the shared region - munmap(loadAddress, fileLen); - - if ( 0 == r ) { - if ( base_address != (caddr_t)baseAddress ) - this->setSlide((uintptr_t)base_address - baseAddress); - if ( context.verboseMapping ) { - if ( base_address != (caddr_t)baseAddress ) - fprintf(stderr, "dyld: Mapping split-seg load_shared_alt_region %s\n", this->getPath()); - else - fprintf(stderr, "dyld: Mapping split-seg load_shared %s\n", this->getPath()); - for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = fSegments[segIndex]; - const sf_mapping* entry = &mappingTable[entryIndex]; - if ( (entry->protection & VM_PROT_ZF) == 0 ) - fprintf(stderr, "%18s at 0x%08lX->0x%08lX\n", - seg->getName(), seg->getActualLoadAddress(), seg->getActualLoadAddress()+seg->getFileSize()-1); - if ( entryIndex < (mappingTableCount-1) ) { - const sf_mapping* nextEntry = &mappingTable[entryIndex+1]; - if ( (nextEntry->protection & VM_PROT_ZF) != 0 ) { - fprintf(stderr, "%18s at 0x%08lX->0x%08lX\n", - seg->getName(), (uintptr_t)(nextEntry->mapping_offset + base_address), (uintptr_t)(nextEntry->mapping_offset + base_address + nextEntry->size - 1)); - ++entryIndex; - } - } - } - } - } - return r; -} -void -ImageLoaderMachO::initMappingTable(uint64_t offsetInFat, - sf_mapping *mappingTable, - uintptr_t baseAddress) -{ - unsigned int segmentCount = fSegments.size(); + unsigned int segmentCount = fSegmentsArrayCount; for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ - Segment* seg = fSegments[segIndex]; + Segment* seg = &fSegmentsArray[segIndex]; sf_mapping* entry = &mappingTable[entryIndex]; entry->mapping_offset = seg->getPreferredLoadAddress() - baseAddress; entry->size = seg->getFileSize(); @@ -890,7 +1050,8 @@ ImageLoaderMachO::initMappingTable(uint64_t offsetInFat, } } -#endif // !__LP64__ split segs not supported for 64-bits +#endif // SPLIT_SEG_SHARED_REGION_SUPPORT + void ImageLoaderMachO::setSlide(intptr_t slide) @@ -901,27 +1062,42 @@ void ImageLoaderMachO::setSlide(intptr_t slide) void ImageLoaderMachO::parseLoadCmds() { // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i){ - Segment* seg = fSegments[i]; + 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() - seg->getFileOffset()); + 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 ( seg->hasFixUps() ) + 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()); + 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; + // keep count of images used in shared cache + if ( fInSharedCache ) + ++fgImagesUsedFromSharedCache; + // 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)]; @@ -942,34 +1118,34 @@ void ImageLoaderMachO::parseLoadCmds() fHasSubUmbrella = true; break; case LC_SUB_FRAMEWORK: - { - const struct sub_framework_command* subf = (struct sub_framework_command*)cmd; - fReExportThruFramework = (char*)cmd + subf->umbrella.offset; - } + fInUmbrella = true; break; case LC_SUB_LIBRARY: fHasSubLibraries = true; break; case LC_ROUTINES_COMMAND: - fDashInit = (struct macho_routines_command*)cmd; + 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 ) - fModInitSection = sect; + fHasInitializers = true; else if ( type == S_MOD_TERM_FUNC_POINTERS ) - fModTermSection = sect; - else if ( isDataSeg && (strcmp(sect->sectname, "__dyld") == 0) ) { - fDATAdyld = sect; - } + fHasTerminators = true; + else if ( type == S_DTRACE_DOF ) + fHasDOFSections = true; +#if IMAGE_NOTIFY_SUPPORT else if ( isDataSeg && (strcmp(sect->sectname, "__image_notify") == 0) ) - fImageNotifySection = sect; + fHasImageNotifySection = true; +#endif } } break; @@ -981,12 +1157,14 @@ void ImageLoaderMachO::parseLoadCmds() 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 ) - throwf("unknown required load command 0x%08X", cmd->cmd); + dyld::throwf("unknown required load command 0x%08X", cmd->cmd); } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } @@ -1006,23 +1184,33 @@ const char* ImageLoaderMachO::getInstallPath() const // 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 ( fReExportThruFramework != NULL ) { - // 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], fReExportThruFramework) == 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(fReExportThruFramework)+1]; - strcpy(reexportAndSuffix, fReExportThruFramework); - strcat(reexportAndSuffix, context.imageSuffix); - if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 ) - return true; + 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; @@ -1109,7 +1297,8 @@ bool ImageLoaderMachO::hasSubLibrary(const LinkContext& context, const ImageLoad return false; } - + + void* ImageLoaderMachO::getMain() const { const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; @@ -1121,16 +1310,16 @@ void* ImageLoaderMachO::getMain() const { #if __ppc__ const ppc_thread_state_t* registers = (ppc_thread_state_t*)(((char*)cmd) + 16); - return (void*)registers->srr0; + return (void*)(registers->srr0 + fSlide); #elif __ppc64__ const ppc_thread_state64_t* registers = (ppc_thread_state64_t*)(((char*)cmd) + 16); - return (void*)registers->srr0; + return (void*)(registers->srr0 + fSlide); #elif __i386__ const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); - return (void*)registers->eip; + return (void*)(registers->eip + fSlide); #elif __x86_64__ const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); - return (void*)registers->rip; + return (void*)(registers->rip + fSlide); #else #warning need processor specific code #endif @@ -1153,6 +1342,7 @@ uint32_t ImageLoaderMachO::doGetDependentLibraryCount() switch (cmd->cmd) { case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: ++count; break; } @@ -1161,7 +1351,7 @@ uint32_t ImageLoaderMachO::doGetDependentLibraryCount() return count; } -void ImageLoaderMachO::doGetDependentLibraries(DependentLibrary libs[]) +void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) { uint32_t index = 0; const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; @@ -1171,18 +1361,17 @@ void ImageLoaderMachO::doGetDependentLibraries(DependentLibrary libs[]) 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; - DependentLibrary* lib = &libs[index++]; + DependentLibraryInfo* lib = &libs[index++]; lib->name = (char*)cmd + dylib->dylib.name.offset; //lib->name = strdup((char*)cmd + dylib->dylib.name.offset); - lib->image = NULL; 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_DYLIB); - lib->checksumMatches = false; - lib->isReExported = false; + lib->required = (cmd->cmd != LC_LOAD_WEAK_DYLIB); + lib->reExported = (cmd->cmd == LC_REEXPORT_DYLIB); } break; } @@ -1206,34 +1395,95 @@ ImageLoader::LibraryInfo ImageLoaderMachO::doGetLibraryInfo() return info; } +void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector& paths) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (unsigned long 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()); + break; + } + char resolvedPath[PATH_MAX]; + if ( realpath(this->getPath(), resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + char* addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[13]); + else + strcpy(newRealPath, &path[13]); + path = strdup(newRealPath); + } + } + else if ( strncmp(path, "@executable_path/", 17) == 0 ) { + if ( issetugid() ) { + dyld::warn("LC_RPATH %s in %s being ignored in setuid program because of @executable_path\n", path, this->getPath()); + break; + } + char resolvedPath[PATH_MAX]; + if ( realpath(context.mainExecutable->getPath(), resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + char* addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newRealPath, &path[17]); + path = strdup(newRealPath); + } + } + else if ( (path[0] != '/') && issetugid() ) { + dyld::warn("LC_RPATH %s in %s being ignored in setuid 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 (std::vector::iterator it=fSegments.begin(); it != fSegments.end(); ++it) { - if ( (*it)->writeable() ) { - return (*it)->getActualLoadAddress(); - } + 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() { -#if __x86_64__ - // r_address is offset from first writable segment - return getFirstWritableSegmentAddress(); -#endif + // r_address is either an offset from the first segment address + // or from the first writable segment address #if __ppc__ || __i386__ - if ( fIsSplitSeg ) { - // in split segment libraries r_address is offset from first writable segment + 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 - - // in non-split segment libraries r_address is offset from first segment - return fSegments[0]->getActualLoadAddress(); } + #if __ppc__ static inline void otherRelocsPPC(uintptr_t* locationToFix, uint8_t relocationType, uint16_t otherHalf, uintptr_t slide) { @@ -1257,7 +1507,7 @@ static inline void otherRelocsPPC(uintptr_t* locationToFix, uint8_t relocationTy instruction->immediateValue = temp >> 16; } //uint32_t after = *((uint32_t*)locationToFix); - //fprintf(stderr, "dyld: ppc fixup %0p type %d from 0x%08X to 0x%08X\n", locationToFix, relocationType, before, after); + //dyld::log("dyld: ppc fixup %0p type %d from 0x%08X to 0x%08X\n", locationToFix, relocationType, before, after); } #endif @@ -1295,34 +1545,27 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) { // if prebound and loaded at prebound address, then no need to rebase if ( this->usablePrebinding(context) ) { - // skip rebasing cause prebound and prebinding not disabled + // skip rebasing because prebinding is valid ++fgImagesWithUsedPrebinding; // bump totals for statistics return; } - // update_prebinding fails if a prebound dylib depends on a non-prebound dylib - // In the unusual case that we find a prebound dylib dependent on un-prebound dylib and we are running - // update_prebinding, we want the link of the prebound dylib to fail so that it will be excluded from - // the list of dylibs to be re-written. - if ( context.prebinding && !this->isPrebindable() ) - throwf("dylib not prebound: %s", this->getPath()); - // print why prebinding was not used if ( context.verbosePrebinding ) { if ( !this->isPrebindable() ) { - fprintf(stderr, "dyld: image not prebound, so could not use prebinding in %s\n", this->getPath()); + dyld::log("dyld: image not prebound, so could not use prebinding in %s\n", this->getPath()); } else if ( fSlide != 0 ) { - fprintf(stderr, "dyld: image slid, so could not use prebinding in %s\n", this->getPath()); + dyld::log("dyld: image slid, so could not use prebinding in %s\n", this->getPath()); } else if ( !this->allDependentLibrariesAsWhenPreBound() ) { - fprintf(stderr, "dyld: dependent libraries changed, so could not use prebinding in %s\n", this->getPath()); + dyld::log("dyld: dependent libraries changed, so could not use prebinding in %s\n", this->getPath()); } else if ( !this->usesTwoLevelNameSpace() ){ - fprintf(stderr, "dyld: image uses flat-namespace so, parts of prebinding ignored %s\n", this->getPath()); + dyld::log("dyld: image uses flat-namespace so, parts of prebinding ignored %s\n", this->getPath()); } else { - fprintf(stderr, "dyld: environment variable disabled use of prebinding in %s\n", this->getPath()); + dyld::log("dyld: environment variable disabled use of prebinding in %s\n", this->getPath()); } } @@ -1330,24 +1573,35 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) 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 and we got here, then prebinding is not valid, so reset all lazy pointers - if ( this->isPrebindable() ) + // 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); #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 ) return; +#if TEXT_RELOC_SUPPORT // if there are __TEXT fixups, temporarily make __TEXT writable if ( fTextSegmentWithFixups != NULL ) - fTextSegmentWithFixups->tempWritable(); - + fTextSegmentWithFixups->tempWritable(context, this); +#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 ) @@ -1359,8 +1613,7 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) if ( reloc->r_extern != 0 ) throw "extern relocation found with local relocations"; *((uintptr_t*)(reloc->r_address + relocBase)) += slide; - #endif - #if __ppc__ || __ppc64__ || __i386__ + #else if ( (reloc->r_address & R_SCATTERED) == 0 ) { if ( reloc->r_symbolnum == R_ABS ) { // ignore absolute relocations @@ -1403,15 +1656,14 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) ++reloc; // these relocations come in pairs, get next one otherRelocsPPC(locationToFix, sreloc->r_type, reloc->r_address, slide); break; - #endif - #if __ppc__ case PPC_RELOC_PB_LA_PTR: // do nothing break; #elif __ppc64__ case PPC_RELOC_PB_LA_PTR: - // these should never exist in ppc64, but the first ld64 had a bug and created them - *locationToFix = sreloc->r_value + slide; + // 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: @@ -1426,22 +1678,23 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) throw "bad local scattered relocation length"; } } - #endif + #endif // x86_64 } +#if TEXT_RELOC_SUPPORT // if there were __TEXT fixups, restore write protection if ( fTextSegmentWithFixups != NULL ) { - fTextSegmentWithFixups->setPermissions(); - sys_icache_invalidate((void*)fTextSegmentWithFixups->getActualLoadAddress(), fTextSegmentWithFixups->getSize()); + fTextSegmentWithFixups->setPermissions(context,this); + sys_icache_invalidate((void*)fTextSegmentWithFixups->getActualLoadAddress(this), fTextSegmentWithFixups->getSize()); } - +#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 struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const { int32_t high = symbolCount-1; int32_t mid = hintIndex; @@ -1454,6 +1707,9 @@ const struct macho_nlist* ImageLoaderMachO::binarySearchWithToc(const char* key, 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; @@ -1479,9 +1735,15 @@ const struct macho_nlist* ImageLoaderMachO::binarySearchWithToc(const char* key, return NULL; } -const struct macho_nlist* ImageLoaderMachO::binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) +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]; @@ -1507,7 +1769,7 @@ const struct macho_nlist* ImageLoaderMachO::binarySearch(const char* key, const return NULL; } -const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, const void* hint, bool searchReExports, ImageLoader** foundIn) const +const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, const void* hint, bool searchReExports, const ImageLoader** foundIn) const { const struct macho_nlist* sym = NULL; const struct twolevel_hint* theHint = (struct twolevel_hint*)hint; @@ -1596,15 +1858,22 @@ const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name } } - + return NULL; } -uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym) const + + +uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor) const { - const struct macho_nlist* nlistSym = (const struct macho_nlist*)sym; - return nlistSym->n_value + fSlide; + return this->getSymbolAddress((const struct macho_nlist*)sym, requestor, context); +} + +uintptr_t ImageLoaderMachO::getSymbolAddress(const struct macho_nlist* sym, const ImageLoader* requestor, const LinkContext& context) const +{ + uintptr_t result = sym->n_value + fSlide; + return result; } ImageLoader::DefinitionFlags ImageLoaderMachO::getExportedSymbolInfo(const Symbol* sym) const @@ -1751,33 +2020,38 @@ bool ImageLoaderMachO::symbolRequiresCoalescing(const struct macho_nlist* symbol static void __attribute__((noreturn)) throwSymbolNotFound(const char* symbol, const char* referencedFrom, const char* expectedIn) { - const char* formatString = "Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n"; - char buf[strlen(symbol)+strlen(referencedFrom)+strlen(expectedIn)+strlen(formatString)]; - sprintf(buf, formatString, symbol, referencedFrom, expectedIn); - throw strdup(buf); // this is a leak if exception doesn't halt program + 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, ImageLoader** foundIn) +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 = undefinedSymbol->n_value + this->fSlide; + uintptr_t addr = this->getSymbolAddress(undefinedSymbol, this, context); *foundIn = this; return addr; } const Symbol* sym; - if ( context.flatExportFinder(symbolName, &sym, foundIn) ) - return (*foundIn)->getExportedSymbolAddress(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); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); } if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { // definition can't be found anywhere @@ -1787,15 +2061,26 @@ uintptr_t ImageLoaderMachO::resolveUndefined(const LinkContext& context, const s throwSymbolNotFound(symbolName, this->getPath(), "flat namespace"); } else { - // symbol requires searching images with coalesced symbols + // 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) ) - return (*foundIn)->getExportedSymbolAddress(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"); - //fprintf(stderr, "dyld: coalesced symbol %s not found in any coalesced image, falling back to two-level lookup", symbolName); + //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; @@ -1811,13 +2096,13 @@ uintptr_t ImageLoaderMachO::resolveUndefined(const LinkContext& context, const s // flat lookup const Symbol* sym; if ( context.flatExportFinder(symbolName, &sym, foundIn) ) - return (*foundIn)->getExportedSymbolAddress(sym); + 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); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); throwSymbolNotFound(symbolName, this->getPath(), "dynamic lookup"); } @@ -1830,11 +2115,12 @@ uintptr_t ImageLoaderMachO::resolveUndefined(const LinkContext& context, const s } } else { - throw "corrupt binary, library ordinal too big"; + 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 ) { - //fprintf(stderr, "resolveUndefined(%s) in %s\n", symbolName, this->getPath()); + //dyld::log("resolveUndefined(%s) in %s\n", symbolName, this->getPath()); throw "symbol not found"; } @@ -1851,12 +2137,12 @@ uintptr_t ImageLoaderMachO::resolveUndefined(const LinkContext& context, const s const Symbol* sym = target->findExportedSymbol(symbolName, hint, true, foundIn); if ( sym!= NULL ) { - return (*foundIn)->getExportedSymbolAddress(sym); + 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 undefinedSymbol->n_value + fSlide; + 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 @@ -1902,14 +2188,15 @@ void ImageLoaderMachO::doBindExternalRelocations(const LinkContext& context, boo 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(); - + fTextSegmentWithFixups->tempWritable(context, this); +#endif // cache last lookup - const struct macho_nlist* lastUndefinedSymbol = 0; + const struct macho_nlist* lastUndefinedSymbol = NULL; uintptr_t symbolAddr = 0; - ImageLoader* image = NULL; + const ImageLoader* image = NULL; // loop through all external relocation records and bind each const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); @@ -1925,6 +2212,7 @@ void ImageLoaderMachO::doBindExternalRelocations(const LinkContext& context, boo 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; @@ -1955,21 +2243,25 @@ void ImageLoaderMachO::doBindExternalRelocations(const LinkContext& context, boo if ( undefinedSymbol != lastUndefinedSymbol ) { symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, &image); lastUndefinedSymbol = undefinedSymbol; + symbolAddrCached = false; } if ( context.verboseBind ) { const char *path = NULL; - if(NULL != image) { + if ( image != NULL ) { path = image->getShortName(); } - if(0 == value) { - fprintf(stderr, "dyld: bind: %s:0x%08lx = %s:%s, *0x%08lx = 0x%08lx\n", + 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); + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString); } else { - fprintf(stderr, "dyld: bind: %s:0x%08lx = %s:%s, *0x%08lx = 0x%08lx + %ld\n", + 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, value); + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString, value); } } value += symbolAddr; @@ -1987,6 +2279,8 @@ void ImageLoaderMachO::doBindExternalRelocations(const LinkContext& context, boo if ( !prebound || (*location != value) ) *location = value; #endif + // update stats + ++fgTotalBindFixups; } break; default: @@ -1998,14 +2292,13 @@ void ImageLoaderMachO::doBindExternalRelocations(const LinkContext& context, boo } } +#if TEXT_RELOC_SUPPORT // if there were __TEXT fixups, restore write protection if ( fTextSegmentWithFixups != NULL ) { - fTextSegmentWithFixups->setPermissions(); - sys_icache_invalidate((void*)fTextSegmentWithFixups->getActualLoadAddress(), fTextSegmentWithFixups->getSize()); + fTextSegmentWithFixups->setPermissions(context, this); + sys_icache_invalidate((void*)fTextSegmentWithFixups->getActualLoadAddress(this), fTextSegmentWithFixups->getSize()); } - - // update stats - fgTotalBindFixups += fDynamicInfo->nextrel; +#endif } const mach_header* ImageLoaderMachO::machHeader() const @@ -2019,19 +2312,25 @@ uintptr_t ImageLoaderMachO::getSlide() const } // hmm. maybe this should be up in ImageLoader?? -const void* ImageLoaderMachO::getBaseAddress() const +const void* ImageLoaderMachO::getEnd() const { - Segment* seg = fSegments[0]; - return (const void*)seg->getActualLoadAddress(); + 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, ImageLoader* targetImage, const LinkContext& context) +uintptr_t ImageLoaderMachO::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(); - fprintf(stderr, "dyld: bind: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n", + 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); } @@ -2125,10 +2424,16 @@ uintptr_t ImageLoaderMachO::doBindLazySymbol(uintptr_t* lazyPointer, const LinkC #endif if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) { const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; - ImageLoader* image = NULL; - uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, &image); + 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; } } @@ -2137,14 +2442,57 @@ uintptr_t ImageLoaderMachO::doBindLazySymbol(uintptr_t* lazyPointer, const LinkC } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - throw "lazy pointer not found"; + dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); } +#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.linkingMainExecutable ) { + _spin_lock(&fgReadOnlyImportSpinLock); + context.makeSharedCacheImportSegmentsWritable(true); + } + } + else { + _spin_lock(&fgReadOnlyImportSpinLock); + fReadOnlyImportSegment->tempWritable(context, this); + } + } +} +void ImageLoaderMachO::makeImportSegmentReadOnly(const LinkContext& context) +{ + if ( fReadOnlyImportSegment != NULL ) { + if ( fInSharedCache ) { + if ( !context.linkingMainExecutable ) { + context.makeSharedCacheImportSegmentsWritable(false); + _spin_unlock(&fgReadOnlyImportSpinLock); + } + } + else { + fReadOnlyImportSegment->setPermissions(context, this); + _spin_unlock(&fgReadOnlyImportSpinLock); + } + } +} +#endif -void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, BindingLaziness bindness, bool onlyCoalescedSymbols) +void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys, bool onlyCoalescedSymbols) { +#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; @@ -2163,13 +2511,13 @@ void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, uint32_t elementSize = sizeof(uintptr_t); uint32_t elementCount = sect->size / elementSize; if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { - if ( (bindness == kLazyOnly) || (bindness == kLazyOnlyNoDependents) ) + if ( ! bindNonLazys ) continue; } else if ( type == S_LAZY_SYMBOL_POINTERS ) { // process each symbol pointer in this section fgTotalPossibleLazyBindFixups += elementCount; - if ( bindness == kNonLazyOnly ) + if ( ! bindLazys ) continue; } #if __i386__ @@ -2178,7 +2526,7 @@ void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, elementCount = sect->size / 5; elementSize = 5; fgTotalPossibleLazyBindFixups += elementCount; - if ( bindness == kNonLazyOnly ) + if ( ! bindLazys ) continue; } #endif @@ -2188,6 +2536,9 @@ void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, 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; @@ -2214,7 +2565,7 @@ void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, // symbol table entry is an import entry (usually it is a local symbol // definition). if ( context.verboseWarnings && !alreadyWarned ) { - fprintf(stderr, "dyld: malformed executable '%s', skipping indirect symbol to %s\n", + dyld::log("dyld: malformed executable '%s', skipping indirect symbol to %s\n", this->getPath(), &fStrings[sym->n_un.n_strx]); alreadyWarned = true; } @@ -2222,7 +2573,7 @@ void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, } } } - ImageLoader *image = NULL; + const ImageLoader* image = NULL; // if only processing coalesced symbols and this one does not require coalesceing, skip to next if ( onlyCoalescedSymbols && !symbolRequiresCoalescing(sym) ) continue; @@ -2231,43 +2582,35 @@ void ImageLoaderMachO::doBindIndirectSymbolPointers(const LinkContext& context, // update pointer symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context); + // update stats + ++fgTotalBindFixups; } } - // update stats - fgTotalBindFixups += elementCount; } } break; } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } +#if __i386__ + this->makeImportSegmentReadOnly(context); +#endif } -/* - * The address of these symbols are written in to the (__DATA,__dyld) section - * at the following offsets: - * at offset 0 stub_binding_helper_interface - * at offset 4 _dyld_func_lookup - * at offset 8 start_debug_thread - * The 'C' types (if any) for these symbols are ignored here and all are - * declared as longs so the assignment of their address in to the section will - * not require a cast. stub_binding_helper_interface is really a label in the - * assembly code interface for the stub binding. It does not have a meaningful - * 'C' type. _dyld_func_lookup is the routine in dyld_libfuncs.c. - * start_debug_thread is the routine in debug.c. - * - * For ppc the image's stub_binding_binding_helper is read from: - * at offset 20 the image's stub_binding_binding_helper address - * and saved into to the image structure. - */ +#if SUPPORT_OLD_CRT_INITIALIZATION +// first 16 bytes of "start" in crt1.o +#if __ppc__ + static uint32_t sStandardEntryPointInstructions[4] = { 0x7c3a0b78, 0x3821fffc, 0x54210034, 0x38000000 }; +#elif __i386__ + static uint8_t sStandardEntryPointInstructions[16] = { 0x6a, 0x00, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0x8b, 0x5d, 0x04, 0x89, 0x5c, 0x24 }; +#endif +#endif + struct DATAdyld { - void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper_interface - void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup - void* startDebugThread; // debugger interface ??? - void* debugPort; // debugger interface ??? - void* debugThread; // debugger interface ??? - void* stubBindHelper; // filled in at static link time to point to stub helper in image - void* coreDebug; // ??? + void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper_interface + void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup + // the following only exist in main executables built for 10.5 or later + ProgramVars vars; }; // These are defined in dyldStartup.s @@ -2278,29 +2621,68 @@ extern "C" void fast_stub_binding_helper_interface(); void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) { - if ( fDATAdyld != NULL ) { - struct DATAdyld* dd = (struct DATAdyld*)(fDATAdyld->addr + fSlide); - if ( fDATAdyld->size > offsetof(DATAdyld, dyldLazyBinder) ) { - if ( dd->dyldLazyBinder != (void*)&stub_binding_helper ) - dd->dyldLazyBinder = (void*)&stub_binding_helper; - } - if ( fDATAdyld->size > offsetof(DATAdyld, dyldFuncLookup) ) { - if ( dd->dyldFuncLookup != (void*)&dyld_func_lookup ) - dd->dyldFuncLookup = (void*)&dyld_func_lookup; - } - //if ( fDATAdyld->size > offsetof(DATAdyld, startDebugThread) ) - // dd->startDebugThread = &start_debug_thread; -#ifdef __ppc__ - //if ( fDATAdyld->size > offsetof(DATAdyld, stubBindHelper) ) - // save = dd->stubBindHelper; -#endif + 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; + // set up __dyld section + // optimizations: + // 1) do nothing if image is in dyld shared cache and dyld loaded at same address as when cache built + // 2) first read __dyld value, if already correct don't write, this prevents dirtying a page + if ( !fInSharedCache || !context.dyldLoadedAtSameAddressNeededBySharedCache ) { + 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 ( 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, "__dyld" ) == 0 ) { + struct DATAdyld* dd = (struct DATAdyld*)(sect->addr + fSlide); + if ( sect->size > offsetof(DATAdyld, dyldLazyBinder) ) { + if ( dd->dyldLazyBinder != (void*)&stub_binding_helper ) + dd->dyldLazyBinder = (void*)&stub_binding_helper; + } + if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) { + if ( dd->dyldFuncLookup != (void*)&dyld_func_lookup ) + dd->dyldFuncLookup = (void*)&dyld_func_lookup; + } + if ( mh->filetype == MH_EXECUTE ) { + // there are two ways to get the program variables + if ( (sect->size > offsetof(DATAdyld, vars)) && (dd->vars.mh == mh) ) { + // some really old binaries have space for vars, but it is zero filled + // main executable has 10.5 style __dyld section that has program variable pointers + context.setNewProgramVars(dd->vars); + } + else { + // main executable is pre-10.5 and requires the symbols names to be looked up + this->lookupProgramVars(context); + #if SUPPORT_OLD_CRT_INITIALIZATION + // If the first 16 bytes of the entry point's instructions do not + // match what crt1.o supplies, then the program has a custom entry point. + // This means it might be doing something that needs to be executed before + // initializers are run. + if ( memcmp(this->getMain(), sStandardEntryPointInstructions, 16) != 0 ) { + if ( context.verboseInit ) + dyld::log("dyld: program uses non-standard entry point so delaying running of initializers\n"); + context.setRunInitialzersOldWay(); + } + #endif + } + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } } #if __i386__ - if ( ! this->usablePrebinding(context) || !this->usesTwoLevelNameSpace() ) { + if ( ! this->usablePrebinding(context) ) { // reset all "fast" stubs - 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; + this->makeImportSegmentWritable(context); + cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { switch (cmd->cmd) { case LC_SEGMENT_COMMAND: @@ -2328,7 +2710,7 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) // 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]; - ImageLoader* image = NULL; + 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); @@ -2362,16 +2744,50 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } + this->makeImportSegmentReadOnly(context); } #endif } + +void ImageLoaderMachO::lookupProgramVars(const LinkContext& context) const +{ + ProgramVars vars = context.programVars; + const ImageLoader::Symbol* sym; + + // get mach header directly + vars.mh = (macho_header*)fMachOData; + + // lookup _NXArgc + sym = this->findExportedSymbol("_NXArgc", NULL, false, NULL); + if ( sym != NULL ) + vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this); + + // lookup _NXArgv + sym = this->findExportedSymbol("_NXArgv", NULL, false, NULL); + if ( sym != NULL ) + vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this); + + // lookup _environ + sym = this->findExportedSymbol("_environ", NULL, false, NULL); + if ( sym != NULL ) + vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this); + + // lookup __progname + sym = this->findExportedSymbol("___progname", NULL, false, NULL); + if ( sym != NULL ) + vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this); + + context.setNewProgramVars(vars); +} + + bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const { // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind - if ( this->isPrebindable() + if ( (this->isPrebindable() || fInSharedCache) && (this->getSlide() == 0) - && (this->usesTwoLevelNameSpace() || context.prebinding) + && this->usesTwoLevelNameSpace() && this->allDependentLibrariesAsWhenPreBound() ) { // allow environment variables to disable prebinding if ( context.bindFlat ) @@ -2390,65 +2806,136 @@ bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const return false; } -void ImageLoaderMachO::doBind(const LinkContext& context, BindingLaziness bindness) +void ImageLoaderMachO::doBind(const LinkContext& context, bool forceLazysBound) { + // check for runtime forcing of lazy pointers to be bound + if ( forceLazysBound && (this->getState() > dyld_image_state_bound) ) { + this->doBindIndirectSymbolPointers(context, false, forceLazysBound, false); + return; + } + // 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 be imports rebound (even if correctly prebound) - if ( this->usablePrebinding(context) && this->usesTwoLevelNameSpace() ) { + // 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, kLazyAndNonLazy, 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 - switch (bindness) { - case kNonLazyOnly: - case kLazyAndNonLazy: - // external relocations are used for data initialized to external symbols - this->doBindExternalRelocations(context, false); - break; - case kLazyOnly: - case kLazyOnlyNoDependents: - break; - } - // "indirect symbols" are used for code references to external symbols - this->doBindIndirectSymbolPointers(context, bindness, false); + // 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) { - if ( fDashInit != NULL ) { - Initializer func = (Initializer)(fDashInit->init_address + fSlide); - if ( context.verboseInit ) - fprintf(stderr, "dyld: calling -init function 0x%p in %s\n", func, this->getPath()); - func(context.argc, context.argv, context.envp, context.apple); + if ( fHasDashInit ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (unsigned long i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_ROUTINES_COMMAND: + Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); + if ( context.verboseInit ) + dyld::log("dyld: calling -init function 0x%p in %s\n", func, this->getPath()); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } } } void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) { - if ( fModInitSection != NULL ) { - Initializer* inits = (Initializer*)(fModInitSection->addr + fSlide); - const uint32_t count = fModInitSection->size / sizeof(uintptr_t); - for (uint32_t i=0; i < count; ++i) { - Initializer func = inits[i]; - if ( context.verboseInit ) - fprintf(stderr, "dyld: calling initializer function %p in %s\n", func, this->getPath()); - func(context.argc, context.argv, context.envp, context.apple); + if ( fHasInitializers ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (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; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_MOD_INIT_FUNC_POINTERS ) { + Initializer* inits = (Initializer*)(sect->addr + fSlide); + const uint32_t count = sect->size / sizeof(uintptr_t); + for (uint32_t i=0; i < count; ++i) { + Initializer func = inits[i]; + if ( context.verboseInit ) + dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } } } } + + +void ImageLoaderMachO::doGetDOFSections(const LinkContext& context, std::vector& dofs) +{ + if ( fHasDOFSections ) { + // walk load commands (mapped in at start of __TEXT segment) + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & SECTION_TYPE) == S_DTRACE_DOF ) { + ImageLoader::DOFInfo info; + info.dof = (void*)(sect->addr + fSlide); + info.imageHeader = this->machHeader(); + info.imageShortName = this->getShortName(); + dofs.push_back(info); + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + void ImageLoaderMachO::doInitialization(const LinkContext& context) { // mach-o has -init and static initializers @@ -2458,411 +2945,213 @@ void ImageLoaderMachO::doInitialization(const LinkContext& context) bool ImageLoaderMachO::needsInitialization() { - return ( (fDashInit != NULL) || (fModInitSection != NULL) ); + return ( fHasDashInit || fHasInitializers ); } bool ImageLoaderMachO::needsTermination() { - return ( fModTermSection != NULL ); + return fHasTerminators; } +#if IMAGE_NOTIFY_SUPPORT bool ImageLoaderMachO::hasImageNotification() { - return ( fImageNotifySection != NULL ); + return fHasImageNotifySection; } - +#endif void ImageLoaderMachO::doTermination(const LinkContext& context) { - if ( fModTermSection != NULL ) { - Terminator* terms = (Terminator*)(fModTermSection->addr + fSlide); - const uint32_t count = fModTermSection->size / sizeof(uintptr_t); - for (uint32_t i=count; i > 0; --i) { - Terminator func = terms[i-1]; - if ( context.verboseInit ) - fprintf(stderr, "dyld: calling terminaton function %p in %s\n", func, this->getPath()); - func(); + if ( fHasTerminators ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (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; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_MOD_TERM_FUNC_POINTERS ) { + Terminator* terms = (Terminator*)(sect->addr + fSlide); + const uint32_t count = sect->size / sizeof(uintptr_t); + 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()); + func(); + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } } } +#if IMAGE_NOTIFY_SUPPORT void ImageLoaderMachO::doNotification(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]) { - if ( fImageNotifySection != NULL ) { - dyld_image_notifier* notes = (dyld_image_notifier*)(fImageNotifySection->addr + fSlide); - const uint32_t count = fImageNotifySection->size / sizeof(uintptr_t); - for (uint32_t i=count; i > 0; --i) { - dyld_image_notifier func = notes[i-1]; - func(mode, infoCount, 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); - fprintf(stderr, "total hinted binary tree searches: %d\n", fgHintedBinaryTreeSearchs); - fprintf(stderr, "total unhinted binary tree searches: %d\n", fgUnhintedBinaryTreeSearchs); - fprintf(stderr, "total images with weak exports: %d\n", fgCountOfImagesWithWeakExports); + //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 - fprintf(stderr, "linkedit pages accessed (%lu):\n", sLinkEditPageBuckets.size()); + dyld::log("linkedit pages accessed (%lu):\n", sLinkEditPageBuckets.size()); #endif } -void ImageLoaderMachO::doPrebinding(const LinkContext& context, time_t timestamp, uint8_t* fileToPrebind) -{ - // update __DATA segment - this->applyPrebindingToDATA(fileToPrebind); - - // update load commands - this->applyPrebindingToLoadCommands(context, fileToPrebind, timestamp); - - // update symbol table - this->applyPrebindingToLinkEdit(context, fileToPrebind); -} -void ImageLoaderMachO::applyPrebindingToDATA(uint8_t* fileToPrebind) +ImageLoader::SegmentIterator ImageLoaderMachO::beginSegments() const { - const unsigned int segmentCount = fSegments.size(); - for(unsigned int i=0; i < segmentCount; ++i) { - SegmentMachO* seg = (SegmentMachO*)fSegments[i]; - if ( seg->writeable() ) { - memcpy(&fileToPrebind[seg->fFileOffset], (void*)seg->getActualLoadAddress(), seg->fFileSize); - } - } + return SegmentIterator(fSegmentsArray); } -void ImageLoaderMachO::applyPrebindingToLoadCommands(const LinkContext& context, uint8_t* fileToPrebind, time_t timestamp) +ImageLoader::SegmentIterator ImageLoaderMachO::endSegments() const { - macho_header* mh = (macho_header*)fileToPrebind; - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)&fileToPrebind[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: - { - // update each dylib load command with the timestamp of the target dylib - struct dylib_command* dylib = (struct dylib_command*)cmd; - const char* name = (char*)cmd + dylib->dylib.name.offset; - for (const DependentLibrary* dl=fLibraries; dl < &fLibraries[fLibrariesCount]; dl++) { - if (strcmp(dl->name, name) == 0 ) { - // found matching DependentLibrary for this load command - if ( dl->image == NULL ) { - // missing weak linked dylib - dylib->dylib.timestamp = 0; - } - else { - ImageLoaderMachO* targetImage = (ImageLoaderMachO*)(dl->image); // !!! assume only mach-o images are prebound - if ( ! targetImage->isPrebindable() ) - throw "dependent dylib is not prebound"; - // if the target is currently being re-prebound then its timestamp will be the same as this one - if ( ! targetImage->usablePrebinding(context) ) { - dylib->dylib.timestamp = timestamp; - } - else { - // otherwise dependent library is already correctly prebound, so use its checksum - dylib->dylib.timestamp = targetImage->doGetLibraryInfo().checksum; - } - } - break; - } - } - } - break; - case LC_ID_DYLIB: - { - // update the ID of this library with the new timestamp - struct dylib_command* dylib = (struct dylib_command*)cmd; - dylib->dylib.timestamp = timestamp; - } - break; - case LC_SEGMENT_COMMAND: - // if dylib was rebased, update segment commands - if ( fSlide != 0 ) { - struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - seg->vmaddr += fSlide; - struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); - struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; - for (struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - sect->addr += fSlide; - } - } - break; - case LC_ROUTINES_COMMAND: - // if dylib was rebased, update -init command - if ( fSlide != 0 ) { - struct macho_routines_command* routines = (struct macho_routines_command*)cmd; - routines->init_address += fSlide; - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } + return SegmentIterator(&fSegmentsArray[fSegmentsArrayCount]); } -void ImageLoaderMachO::applyPrebindingToLinkEdit(const LinkContext& context, uint8_t* fileToPrebind) +SegmentMachO::SegmentMachO(const struct macho_segment_command* cmd) + : fSegmentLoadCommand(cmd) { - // In prebound images, the n_value of the symbol table entry for is the prebound address - // This is needed when prebinding can't be used, to back solve for any possible addend in non-lazy pointers - const char* stringPool = NULL; - struct macho_nlist* symbolTable = NULL; - const struct dysymtab_command* dysymtab = NULL; - - // get symbol table info - macho_header* mh = (macho_header*)fileToPrebind; - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)&fileToPrebind[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; - stringPool = (const char*)&fileToPrebind[symtab->stroff]; - symbolTable = (struct macho_nlist*)(&fileToPrebind[symtab->symoff]); - } - break; - case LC_DYSYMTAB: - dysymtab = (struct dysymtab_command*)cmd; - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); - } - - // walk all imports and re-resolve their n_value (needed incase prebinding is invalid) - struct macho_nlist* lastImport = &symbolTable[dysymtab->iundefsym+dysymtab->nundefsym]; - for (struct macho_nlist* entry = &symbolTable[dysymtab->iundefsym]; entry < lastImport; ++entry) { - ImageLoader* dummy; - entry->n_value = this->resolveUndefined(context, entry, this->usesTwoLevelNameSpace(), &dummy); - } - - // walk all exports and slide their n_value - struct macho_nlist* lastExport = &symbolTable[dysymtab->iextdefsym+dysymtab->nextdefsym]; - for (struct macho_nlist* entry = &symbolTable[dysymtab->iextdefsym]; entry < lastExport; ++entry) { - if ( (entry->n_type & N_TYPE) == N_SECT ) - entry->n_value += fSlide; - } - - // walk all local symbols and slide their n_value - struct macho_nlist* lastLocal = &symbolTable[dysymtab->ilocalsym+dysymtab->nlocalsym]; - for (struct macho_nlist* entry = &symbolTable[dysymtab->ilocalsym]; entry < lastLocal; ++entry) { - if ( entry->n_sect != NO_SECT ) - entry->n_value += fSlide; - } - - // walk all local relocations and reset every PPC_RELOC_PB_LA_PTR r_value - relocation_info* const relocsStart = (struct relocation_info*)(&fileToPrebind[dysymtab->locreloff]); - relocation_info* const relocsEnd = &relocsStart[dysymtab->nlocrel]; - for (relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { - if ( (reloc->r_address & R_SCATTERED) != 0 ) { - struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; - if (sreloc->r_length == RELOC_SIZE) { - switch(sreloc->r_type) { - #if __ppc__ || __ppc64__ - case PPC_RELOC_PB_LA_PTR: - #elif __i386__ || __x86_64__ - case GENERIC_RELOC_PB_LA_PTR: - #else - #error unknown architecture - #endif - sreloc->r_value += fSlide; - break; - } - } - } - } - - // if multi-module, fix up objc_addr (10.4 and later runtime does not use this, but we want to keep file checksum consistent) - if ( dysymtab->nmodtab != 0 ) { - dylib_module* const modulesStart = (struct dylib_module*)(&fileToPrebind[dysymtab->modtaboff]); - dylib_module* const modulesEnd = &modulesStart[dysymtab->nmodtab]; - for (dylib_module* module=modulesStart; module < modulesEnd; ++module) { - if ( module->objc_module_info_size != 0 ) { - module->objc_module_info_addr += fSlide; - } - } - } -} - -// file on disk has been reprebound, but we are still mapped to old file -void ImageLoaderMachO::prebindUnmap(const LinkContext& context) -{ - // this removes all mappings to the old file, so the kernel will unlink (delete) it. - // We need to leave the load commands and __LINKEDIT in place - for (std::vector::iterator it=fSegments.begin(); it != fSegments.end(); ++it) { - void* segmentAddress = (void*)((*it)->getActualLoadAddress()); - uintptr_t segmentSize = (*it)->getSize(); - //fprintf(stderr, "unmapping segment %s at %p for %s\n", (*it)->getName(), segmentAddress, this->getPath()); - // save load commands at beginning of __TEXT segment - if ( segmentAddress == fMachOData ) { - // typically load commands are one or two pages in size, so ok to alloc on stack - uint32_t loadCmdSize = sizeof(macho_header) + ((macho_header*)fMachOData)->sizeofcmds; - uint32_t loadCmdPages = (loadCmdSize+4095) & (-4096); - uint8_t loadcommands[loadCmdPages]; - memcpy(loadcommands, fMachOData, loadCmdPages); - // unmap whole __TEXT segment - munmap((void*)(fMachOData), segmentSize); - // allocate and copy back mach_header and load commands - vm_address_t addr = (vm_address_t)fMachOData; - int r2 = vm_allocate(mach_task_self(), &addr, loadCmdPages, false /*at this address*/); - if ( r2 != 0 ) - fprintf(stderr, "prebindUnmap() vm_allocate for __TEXT %d failed\n", loadCmdPages); - memcpy((void*)fMachOData, loadcommands, loadCmdPages); - //fprintf(stderr, "copying back load commands to %p size=%u for %s\n", segmentAddress, loadCmdPages, this->getPath()); - } - else if ( strcmp((*it)->getName(), "__LINKEDIT") == 0 ) { - uint32_t linkEditSize = segmentSize; - uint32_t linkEditPages = (linkEditSize+4095) & (-4096); - void* linkEditTmp = malloc(linkEditPages); - memcpy(linkEditTmp, segmentAddress, linkEditPages); - // unmap whole __LINKEDIT segment - munmap(segmentAddress, segmentSize); - vm_address_t addr = (vm_address_t)segmentAddress; - int r2 = vm_allocate(mach_task_self(), &addr, linkEditPages, false /*at this address*/); - if ( r2 != 0 ) - fprintf(stderr, "prebindUnmap() vm_allocate for __LINKEDIT %d failed\n", linkEditPages); - memcpy(segmentAddress, linkEditTmp, linkEditPages); - //fprintf(stderr, "copying back __LINKEDIT to %p size=%u for %s\n", segmentAddress, linkEditPages, this->getPath()); - free(linkEditTmp); - } - else { - // unmap any other segment - munmap((void*)(segmentAddress), (*it)->getSize()); - } - } } - - -SegmentMachO::SegmentMachO(const struct macho_segment_command* cmd, ImageLoaderMachO* image, const uint8_t* fileData) - : fImage(image), fSize(cmd->vmsize), fFileSize(cmd->filesize), fFileOffset(cmd->fileoff), fPreferredLoadAddress(cmd->vmaddr), - fVMProtection(cmd->initprot), fHasFixUps(false), fUnMapOnDestruction(false) +SegmentMachO::~SegmentMachO() { - strncpy(fName, cmd->segname, 16); - fName[16] = '\0'; - // scan sections for fix-up bit - const struct macho_section* const sectionsStart = (struct macho_section*)((char*)cmd + sizeof(struct macho_segment_command)); - const struct macho_section* const sectionsEnd = §ionsStart[cmd->nsects]; - for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { - if ( (sect->flags & (S_ATTR_EXT_RELOC | S_ATTR_LOC_RELOC)) != 0 ) - fHasFixUps = true; - } } -SegmentMachO::~SegmentMachO() +void SegmentMachO::adjust(const struct macho_segment_command* cmd) { - if ( fUnMapOnDestruction ) { - //fprintf(stderr, "unmapping segment %s at 0x%08lX\n", getName(), getActualLoadAddress()); - munmap((void*)(this->getActualLoadAddress()), this->getSize()); - } + fSegmentLoadCommand = cmd; } -const ImageLoader* SegmentMachO::getImage() +void SegmentMachO::unmap(const ImageLoader* image) { - return fImage; + // update stats + --ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped -= fSegmentLoadCommand->vmsize; + munmap((void*)(this->getActualLoadAddress(image)), fSegmentLoadCommand->vmsize); } + const char* SegmentMachO::getName() { - return fName; + return fSegmentLoadCommand->segname; } uintptr_t SegmentMachO::getSize() { - return fSize; + return fSegmentLoadCommand->vmsize; } uintptr_t SegmentMachO::getFileSize() { - return fFileSize; + return fSegmentLoadCommand->filesize; } uintptr_t SegmentMachO::getFileOffset() { - return fFileOffset; + return fSegmentLoadCommand->fileoff; } bool SegmentMachO::readable() { - return ( (fVMProtection & VM_PROT_READ) != 0); + return ( (fSegmentLoadCommand->initprot & VM_PROT_READ) != 0); } bool SegmentMachO::writeable() { - return ((fVMProtection & VM_PROT_WRITE) != 0); + return ((fSegmentLoadCommand->initprot & VM_PROT_WRITE) != 0); } bool SegmentMachO::executable() { - return ((fVMProtection & VM_PROT_EXECUTE) != 0); + return ((fSegmentLoadCommand->initprot & VM_PROT_EXECUTE) != 0); } bool SegmentMachO::unaccessible() { - return (fVMProtection == 0); + return (fSegmentLoadCommand->initprot == 0); } +#if TEXT_RELOC_SUPPORT bool SegmentMachO::hasFixUps() { - return fHasFixUps; + // 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; + } + return false; } +#endif -uintptr_t SegmentMachO::getActualLoadAddress() +#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) { - return fPreferredLoadAddress + fImage->fSlide; + return fSegmentLoadCommand->vmaddr + inImage->getSlide(); } uintptr_t SegmentMachO::getPreferredLoadAddress() { - return fPreferredLoadAddress; + return fSegmentLoadCommand->vmaddr; } bool SegmentMachO::hasPreferredLoadAddress() { - return (fPreferredLoadAddress != 0); + return (fSegmentLoadCommand->vmaddr != 0); } -void SegmentMachO::setUnMapWhenDestructed(bool unmap) -{ - fUnMapOnDestruction = unmap; -} -static uint32_t *buildCRCTable(void) +Segment* SegmentMachO::next(Segment* location) { - uint32_t *table = new uint32_t[256]; - uint32_t p = 0xedb88320UL; // standard CRC-32 polynomial - - for (unsigned int i = 0; i < 256; i++) { - uint32_t c = i; - for (unsigned int j = 0; j < 8; j++) { - if ( c & 1 ) c = p ^ (c >> 1); - else c = c >> 1; - } - table[i] = c; - } - - return table; + return &((SegmentMachO*)location)[1]; } -uint32_t SegmentMachO::crc32() -{ - if ( !readable() ) return 0; - - static uint32_t *crcTable = NULL; - if ( !crcTable ) crcTable = buildCRCTable(); - - uint32_t crc = ~(uint32_t)0; - uint8_t *p = (uint8_t *)getActualLoadAddress(); - uint8_t *end = p + getSize(); - while ( p < end ) { - crc = crcTable[(crc & 0xff) ^ (*p++)] ^ (crc >> 8); - } - return crc ^ ~(uint32_t)0; -} diff --git a/src/ImageLoaderMachO.h b/src/ImageLoaderMachO.h index 4e33955..a7f2c41 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -29,10 +29,12 @@ #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 @@ -43,16 +45,18 @@ 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(); const char* getInstallPath() const; virtual void* getMain() const; virtual const struct mach_header* machHeader() const; virtual uintptr_t getSlide() const; - virtual const void* getBaseAddress() const; + virtual const void* getEnd() const; virtual bool hasCoalescedExports() const; - virtual const Symbol* findExportedSymbol(const char* name, const void* hint, bool searchReExports, ImageLoader** foundIn) const; - virtual uintptr_t getExportedSymbolAddress(const Symbol* sym) const; + virtual const Symbol* findExportedSymbol(const char* name, const void* hint, 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; virtual uint32_t getExportedSymbolCount() const; @@ -66,9 +70,13 @@ public: virtual bool forceFlat() const; virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context); 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 bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset); virtual bool usablePrebinding(const LinkContext& context) const; @@ -81,12 +89,14 @@ protected: void operator=(const ImageLoaderMachO&); virtual uint32_t doGetDependentLibraryCount(); - virtual void doGetDependentLibraries(DependentLibrary libs[]); + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]); virtual LibraryInfo doGetLibraryInfo(); + virtual void getRPaths(const LinkContext& context, std::vector&) const; virtual void doRebase(const LinkContext& context); - virtual void doBind(const LinkContext& context, BindingLaziness bindness); + virtual void doBind(const LinkContext& context, bool forceLazysBound); + virtual void doUpdateMappingPermissions(const LinkContext& context); virtual void doInitialization(const LinkContext& context); - virtual void doPrebinding(const LinkContext& context, time_t timestamp, uint8_t* fileToPrebind); + virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs); virtual bool needsTermination(); virtual void instantiateSegments(const uint8_t* fileData); virtual bool segmentsMustSlideTogether() const; @@ -96,9 +106,10 @@ protected: 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 void prebindUnmap(const LinkContext& context); + virtual SegmentIterator beginSegments() const; + virtual SegmentIterator endSegments() const; -#if !__LP64__ // split segs not supported for 64-bits +#if SPLIT_SEG_DYLIB_SUPPORT virtual void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); #endif @@ -107,37 +118,44 @@ private: void init(); void parseLoadCmds(); - uintptr_t bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, ImageLoader* targetImage, const LinkContext& context); - void doBindIndirectSymbolPointers(const LinkContext& context, BindingLaziness bindness, bool onlyCoalescedSymbols); + 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, ImageLoader **foundIn); + 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); + void resetPreboundLazyPointers(const LinkContext& context, uintptr_t relocBase); void doImageInit(const LinkContext& context); void doModInitFunctions(const LinkContext& context); void setupLazyPointerHandler(const LinkContext& context); - void applyPrebindingToDATA(uint8_t* fileToPrebind); - void applyPrebindingToLoadCommands(const LinkContext& context, uint8_t* fileToPrebind, time_t timestamp); - void applyPrebindingToLinkEdit(const LinkContext& context, uint8_t* fileToPrebind); -#if !__LP64__ // split segs not supported for 64-bits + 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); - int sharedRegionMapFilePrivateOutside(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); void initMappingTable(uint64_t offsetInFat, sf_mapping *mappingTable, uintptr_t baseAddress); - void initMappingTable(uint64_t offsetInFat, _shared_region_mapping_np *mappingTable); #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 static bool symbolRequiresCoalescing(const struct macho_nlist* symbol); static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer); - static const struct macho_nlist* binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount); - static 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 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; const uint8_t* fMachOData; const uint8_t* fLinkEditBase; // add any internal "offset" to this to get actual address @@ -145,20 +163,32 @@ private: const char* fStrings; const struct dysymtab_command* fDynamicInfo; uintptr_t fSlide; - bool fIsSplitSeg; - bool fHasSubLibraries; - bool fHasSubUmbrella; - bool fTextSegmentHasFixups; - const struct macho_routines_command* fDashInit; - const struct macho_section* fModInitSection; - const struct macho_section* fModTermSection; - const struct macho_section* fDATAdyld; - const struct macho_section* fImageNotifySection; const struct twolevel_hints_command* fTwoLevelHints; const struct dylib_command* fDylibID; - const char* fReExportThruFramework; + class SegmentMachO* fSegmentsArray; +#if TEXT_RELOC_SUPPORT class SegmentMachO* fTextSegmentWithFixups; // NULL unless __TEXT segment has fixups - +#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, +#endif + fHasSubLibraries : 1, + fHasSubUmbrella : 1, + fInUmbrella : 1, + 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; @@ -168,10 +198,9 @@ private: class SegmentMachO : public Segment { public: - SegmentMachO(const struct macho_segment_command* cmd, ImageLoaderMachO*, const uint8_t* fileData); + SegmentMachO(const struct macho_segment_command* cmd); virtual ~SegmentMachO(); - virtual const ImageLoader* getImage(); virtual const char* getName(); virtual uintptr_t getSize(); virtual uintptr_t getFileSize(); @@ -180,31 +209,27 @@ public: virtual bool writeable(); virtual bool executable(); virtual bool unaccessible(); - virtual bool hasFixUps(); - virtual uintptr_t getActualLoadAddress(); + virtual uintptr_t getActualLoadAddress(const ImageLoader*); virtual uintptr_t getPreferredLoadAddress(); - virtual void setUnMapWhenDestructed(bool unmap); - virtual uint32_t crc32(); - + 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; - - ImageLoaderMachO* const fImage; - char fName[18]; - const uintptr_t fSize; - const uintptr_t fFileSize; - const uintptr_t fFileOffset; - const uintptr_t fPreferredLoadAddress; - const uint16_t fVMProtection; - bool fHasFixUps; - bool fUnMapOnDestruction; + const struct macho_segment_command* fSegmentLoadCommand; }; diff --git a/src/dyld.cpp b/src/dyld.cpp index 4d875e4..8d340ad 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-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include // mach_absolute_time() @@ -32,35 +33,60 @@ #include #include #include +#include #include #include #include +#include +#include #include +#include #include "mach-o/dyld_gdb.h" #include "dyld.h" #include "ImageLoader.h" #include "ImageLoaderMachO.h" -#include "dyldLibSystemThreadHelpers.h" +#include "dyldLibSystemInterface.h" +#include "dyld_cache_format.h" + +// from _simple.h in libc +typedef struct _SIMPLE* _SIMPLE_STRING; +extern "C" void _simple_vdprintf(int __fd, const char *__fmt, va_list __ap); +extern "C" void _simple_dprintf(int __fd, const char *__fmt, ...); +extern "C" _SIMPLE_STRING _simple_salloc(void); +extern "C" int _simple_vsprintf(_SIMPLE_STRING __b, const char *__fmt, va_list __ap); +extern "C" void _simple_sfree(_SIMPLE_STRING __b); +extern "C" char * _simple_string(_SIMPLE_STRING __b); + + + +// 32-bit ppc is only architecture that uses cpu-sub-types +#define CPU_SUBTYPES_SUPPORTED __ppc__ + + +#define OLD_GDB_DYLD_INTERFACE __ppc__ || __i386__ #define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */ /* implemented in dyld_gdb.cpp */ -void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); -void removeImageFromAllImages(const mach_header* mh); +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern void removeImageFromAllImages(const mach_header* mh); #if OLD_GDB_DYLD_INTERFACE -void addImageForgdb(const mach_header* mh, uintptr_t slide, const char* physicalPath, const char* logicalPath); -void removeImageForgdb(const struct mach_header* mh); +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 // magic so CrashReporter logs message extern "C" { char error_string[1024]; } +// implemented in dyldStartup.s for CrashReporter +extern "C" void dyld_fatal_error(const char* errString) __attribute__((noreturn)); + // @@ -93,6 +119,7 @@ struct EnvironmentVariables { bool DYLD_PRINT_STATISTICS; bool DYLD_PRINT_OPTS; bool DYLD_PRINT_ENV; + bool DYLD_DISABLE_DOFS; // DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix // DYLD_PRINT_OPTS ==> gLinkContext.verboseOpts // DYLD_PRINT_ENV ==> gLinkContext.verboseEnv @@ -101,28 +128,37 @@ struct EnvironmentVariables { // DYLD_PRINT_SEGMENTS ==> gLinkContext.verboseMapping // DYLD_PRINT_BINDINGS ==> gLinkContext.verboseBind // DYLD_PRINT_REBASINGS ==> gLinkContext.verboseRebase + // DYLD_PRINT_DOFS ==> gLinkContext.verboseDOF // DYLD_PRINT_APIS ==> gLogAPIs // DYLD_IGNORE_PREBINDING ==> gLinkContext.prebindUsage // DYLD_PREBIND_DEBUG ==> gLinkContext.verbosePrebinding // DYLD_NEW_LOCAL_SHARED_REGIONS ==> gLinkContext.sharedRegionMode // DYLD_SHARED_REGION ==> gLinkContext.sharedRegionMode - // DYLD_SLIDE_AND_PACK_DYLIBS ==> gLinkContext.slideAndPackDylibs // 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 cpu_type_t sHostCPU; static cpu_subtype_t sHostCPUsubtype; static ImageLoader* sMainExecutable = NULL; -static bool sAllImagesMightContainUnlinkedImages; // necessary until will support dylib unloading +static bool sMainExecutableIsSetuid = 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]; +static StateHandlers sBatchHandlers[7]; static ImageLoader* sLastImageByAddressCache; static EnvironmentVariables sEnv; static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; @@ -130,10 +166,67 @@ static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib" 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; +#endif ImageLoader::LinkContext gLinkContext; bool gLogAPIs = false; -const struct ThreadingHelpers* gThreadHelpers = NULL; +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 + + + +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; +} + + +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; +} + +void log(const char* format, ...) +{ + va_list list; + va_start(list, format); + _simple_vdprintf(STDERR_FILENO, format, list); + va_end(list); +} +void warn(const char* format, ...) +{ + _simple_dprintf(STDERR_FILENO, "dyld: warning, "); + va_list list; + va_start(list, format); + _simple_vdprintf(STDERR_FILENO, format, list); + va_end(list); +} // utility class to assure files are closed when an exception is thrown @@ -147,78 +240,324 @@ private: }; FileOpener::FileOpener(const char* path) + : fd(-1) { fd = open(path, O_RDONLY, 0); } FileOpener::~FileOpener() { - close(fd); + if ( fd != -1 ) + close(fd); } +// forward declaration +#if __ppc__ || __i386__ +bool isRosetta(); +#endif + -// Objective-C installs an addImage hook to dyld to get notified about new images -// The callback needs to be run after the image is rebased and bound, but before its initializers are called -static uint32_t imageNotification(ImageLoader* image, uint32_t startIndex) +static void registerDOFs(const std::vector& dofs) { - // tell all register add image handlers about this - const uint32_t callbackCount = sAddImageCallbacks.size(); - for (uint32_t i=startIndex; i < callbackCount; ++i) { - ImageCallback cb = sAddImageCallbacks[i]; - //fprintf(stderr, "dyld: calling add-image-callback[%d]=%p for %s\n", i, cb, image->getPath()); - (cb)(image->machHeader(), image->getSlide()); +#if __ppc__ + // can't dtrace a program running emulated under rosetta rdar://problem/5179640 + if ( isRosetta() ) + return; +#endif + const unsigned int dofSectionCount = dofs.size(); + if ( !sEnv.DYLD_DISABLE_DOFS && (dofSectionCount != 0) ) { + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + if ( fd < 0 ) { + //dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n"); + } + else { + // allocate a buffer on the stack for the variable length dof_ioctl_data_t type + uint8_t buffer[sizeof(dof_ioctl_data_t) + dofSectionCount*sizeof(dof_helper_t)]; + dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer; + + // fill in buffer with one dof_helper_t per DOF section + ioctlData->dofiod_count = dofSectionCount; + for (unsigned int i=0; i < dofSectionCount; ++i) { + strlcpy(ioctlData->dofiod_helpers[i].dofhp_mod, dofs[i].imageShortName, DTRACE_MODNAMELEN); + ioctlData->dofiod_helpers[i].dofhp_dof = (uintptr_t)(dofs[i].dof); + ioctlData->dofiod_helpers[i].dofhp_addr = (uintptr_t)(dofs[i].dof); + } + + // tell kernel about all DOF sections en mas + // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel + user_addr_t val = (user_addr_t)(unsigned long)ioctlData; + if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) { + // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field. + for (unsigned int i=0; i < dofSectionCount; ++i) { + RegisteredDOF info; + info.mh = dofs[i].imageHeader; + info.registrationID = (int)(ioctlData->dofiod_helpers[i].dofhp_dof); + sImageFilesNeedingDOFUnregistration.push_back(info); + if ( gLinkContext.verboseDOF ) { + dyld::log("dyld: registering DOF section 0x%p in %s with dtrace, ID=0x%08X\n", + dofs[i].dof, dofs[i].imageShortName, info.registrationID); + } + } + } + else { + dyld::log( "dyld: ioctl to register dtrace DOF section failed\n"); + } + close(fd); + } + } +} + +static void unregisterDOF(int registrationID) +{ + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + if ( fd < 0 ) { + dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to unregister dtrace DOF section\n"); + } + else { + ioctl(fd, DTRACEHIOC_REMOVE, registrationID); + close(fd); + if ( gLinkContext.verboseInit ) + dyld::warn("unregistering DOF section ID=0x%08X with dtrace\n", registrationID); } - return callbackCount; } +// +// _dyld_register_func_for_add_image() is implemented as part of the general image state change notification +// +static void notifyAddImageCallbacks(ImageLoader* image) +{ + for (std::vector::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++) + (*it)(image->machHeader(), image->getSlide()); +} + + +// notify gdb about these new images +static const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + addImagesToAllImages(infoCount, info); + return NULL; +} -// notify gdb et al about these new images -static void notifyAdding(std::vector& images) +#if IMAGE_NOTIFY_SUPPORT +// notify objc about these new images +static void notifyAdding(const ImageLoader* const * images, unsigned int count) { // build array - unsigned int len = images.size(); - if ( len != 0 ) { - dyld_image_info infos[len]; - for (unsigned int i=0; i < len; ++i) { + if ( count != 0 ) { + dyld_image_info infos[count]; + for (unsigned int i=0; i < count; ++i) { dyld_image_info* p = &infos[i]; - ImageLoader* image = images[i]; + const ImageLoader* image = images[i]; p->imageLoadAddress = image->machHeader(); p->imageFilePath = image->getPath(); p->imageFileModDate = image->lastModified(); - //fprintf(stderr, "notifying objc about %s\n", image->getPath()); + //dyld::log("notifying objc about %s\n", image->getPath()); } - // tell gdb - addImagesToAllImages(len, infos); - // 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, len, infos); + (*it)->doNotification(dyld_image_adding, count, infos); + } + } +} +#endif + +static StateHandlers* stateToHandlers(dyld_image_states state, StateHandlers handlersArray[8]) +{ + switch ( state ) { + case dyld_image_state_mapped: + return &handlersArray[0]; + + case dyld_image_state_dependents_mapped: + return &handlersArray[1]; + + case dyld_image_state_rebased: + return &handlersArray[2]; + + case dyld_image_state_bound: + return &handlersArray[3]; + + case dyld_image_state_dependents_initialized: + return &handlersArray[4]; + + case dyld_image_state_initialized: + return &handlersArray[5]; + + case dyld_image_state_terminated: + return &handlersArray[6]; + } + return NULL; +} + +static void notifySingle(dyld_image_states state, const struct mach_header* mh, const char* path, time_t modDate) +{ + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + dyld_image_info info; + info.imageLoadAddress = mh; + info.imageFilePath = path; + info.imageFileModDate = modDate; + for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { + const char* result = (*it)(state, 1, &info); + if ( (result != NULL) && (state == dyld_image_state_mapped) ) { + //fprintf(stderr, " image rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + const char* str = strdup(result); + throw str; + } } } } +static int imageSorter(const void* l, const void* r) +{ + const ImageLoader* left = *((ImageLoader**)l); + const ImageLoader* right= *((ImageLoader**)r); + return left->compare(right); +} + +static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler) +{ + std::vector* handlers = stateToHandlers(state, sBatchHandlers); + if ( handlers != NULL ) { + // don't use a vector because it will use malloc/free and we want notifcation to be low cost + ImageLoader* images[sAllImages.size()+1]; + ImageLoader** end = images; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + dyld_image_states imageState = (*it)->getState(); + if ( (imageState == state) || (orLater && (imageState > state)) ) + *end++ = *it; + } + if ( sBundleBeingLoaded != NULL ) { + dyld_image_states imageState = sBundleBeingLoaded->getState(); + if ( (imageState == state) || (orLater && (imageState > state)) ) + *end++ = sBundleBeingLoaded; + } + unsigned int count = end-images; + if ( end != images ) { + // sort bottom up + qsort(images, count, sizeof(ImageLoader*), &imageSorter); + // build info array + dyld_image_info infos[count]; + for (unsigned int i=0; i < count; ++i) { + dyld_image_info* p = &infos[i]; + ImageLoader* image = images[i]; + //dyld::log(" state=%d, name=%s\n", state, image->getPath()); + p->imageLoadAddress = image->machHeader(); + p->imageFilePath = image->getPath(); + p->imageFileModDate = image->lastModified(); + // special case for add_image hook + if ( state == dyld_image_state_bound ) + notifyAddImageCallbacks(image); + } + + if ( onlyHandler != NULL ) { + const char* result = (*onlyHandler)(state, count, infos); + if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { + //fprintf(stderr, " images rejected by handler=%p\n", onlyHandler); + // make copy of thrown string so that later catch clauses can free it + const char* str = strdup(result); + throw str; + } + } + else { + // call each handler with whole array + for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { + const char* result = (*it)(state, count, infos); + if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { + //fprintf(stderr, " images rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + const char* str = strdup(result); + throw str; + } + } + } + } + } +} + +static void notifyBatch(dyld_image_states state) +{ + notifyBatchPartial(state, false, NULL); +} + // In order for register_func_for_add_image() callbacks to to be called bottom up, // we need to maintain a list of root images. The main executable is usally the // first root. Any images dynamically added are also roots (unless already loaded). // If DYLD_INSERT_LIBRARIES is used, those libraries are first. static void addRootImage(ImageLoader* image) { - //fprintf(stderr, "addRootImage(%p, %s)\n", image, image->getPath()); + //dyld::log("addRootImage(%p, %s)\n", image, image->getPath()); // add to list of roots 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() +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) + (*it)->clearDepth(); +} + +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) +{ + // make a copy of the pointers to program variables + gLinkContext.programVars = newVars; + + // now set each program global to their initial value + *gLinkContext.programVars.NXArgcPtr = gLinkContext.argc; + *gLinkContext.programVars.NXArgvPtr = gLinkContext.argv; + *gLinkContext.programVars.environPtr = gLinkContext.envp; + *gLinkContext.programVars.__prognamePtr = gLinkContext.progname; +} + +#if SUPPORT_OLD_CRT_INITIALIZATION +static void setRunInitialzersOldWay() +{ + gRunInitializersOldWay = true; +} +#endif static void addImage(ImageLoader* image) { @@ -226,11 +565,7 @@ static void addImage(ImageLoader* image) sAllImages.push_back(image); if ( sEnv.DYLD_PRINT_LIBRARIES || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { - uint64_t offset = image->getOffsetInFatFile(); - if ( offset == 0 ) - fprintf(stderr, "dyld: loaded: %s\n", image->getPath()); - else - fprintf(stderr, "dyld: loaded: %s, cpu-sub-type: %d\n", image->getPath(), image->machHeader()->cpusubtype); + dyld::log("dyld: loaded: %s\n", image->getPath()); } #if OLD_GDB_DYLD_INTERFACE @@ -250,12 +585,27 @@ void removeImage(ImageLoader* image) } } + // if has dtrace DOF section, tell dtrace it is going away, then remove from sImageFilesNeedingDOFUnregistration + for (std::vector::iterator it=sImageFilesNeedingDOFUnregistration.begin(); it != sImageFilesNeedingDOFUnregistration.end(); ) { + if ( it->mh == image->machHeader() ) { + unregisterDOF(it->registrationID); + sImageFilesNeedingDOFUnregistration.erase(it); + // don't increment iterator, the erase caused next element to be copied to where this iterator points + } + else { + ++it; + } + } + // tell all register add image handlers about this - // do this before removing image from internal data structures so that the callback can querey dyld about the image - for (std::vector::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) { - (*it)(image->machHeader(), image->getSlide()); + // 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++) { + (*it)(image->machHeader(), image->getSlide()); + } } +#if IMAGE_NOTIFY_SUPPORT // tell all interested images for (std::vector::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) { dyld_image_info info; @@ -264,6 +614,7 @@ void removeImage(ImageLoader* image) info.imageFileModDate = image->lastModified(); (*it)->doNotification(dyld_image_removing, 1, &info); } +#endif // remove from master list for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { @@ -273,10 +624,11 @@ void removeImage(ImageLoader* image) } } - // flush find-by-address cache + // flush find-by-address cache (do this after removed from master list, so there is no chance it can come back) if ( sLastImageByAddressCache == image ) sLastImageByAddressCache = NULL; +#if IMAGE_NOTIFY_SUPPORT // if in announcement list, pull it out for (std::vector::iterator it=sImagesToNotifyAboutOtherImages.begin(); it != sImagesToNotifyAboutOtherImages.end(); it++) { if ( *it == image ) { @@ -284,6 +636,7 @@ void removeImage(ImageLoader* image) break; } } +#endif // if in root list, pull it out for (std::vector::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) { @@ -293,6 +646,11 @@ void removeImage(ImageLoader* image) } } + // log if requested + if ( sEnv.DYLD_PRINT_LIBRARIES || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { + dyld::log("dyld: unloaded: %s\n", image->getPath()); + } + // tell gdb, new way removeImageFromAllImages(image->machHeader()); @@ -317,22 +675,28 @@ const char* getExecutablePath() void initializeMainExecutable() { + +#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(); - for(int i=0; i < rootCount; ++i) { - ImageLoader* image = sImageRoots[i]; - //fprintf(stderr, "initializeMainExecutable: image = %p\n", image); - image->runInitializers(gLinkContext); - } -/* - // this does not work??? - for (std::vector::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) { - ImageLoader* image = *it; - fprintf(stderr, "initializeMainExecutable: image = %p\n", image); - // don't know why vector sometimes starts with NULL element??? - if ( image != NULL ) - image->runInitializers(gLinkContext); + if ( rootCount > 1 ) { + for(int i=1; i < rootCount; ++i) + sImageRoots[i]->runInitializers(gLinkContext); } -*/ + + // run initializers for main executable and everything it brings up + sMainExecutable->runInitializers(gLinkContext); + + // register atexit() handler to run terminators in all loaded images when this process exits + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->cxa_atexit)(&runTerminators, NULL, NULL); + + // dump info if requested if ( sEnv.DYLD_PRINT_STATISTICS ) ImageLoaderMachO::printStatistics(sAllImages.size()); } @@ -348,14 +712,16 @@ ImageLoader* mainExecutable() } -void runTerminators() +void runTerminators(void* extra) { const unsigned int imageCount = sImageFilesNeedingTermination.size(); 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); } @@ -397,6 +763,7 @@ static const char** parseColonList(const char* list) return (const char**)result; } + static void paths_expand_roots(const char **paths, const char *key, const char *val) { // assert(val != NULL); @@ -417,14 +784,19 @@ static void paths_expand_roots(const char **paths, const char *key, const char * static void removePathWithPrefix(const char* paths[], const char* prefix) { - size_t prefixLen = strlen(prefix); - for(int s=0,d=0; (paths[d] != NULL) && (paths[s] != NULL); ++s, ++d) { - if ( strncmp(paths[s], prefix, prefixLen) == 0 ) - ++s; - paths[d] = paths[s]; - } + size_t prefixLen = strlen(prefix); + int skip = 0; + int i; + for(i = 0; paths[i] != NULL; ++i) { + if ( strncmp(paths[i], prefix, prefixLen) == 0 ) + ++skip; + else + paths[i-skip] = paths[i]; + } + paths[i-skip] = NULL; } + #if 0 static void paths_dump(const char **paths) { @@ -432,7 +804,7 @@ static void paths_dump(const char **paths) const char **strs = paths; while(*strs != NULL) { - fprintf(stderr, "\"%s\"\n", *strs); + dyld::log("\"%s\"\n", *strs); strs++; } return; @@ -443,7 +815,7 @@ static void printOptions(const char* argv[]) { uint32_t i = 0; while ( NULL != argv[i] ) { - fprintf(stderr, "opt[%i] = \"%s\"\n", i, argv[i]); + dyld::log("opt[%i] = \"%s\"\n", i, argv[i]); i++; } } @@ -451,33 +823,36 @@ static void printOptions(const char* argv[]) static void printEnvironmentVariables(const char* envp[]) { while ( NULL != *envp ) { - fprintf(stderr, "%s\n", *envp); + dyld::log("%s\n", *envp); envp++; } } - - 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] != '/' ) { - fprintf(stderr, "dyld: warning DYLD_ROOT_PATH not used because it contains a non-absolute path\n"); + dyld::warn("DYLD_ROOT_PATH not used because it contains a non-absolute path\n"); sEnv.DYLD_ROOT_PATH = NULL; break; } @@ -485,16 +860,12 @@ void processDyldEnvironmentVarible(const char* key, const char* value) } } 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); - } - else if ( strcmp(key, "DYLD_DEBUG_TRACE") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_DEBUG_TRACE not supported\n"); - } - else if ( strcmp(key, "DYLD_ERROR_PRINT") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_ERROR_PRINT not supported\n"); + gSharedCacheDontNotify = true; } else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) { sEnv.DYLD_PRINT_OPTS = true; @@ -502,61 +873,39 @@ void processDyldEnvironmentVarible(const char* key, const char* value) else if ( strcmp(key, "DYLD_PRINT_ENV") == 0 ) { sEnv.DYLD_PRINT_ENV = true; } + else if ( strcmp(key, "DYLD_DISABLE_DOFS") == 0 ) { + sEnv.DYLD_DISABLE_DOFS = true; + } + else if ( strcmp(key, "DYLD_DISABLE_PREFETCH") == 0 ) { + gLinkContext.preFetchDisabled = true; + } else if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) { sEnv.DYLD_PRINT_LIBRARIES = true; } else if ( strcmp(key, "DYLD_PRINT_LIBRARIES_POST_LAUNCH") == 0 ) { sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH = true; } - else if ( strcmp(key, "DYLD_TRACE") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_TRACE not supported\n"); - } - else if ( strcmp(key, "DYLD_EBADEXEC_ONLY") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_EBADEXEC_ONLY not supported\n"); - } else if ( strcmp(key, "DYLD_BIND_AT_LAUNCH") == 0 ) { sEnv.DYLD_BIND_AT_LAUNCH = true; } else if ( strcmp(key, "DYLD_FORCE_FLAT_NAMESPACE") == 0 ) { gLinkContext.bindFlat = true; } - else if ( strcmp(key, "DYLD_DEAD_LOCK_HANG") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_DEAD_LOCK_HANG not supported\n"); - } - else if ( strcmp(key, "DYLD_ABORT_MULTIPLE_INITS") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_ABORT_MULTIPLE_INITS not supported\n"); - } else if ( strcmp(key, "DYLD_NEW_LOCAL_SHARED_REGIONS") == 0 ) { - gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; - } - else if ( strcmp(key, "DYLD_SLIDE_AND_PACK_DYLIBS") == 0 ) { - gLinkContext.slideAndPackDylibs = true; + // ignore, no longer relevant but some scripts still set it } else if ( strcmp(key, "DYLD_NO_FIX_PREBINDING") == 0 ) { - // since the new dyld never runs fix_prebinding, no need to warn if someone does not want it run - //fprintf(stderr, "dyld: warning DYLD_NO_FIX_PREBINDING not supported\n"); + gSharedCacheDontNotify = true; } else if ( strcmp(key, "DYLD_PREBIND_DEBUG") == 0 ) { gLinkContext.verbosePrebinding = true; } - else if ( strcmp(key, "DYLD_HINTS_DEBUG") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_HINTS_DEBUG not supported\n"); - } - else if ( strcmp(key, "DYLD_SAMPLE_DEBUG") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_SAMPLE_DEBUG not supported\n"); - } - else if ( strcmp(key, "DYLD_EXECUTABLE_PATH_DEBUG") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_EXECUTABLE_PATH_DEBUG not supported\n"); - } - else if ( strcmp(key, "DYLD_TWO_LEVEL_DEBUG") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_TWO_LEVEL_DEBUG not supported\n"); - } - else if ( strcmp(key, "DYLD_LAZY_INITIALIZERS") == 0 ) { - fprintf(stderr, "dyld: warning DYLD_LAZY_INITIALIZERS not supported\n"); - } else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) { gLinkContext.verboseInit = true; } + else if ( strcmp(key, "DYLD_PRINT_DOFS") == 0 ) { + gLinkContext.verboseDOF = true; + } else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) { sEnv.DYLD_PRINT_STATISTICS = true; } @@ -576,6 +925,7 @@ void processDyldEnvironmentVarible(const char* key, const char* value) gLinkContext.verboseWarnings = true; } else if ( strcmp(key, "DYLD_SHARED_REGION") == 0 ) { + gSharedCacheDontNotify = true; if ( strcmp(value, "private") == 0 ) { gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; } @@ -589,10 +939,11 @@ void processDyldEnvironmentVarible(const char* key, const char* value) gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; } else { - fprintf(stderr, "dyld: warning unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n"); + dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n"); } } else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) { + gSharedCacheDontNotify = true; if ( strcmp(value, "all") == 0 ) { gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; } @@ -606,14 +957,15 @@ void processDyldEnvironmentVarible(const char* key, const char* value) gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding; } else { - fprintf(stderr, "dyld: warning unknown option to DYLD_IGNORE_PREBINDING. Valid options are: all, app, nonsplit\n"); + dyld::warn("unknown option to DYLD_IGNORE_PREBINDING. Valid options are: all, app, nonsplit\n"); } } else { - fprintf(stderr, "dyld: warning, unknown environment variable: %s\n", key); + dyld::warn("unknown environment variable: %s\n", key); } } + // // For security, setuid programs ignore DYLD_* environment variables. // Additionally, the DYLD_* enviroment variables are removed @@ -621,6 +973,9 @@ 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; @@ -642,21 +997,12 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep) } while ( *d++ != NULL ); } - // setup DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment - if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { - const char** paths = sFrameworkFallbackPaths; - removePathWithPrefix(paths, "$HOME"); - sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = paths; - } - - // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment - if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) { - const char** paths = sLibraryFallbackPaths; - removePathWithPrefix(paths, "$HOME"); - sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths; - } + // disable framework and library fallback paths for setuid binaries rdar://problem/4589305 + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = NULL; + sEnv.DYLD_FALLBACK_LIBRARY_PATH = NULL; } + static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) { const char* home = NULL; @@ -682,7 +1028,7 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) sEnv.LD_LIBRARY_PATH = parseColonList(path); } } - + // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { const char** paths = sFrameworkFallbackPaths; @@ -707,7 +1053,7 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) static void getHostInfo() { -#if 0 +#if 1 struct host_basic_info info; mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; mach_port_t hostPort = mach_host_self(); @@ -718,17 +1064,30 @@ static void getHostInfo() sHostCPU = info.cpu_type; sHostCPUsubtype = info.cpu_subtype; -#endif - +#else size_t valSize = sizeof(sHostCPU); if (sysctlbyname ("hw.cputype", &sHostCPU, &valSize, NULL, 0) != 0) throw "sysctlbyname(hw.cputype) failed"; valSize = sizeof(sHostCPUsubtype); if (sysctlbyname ("hw.cpusubtype", &sHostCPUsubtype, &valSize, NULL, 0) != 0) throw "sysctlbyname(hw.cpusubtype) failed"; +#endif +} + +static void checkSharedRegionDisable() +{ + #if __ppc__ || __i386__ + // if main executable has segments that overlap the shared region, + // then disable using the shared region + if ( sMainExecutable->overlapsWithAddressRange((void*)0x90000000, (void*)0xAFFFFFFF) ) { + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + if ( gLinkContext.verboseMapping ) + dyld::warn("disabling shared region because main executable overlaps\n"); + } + #endif } -bool validImage(ImageLoader* possibleImage) +bool validImage(const ImageLoader* possibleImage) { const unsigned int imageCount = sAllImages.size(); for(unsigned int i=0; i < imageCount; ++i) { @@ -741,35 +1100,13 @@ bool validImage(ImageLoader* possibleImage) uint32_t getImageCount() { - if ( sAllImagesMightContainUnlinkedImages ) { - uint32_t count = 0; - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - if ( (*it)->isLinked() ) - ++count; - } - return count; - } - else { - return sAllImages.size(); - } + return sAllImages.size(); } ImageLoader* getIndexedImage(unsigned int index) { - if ( sAllImagesMightContainUnlinkedImages ) { - uint32_t count = 0; - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - if ( (*it)->isLinked() ) { - if ( index == count ) - return *it; - ++count; - } - } - } - else { - if ( index < sAllImages.size() ) - return sAllImages[index]; - } + if ( index < sAllImages.size() ) + return sAllImages[index]; return NULL; } @@ -792,7 +1129,7 @@ ImageLoader* findImageContainingAddress(const void* addr) static int cacheMiss = 0; static int cacheNotMacho = 0; if ( ((cacheHit+cacheMiss+cacheNotMacho) % 100) == 0 ) - fprintf(stderr, "findImageContainingAddress(): cache hit = %d, miss = %d, unknown = %d\n", cacheHit, cacheMiss, cacheNotMacho); + 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) ) { @@ -922,6 +1259,8 @@ static const char* getLibraryLeafName(const char* path) } +// only for architectures that use cpu-sub-types +#if CPU_SUBTYPES_SUPPORTED const cpu_subtype_t CPU_SUBTYPE_END_OF_LIST = -1; @@ -956,23 +1295,6 @@ static const cpu_subtype_t kPPC32[kPPC_RowCount][6] = { }; -// -// 64-bit PowerPC sub-type lists -// -const int kPPC64_RowCount = 1; -static const cpu_subtype_t kPPC64[kPPC64_RowCount][3] = { - // G5 can run any 64-bit code - { CPU_SUBTYPE_POWERPC_970, CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_END_OF_LIST }, -}; - - - -// -// 32-bit x86 sub-type lists -// -// TO-DO - - // 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) @@ -984,15 +1306,6 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub return kPPC32[i]; } break; - case CPU_TYPE_POWERPC64: - for (int i=0; i < kPPC64_RowCount ; ++i) { - if ( kPPC64[i][0] == subtype ) - return kPPC64[i]; - } - break; - case CPU_TYPE_I386: - // To do - break; } return NULL; } @@ -1007,7 +1320,7 @@ static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[ for (uint32_t subTypeIndex=0; list[subTypeIndex] != CPU_SUBTYPE_END_OF_LIST; ++subTypeIndex) { for(uint32_t fatIndex=0; fatIndex < OSSwapBigToHostInt32(fh->nfat_arch); ++fatIndex) { if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[fatIndex].cputype) == cpu) - && (list[subTypeIndex] == archs[fatIndex].cpusubtype) ) { + && (list[subTypeIndex] == (cpu_subtype_t)OSSwapBigToHostInt32(archs[fatIndex].cpusubtype)) ) { *offset = OSSwapBigToHostInt32(archs[fatIndex].offset); *len = OSSwapBigToHostInt32(archs[fatIndex].size); return true; @@ -1040,33 +1353,19 @@ static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) { switch (cpu) { case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_POWERPC_ALL ) { *offset = OSSwapBigToHostInt32(archs[i].offset); *len = OSSwapBigToHostInt32(archs[i].size); return true; } break; - case CPU_TYPE_I386: - if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_I386_ALL ) { - *offset = OSSwapBigToHostInt32(archs[i].offset); - *len = OSSwapBigToHostInt32(archs[i].size); - return true; - } - break; - case CPU_TYPE_X86_64: - if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) { - *offset = OSSwapBigToHostInt32(archs[i].offset); - *len = OSSwapBigToHostInt32(archs[i].size); - return true; - } - break; } } } return false; } +#endif // CPU_SUBTYPES_SUPPORTED // // A fat file may contain multiple sub-images for the same cpu-type, @@ -1075,6 +1374,7 @@ static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* // static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) { +#if CPU_SUBTYPES_SUPPORTED // assume all dylibs loaded must have same cpu type as main executable const cpu_type_t cpu = sMainExecutableMachHeader->cputype; @@ -1094,20 +1394,32 @@ static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) // running on an uknown cpu, can only load generic code return fatFindRunsOnAllCPUs(cpu, fh, offset, len); +#else + // just find first slice with matching architecture + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == sMainExecutableMachHeader->cputype) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + } + return false; +#endif } // // This is used to validate if a non-fat (aka thin or raw) mach-o file can be used -// on the current processor. It is deemed compatible if any of the following are true: -// 1) mach_header subtype is in list of compatible subtypes for running processor -// 2) mach_header subtype is same as running processor subtype -// 3) mach_header subtype runs on all processor variants -// -// +// on the current processor. // bool isCompatibleMachO(const uint8_t* firstPage) { +#if CPU_SUBTYPES_SUPPORTED + // It is deemed compatible if any of the following are true: + // 1) mach_header subtype is in list of compatible subtypes for running processor + // 2) mach_header subtype is same as running processor subtype + // 3) mach_header subtype runs on all processor variants const mach_header* mh = (mach_header*)firstPage; if ( mh->magic == sMainExecutableMachHeader->magic ) { if ( mh->cputype == sMainExecutableMachHeader->cputype ) { @@ -1128,36 +1440,44 @@ bool isCompatibleMachO(const uint8_t* firstPage) return true; } - // cpu unknown, so don't know if subtype is compatible - // only load _ALL variant + // cpu type has no ordered list of subtypes switch (mh->cputype) { case CPU_TYPE_POWERPC: - case CPU_TYPE_POWERPC64: + // allow _ALL to be used by any client if ( mh->cpusubtype == CPU_SUBTYPE_POWERPC_ALL ) return true; break; + case CPU_TYPE_POWERPC64: case CPU_TYPE_I386: - if ( mh->cpusubtype == CPU_SUBTYPE_I386_ALL ) - return true; - break; case CPU_TYPE_X86_64: - if ( mh->cpusubtype == CPU_SUBTYPE_X86_64_ALL ) - return true; - break; + // subtypes are not used or these architectures + return true; } } } +#else + // For architectures that don't support cpu-sub-types + // this just check the cpu type. + const mach_header* mh = (mach_header*)firstPage; + if ( mh->magic == sMainExecutableMachHeader->magic ) { + if ( mh->cputype == sMainExecutableMachHeader->cputype ) { + return true; + } + } +#endif return false; } + + // The kernel maps in main executable before dyld gets control. We need to // make an ImageLoader* for the already mapped in main executable. -static ImageLoader* instantiateFromLoadedImage(const struct mach_header* mh, const char* path) +static ImageLoader* instantiateFromLoadedImage(const struct mach_header* mh, uintptr_t slide, const char* path) { // try mach-o loader if ( isCompatibleMachO((const uint8_t*)mh) ) { - ImageLoader* image = new ImageLoaderMachO(path, mh, 0, gLinkContext); + ImageLoader* image = new ImageLoaderMachO(mh, slide, path, gLinkContext); addImage(image); return image; } @@ -1165,32 +1485,91 @@ static ImageLoader* instantiateFromLoadedImage(const struct mach_header* mh, con throw "main executable not a known format"; } +#if DYLD_SHARED_CACHE_SUPPORT +static ImageLoader* findSharedCacheImage(const struct stat& stat_buf, const char* path) +{ + if ( sSharedCache != NULL ) { + // walk shared cache to see if there is a cached image that matches the inode/mtime/path desired + const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)sSharedCache + sSharedCache->imagesOffset); + const dyld_cache_image_info* const end = &start[sSharedCache->imagesCount]; + for( const dyld_cache_image_info* p = start; p != end; ++p) { + // check mtime and inode first because it is fast + if ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) { + // mod-time and inode match an image in the shared cache, now check path + const char* pathInCache = (char*)sSharedCache + p->pathFileOffset; + bool cacheHit = (strcmp(path, pathInCache) == 0); + if ( ! cacheHit ) { + // path does not match install name of dylib in cache, but inode and mtime does match + // perhaps path is a symlink to the cached dylib + struct stat pathInCacheStatBuf; + if ( stat(pathInCache, &pathInCacheStatBuf) != -1 ) + cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf.st_dev) && (pathInCacheStatBuf.st_ino == stat_buf.st_ino) ); + } + if ( cacheHit ) { + // found image in cache, instantiate an ImageLoader with it + return new ImageLoaderMachO((struct mach_header*)(p->address), pathInCache, stat_buf, gLinkContext); + } + } + } + } + return NULL; +} +#endif + +static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context) +{ + // now sanity check that this loaded image does not have the same install path as any existing image + const char* loadedImageInstallPath = image->getInstallPath(); + if ( image->isDylib() && (loadedImageInstallPath != NULL) && (loadedImageInstallPath[0] == '/') ) { + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* anImage = *it; + const char* installPath = anImage->getInstallPath(); + if ( installPath != NULL) { + if ( strcmp(loadedImageInstallPath, installPath) == 0 ) { + //dyld::log("duplicate(%s) => %p\n", installPath, anImage); + delete image; + return anImage; + } + } + } + } + // some API's restrict what they can load + if ( context.mustBeBundle && !image->isBundle() ) + throw "not a bundle"; + if ( context.mustBeDylib && !image->isDylib() ) + throw "not a dylib"; + // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list + if ( ! image->isBundle() ) + addImage(image); + + return image; +} // map in file and instantiate an ImageLoader static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, const LoadContext& context) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //dyld::log("%s(%s)\n", __func__ , path); uint64_t fileOffset = 0; uint64_t fileLength = stat_buf.st_size; -#if __ppc64__ - if ( *((uint32_t*)((char*)(&stat_buf)+0x60)) == 0xFEFEFEFE ) - fileLength = *((uint64_t*)((char*)(&stat_buf)+0x30)); // HACK work around for kernel stat bug rdar://problem/3845883 -#endif // validate it is a file (not directory) if ( (stat_buf.st_mode & S_IFMT) != S_IFREG ) throw "not a file"; - // min file is 4K + uint8_t firstPage[4096]; + bool shortPage = false; + + // min mach-o file is 4K if ( fileLength < 4096 ) { - throw "file to short"; + pread(fd, firstPage, fileLength, 0); + shortPage = true; + } + else { + pread(fd, firstPage, 4096,0); } - uint8_t firstPage[4096]; - pread(fd, firstPage, 4096,0); - // if fat wrapper, find usable sub-file const fat_header* fileStartAsFat = (fat_header*)firstPage; if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { @@ -1204,46 +1583,17 @@ static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, // try mach-o loader if ( isCompatibleMachO(firstPage) ) { - char realFilePath[PATH_MAX]; - if ( gLinkContext.slideAndPackDylibs ) { - // when prebinding, we always want to track the real path of images - if ( realpath(path, realFilePath) != NULL ) - path = realFilePath; - } + if ( shortPage ) + throw "file too short"; // instantiate an image ImageLoader* image = new ImageLoaderMachO(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext); - // now sanity check that this loaded image does not have the same install path as any existing image - const char* loadedImageInstallPath = image->getInstallPath(); - if ( image->isDylib() && (loadedImageInstallPath != NULL) && (loadedImageInstallPath[0] == '/') ) { - for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { - ImageLoader* anImage = *it; - const char* installPath = anImage->getInstallPath(); - if ( installPath != NULL) { - if ( strcmp(loadedImageInstallPath, installPath) == 0 ) { - //fprintf(stderr, "duplicate(%s) => %p\n", installPath, anImage); - delete image; - return anImage; - } - } - } - } - - // some API's restrict what they can load - if ( context.mustBeBundle && !image->isBundle() ) - throw "not a bundle"; - if ( context.mustBeDylib && !image->isDylib() ) - throw "not a dylib"; - - // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list - if ( ! image->isBundle() ) - addImage(image); - - return image; + // validate + return checkandAddImage(image, context); } - // try other file formats... + // try other file formats here... // throw error about what was found @@ -1263,40 +1613,50 @@ static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, // try to open file static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, std::vector* exceptions) { - //fprintf(stdout, "%s(%s)\n", __func__, path); + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); ImageLoader* image = NULL; - // open file (automagically closed when this function exits) - FileOpener file(path); - - //fprintf(stderr, "open(%s) => %d\n", path, file.getFileDescriptor() ); - - if ( file.getFileDescriptor() == -1 ) - return NULL; - + // just return NULL if file not found, but record any other errors struct stat stat_buf; -#if __ppc64__ - memset(&stat_buf, 254, sizeof(struct stat)); // hack until rdar://problem/3845883 is fixed -#endif - if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) - throw "stat error"; + if ( stat(path, &stat_buf) == -1 ) { + int err = errno; + if ( err != ENOENT ) { + exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, err)); + } + return NULL; + } // in case image was renamed or found via symlinks, check for inode match image = findLoadedImage(stat_buf); if ( image != NULL ) return image; - // needed to implement NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED + // do nothing if not already loaded and if RTLD_NOLOAD or NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED if ( context.dontLoad ) return NULL; + +#if DYLD_SHARED_CACHE_SUPPORT + // see if this image is in shared cache + image = findSharedCacheImage(stat_buf, path); + if ( image != NULL ) { + return checkandAddImage(image, context); + } +#endif + // open file (automagically closed when this function exits) + FileOpener file(path); + + // just return NULL if file not found + if ( file.getFileDescriptor() == -1 ) + return NULL; + try { return loadPhase6(file.getFileDescriptor(), stat_buf, path, context); } catch (const char* msg) { - char* newMsg = new char[strlen(msg) + strlen(path) + 8]; - sprintf(newMsg, "%s: %s", path, msg); + const char* newMsg = dyld::mkstringf("%s: %s", path, msg); exceptions->push_back(newMsg); + free((void*)msg); return NULL; } } @@ -1304,7 +1664,7 @@ static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, // look for path match with existing loaded images static ImageLoader* loadPhase5check(const char* path, const LoadContext& context) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //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); for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { @@ -1328,7 +1688,7 @@ static ImageLoader* loadPhase5check(const char* path, const LoadContext& context } } - //fprintf(stderr, "check(%s) => NULL\n", path); + //dyld::log("%s(%s) => NULL\n", __func__, path); return NULL; } @@ -1336,7 +1696,7 @@ static ImageLoader* loadPhase5check(const char* path, const LoadContext& context // open or check existing static ImageLoader* loadPhase5(const char* path, const LoadContext& context, std::vector* exceptions) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); if ( exceptions != NULL ) return loadPhase5open(path, context, exceptions); else @@ -1346,7 +1706,7 @@ static ImageLoader* loadPhase5(const char* path, const LoadContext& context, std // try with and without image suffix static ImageLoader* loadPhase4(const char* path, const LoadContext& context, std::vector* exceptions) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); ImageLoader* image = NULL; if ( gLinkContext.imageSuffix != NULL ) { char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2]; @@ -1358,13 +1718,21 @@ static ImageLoader* loadPhase4(const char* path, const LoadContext& context, std return image; } +static ImageLoader* loadPhase2(const char* path, const LoadContext& context, + const char* const frameworkPaths[], const char* const libraryPaths[], + std::vector* exceptions); // forward reference + // expand @ variables static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std::vector* exceptions) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); 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); + } // handle @executable_path path prefix const char* executablePath = sExecPath; char newPath[strlen(executablePath) + strlen(path)]; @@ -1394,6 +1762,10 @@ 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); + // handle @loader_path path prefix char newPath[strlen(context.origin) + strlen(path)]; strcpy(newPath, context.origin); @@ -1421,6 +1793,38 @@ static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std return image; } } + else if ( context.implicitRPath || (strncmp(path, "@rpath/", 7) == 0) ) { + const char* trailingPath = (strncmp(path, "@rpath/", 7) == 0) ? &path[7] : path; + // substitute @rpath with all -rpath paths up the load chain + for(const ImageLoader::RPathChain* rp=context.rpath; rp != NULL; rp=rp->next) { + if (rp->paths != NULL ) { + for(std::vector::iterator it=rp->paths->begin(); it != rp->paths->end(); ++it) { + const char* anRPath = *it; + char newPath[strlen(anRPath) + strlen(trailingPath)+2]; + strcpy(newPath, anRPath); + strcat(newPath, "/"); + strcat(newPath, trailingPath); + image = loadPhase4(newPath, context, exceptions); + if ( image != NULL ) + return image; + } + } + } + + // substitute @rpath with LD_LIBRARY_PATH + if ( sEnv.LD_LIBRARY_PATH != NULL ) { + image = loadPhase2(trailingPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions); + if ( image != NULL ) + return image; + } + + // if this is the "open" pass, don't try to open @rpath/... as a relative path + if ( (exceptions != NULL) && (trailingPath != path) ) + return NULL; + } + else if ( sMainExecutableIsSetuid && (path[0] != '/') ) { + throwf("unsafe use of relative rpath %s in %s with setuid binary", path, context.origin); + } return loadPhase4(path, context, exceptions); } @@ -1431,7 +1835,7 @@ static ImageLoader* loadPhase2(const char* path, const LoadContext& context, const char* const frameworkPaths[], const char* const libraryPaths[], std::vector* exceptions) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); ImageLoader* image = NULL; const char* frameworkPartialPath = getFrameworkPartialPath(path); if ( frameworkPaths != NULL ) { @@ -1442,7 +1846,7 @@ static ImageLoader* loadPhase2(const char* path, const LoadContext& context, strcpy(npath, *fp); strcat(npath, "/"); strcat(npath, frameworkPartialPath); - //fprintf(stderr, "dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath); + //dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath); image = loadPhase4(npath, context, exceptions); if ( image != NULL ) return image; @@ -1457,7 +1861,7 @@ static ImageLoader* loadPhase2(const char* path, const LoadContext& context, strcpy(libpath, *lp); strcat(libpath, "/"); strcat(libpath, libraryLeafName); - //fprintf(stderr, "dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath); + //dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath); image = loadPhase4(libpath, context, exceptions); if ( image != NULL ) return image; @@ -1469,7 +1873,7 @@ static ImageLoader* loadPhase2(const char* path, const LoadContext& context, // try search overrides and fallbacks static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std::vector* exceptions) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); ImageLoader* image = NULL; // handle LD_LIBRARY_PATH environment variables that force searching @@ -1492,7 +1896,7 @@ static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std return image; // try fallback paths during second time (will open file) - if ( (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_FALLBACK_LIBRARY_PATH != NULL)) ) { + 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); if ( image != NULL ) return image; @@ -1504,8 +1908,8 @@ static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std // try root substitutions static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std::vector* exceptions) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); - + //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) { @@ -1538,7 +1942,7 @@ static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std // ImageLoader* load(const char* path, const LoadContext& context) { - //fprintf(stderr, "%s(%s)\n", __func__ , path); + //dyld::log("%s(%s)\n", __func__ , path); char realPath[PATH_MAX]; // when DYLD_IMAGE_SUFFIX is in used, do a realpath(), otherwise a load of "Foo.framework/Foo" will not match if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL) ) { @@ -1550,14 +1954,12 @@ ImageLoader* load(const char* path, const LoadContext& context) ImageLoader* image = loadPhase0(path, context, NULL); if ( image != NULL ) return image; - + // try all path permutations and try open() until first sucesss std::vector exceptions; image = loadPhase0(path, context, &exceptions); if ( image != NULL ) return image; - else if ( context.dontLoad ) - return NULL; else if ( exceptions.size() == 0 ) throw "image not found"; else { @@ -1571,6 +1973,7 @@ ImageLoader* load(const char* path, const LoadContext& context) for (unsigned int i=0; i < exceptions.size(); ++i) { strcat(fullMsg, delim); strcat(fullMsg, exceptions[i]); + free((void*)exceptions[i]); } throw (const char*)fullMsg; } @@ -1579,6 +1982,219 @@ ImageLoader* load(const char* path, const LoadContext& context) +#if DYLD_SHARED_CACHE_SUPPORT + + +// hack until dyld no longer needs to run on Leopard kernels that don't have new shared region syscall +static bool newSharedRegionSyscallAvailable() +{ + int shreg_version; + size_t buffer_size = sizeof(shreg_version); + if ( sysctlbyname("vm.shared_region_version", &shreg_version, &buffer_size, NULL, 0) == 0 ) { + if ( shreg_version == 3 ) + return true; + } + return false; +} + + +static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address) +{ + if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) && newSharedRegionSyscallAvailable() ) + return syscall(294, start_address); + return -1; +} + + +static int __attribute__((noinline)) _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[]) +{ + int result; + if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) && newSharedRegionSyscallAvailable() ) { + return syscall(295, fd, count, mappings); + } + + // 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 + + // map cache just for this process with mmap() + bool failed = false; + const shared_file_mapping_np* start = mappings; + const shared_file_mapping_np* end = &mappings[count]; + for (const shared_file_mapping_np* p = start; p < end; ++p ) { + void* mmapAddress = (void*)(uintptr_t)(p->sfm_address); + size_t size = p->sfm_size; + int protection = 0; + if ( p->sfm_init_prot & VM_PROT_EXECUTE ) + protection |= PROT_EXEC; + if ( p->sfm_init_prot & VM_PROT_READ ) + protection |= PROT_READ; + if ( p->sfm_init_prot & VM_PROT_WRITE ) + protection |= PROT_WRITE; + off_t offset = p->sfm_file_offset; + mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); + if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress ) + failed = true; + } + if ( !failed ) { + result = 0; + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; + } + else { + result = -1; + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: shared cached cannot be mapped\n"); + } + + return result; +} + + + +#if __ppc__ + #define ARCH_NAME "ppc" + #define ARCH_NAME_ROSETTA "rosetta" + #define ARCH_VALUE CPU_TYPE_POWERPC + #define ARCH_CACHE_MAGIC "dyld_v1 ppc" +#elif __ppc64__ + #define ARCH_NAME "ppc64" + #define ARCH_VALUE CPU_TYPE_POWERPC64 + #define ARCH_CACHE_MAGIC "dyld_v1 ppc64" +#elif __i386__ + #define ARCH_NAME "i386" + #define ARCH_VALUE CPU_TYPE_I386 + #define ARCH_CACHE_MAGIC "dyld_v1 i386" +#elif __x86_64__ + #define ARCH_NAME "x86_64" + #define ARCH_VALUE CPU_TYPE_X86_64 + #define ARCH_CACHE_MAGIC "dyld_v1 x86_64" +#endif + + +static void mapSharedCache() +{ + uint64_t cacheBaseAddress; + // quick check if a cache is alreay mapped into shared region + if ( _shared_region_check_np(&cacheBaseAddress) == 0 ) { + sSharedCache = (dyld_cache_header*)cacheBaseAddress; + // if we don't understand the currently mapped shared cache, then ignore + if ( strcmp(sSharedCache->magic, ARCH_CACHE_MAGIC) != 0 ) { + sSharedCache = NULL; + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: existing shared cached in memory is not compatible\n"); + } + } + else { + // map in shared cache to shared region + int fd; +#if __ppc__ + // rosetta cannot handle optimized _ppc cache, so it use _rosetta cache instead, rdar://problem/5495438 + if ( isRosetta() ) + fd = open(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_ROSETTA, O_RDONLY); + else +#endif + fd = open(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, O_RDONLY); + if ( fd != -1 ) { + uint8_t firstPage[4096]; + if ( ::read(fd, firstPage, 4096) == 4096 ) { + dyld_cache_header* header = (dyld_cache_header*)firstPage; + 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* const end = &mappings[header->mappingCount]; + // validate that the cache file has not been truncated + bool goodCache = false; + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == 0 ) { + goodCache = true; + for (const shared_file_mapping_np* p = mappings; p < end; ++p) { + if ( p->sfm_file_offset+p->sfm_size > (uint64_t)stat_buf.st_size ) + goodCache = false; + } + } + if ( goodCache ) { + const shared_file_mapping_np* mappings = (shared_file_mapping_np*)&firstPage[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"); + } + } + + // remember if dyld loaded at same address as when cache built + if ( sSharedCache != NULL ) { + gLinkContext.dyldLoadedAtSameAddressNeededBySharedCache = ((uintptr_t)(sSharedCache->dyldBaseAddress) == (uintptr_t)&_mh_dylinker_header); + } + + // tell gdb where the shared cache is + if ( sSharedCache != NULL ) { + const shared_file_mapping_np* const start = (shared_file_mapping_np*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset); + dyld_shared_cache_ranges.sharedRegionsCount = sSharedCache->mappingCount; + // only room to tell gdb about first four regions + if ( dyld_shared_cache_ranges.sharedRegionsCount > 4 ) + dyld_shared_cache_ranges.sharedRegionsCount = 4; + if ( gLinkContext.verboseMapping ) { + if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) + dyld::log("dyld: Mapping shared cache\n"); + else if ( gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion ) + dyld::log("dyld: Mapping private shared cache\n"); + } + const shared_file_mapping_np* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount]; + int index = 0; + for (const shared_file_mapping_np* p = start; p < end; ++p, ++index ) { + dyld_shared_cache_ranges.ranges[index].start = p->sfm_address; + dyld_shared_cache_ranges.ranges[index].length = p->sfm_size; + if ( gLinkContext.verboseMapping ) { + dyld::log(" 0x%08llX->0x%08llX %s%s%s init=%x, max=%x\n", p->sfm_address, p->sfm_address+p->sfm_size-1, + ((p->sfm_init_prot & VM_PROT_READ) ? "read " : ""), + ((p->sfm_init_prot & VM_PROT_WRITE) ? "write " : ""), + ((p->sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : ""), p->sfm_init_prot, p->sfm_max_prot); + } + #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 ( (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); + } + #endif + } + + } +} +#endif // #if DYLD_SHARED_CACHE_SUPPORT + + + // create when NSLinkModule is called for a second time on a bundle ImageLoader* cloneImage(ImageLoader* image) { @@ -1588,9 +2204,6 @@ ImageLoader* cloneImage(ImageLoader* image) FileOpener file(image->getPath()); struct stat stat_buf; -#if __ppc64__ - memset(&stat_buf, 254, sizeof(struct stat)); // hack until rdar://problem/3845883 is fixed -#endif if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) throw "stat error"; @@ -1631,7 +2244,7 @@ ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* module } } - // try mach-o each loader + // try each loader if ( isCompatibleMachO(mem) ) { ImageLoader* image = new ImageLoaderMachO(moduleName, (mach_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 @@ -1640,7 +2253,7 @@ ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* module return image; } - // try other file formats... + // try other file formats here... // throw error about what was found switch (*(uint32_t*)mem) { @@ -1661,17 +2274,12 @@ void registerAddCallback(ImageCallback func) // now add to list to get notified when any more images are added sAddImageCallbacks.push_back(func); - // call callback with all existing images, starting at roots - const int rootCount = sImageRoots.size(); - for(int i=0; i < rootCount; ++i) { - ImageLoader* image = sImageRoots[i]; - image->runNotification(gLinkContext, sAddImageCallbacks.size()); + // call callback with all existing images + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) + (*func)(image->machHeader(), image->getSlide()); } - -// for (std::vector::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) { -// ImageLoader* image = *it; -// image->runNotification(gLinkContext, sAddImageCallbacks.size()); -// } } void registerRemoveCallback(ImageCallback func) @@ -1696,21 +2304,14 @@ const char* getErrorMessage() return error_string; } + void halt(const char* message) { - fprintf(stderr, "dyld: %s\n", message); + dyld::log("dyld: %s\n", message); setErrorMessage(message); strncpy(error_string, message, sizeof(error_string)-1); error_string[sizeof(error_string)-1] = '\0'; - -#if __ppc__ || __ppc64__ - __asm__ ("trap"); -#elif __i386__ || __x86_64__ - __asm__ ("int3"); -#else - #error unknown architecture -#endif - abort(); // needed to suppress warning that noreturn function returns + dyld_fatal_error(error_string); } @@ -1719,8 +2320,8 @@ uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) uintptr_t result = 0; // acquire read-lock on dyld's data structures #if 0 // rdar://problem/3811777 turn off locking until deadlock is resolved - if ( gThreadHelpers != NULL ) - (*gThreadHelpers->lockForReading)(); + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->lockForReading)(); #endif // lookup and bind lazy pointer and get target address try { @@ -1736,17 +2337,17 @@ uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) target = dyld::findImageByMachHeader(mh); #endif if ( target == NULL ) - throw "image not found for lazy pointer"; + throwf("image not found for lazy pointer at %p", lazyPointer); result = target->doBindLazySymbol(lazyPointer, gLinkContext); } catch (const char* message) { - fprintf(stderr, "dyld: lazy symbol binding failed: %s\n", message); + dyld::log("dyld: lazy symbol binding failed: %s\n", message); halt(message); } // release read-lock on dyld's data structures #if 0 - if ( gThreadHelpers != NULL ) - (*gThreadHelpers->unlockForReading)(); + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->unlockForReading)(); #endif // return target address to glue which jumps to it with real parameters restored return result; @@ -1772,7 +2373,7 @@ static void undefinedHandler(const char* symboName) } } -static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, ImageLoader** image) +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 ) { @@ -1789,10 +2390,15 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima return false; if ( zlImage != NULL ) { // ZeroLink cache knows where the symbol is - *sym = zlImage->findExportedSymbol(name, NULL, false, image); - if ( *sym != NULL ) { - *image = zlImage; - return true; + 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 { @@ -1801,7 +2407,7 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima for(unsigned int i=0; i < imageCount; ++i){ ImageLoader* anImage = sAllImages[i]; if ( anImage->isBundle() && !anImage->hasHiddenExports() ) { - //fprintf(stderr, "dyld: search for %s in %s\n", name, anImage->getPath()); + //dyld::log("dyld: search for %s in %s\n", name, anImage->getPath()); *sym = anImage->findExportedSymbol(name, NULL, false, image); if ( *sym != NULL ) { return true; @@ -1812,11 +2418,19 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima } // search all images in order - ImageLoader* firstWeakImage = NULL; + const ImageLoader* firstWeakImage = NULL; const ImageLoader::Symbol* firstWeakSym = NULL; const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i){ + for(unsigned int i=0; i < imageCount; ++i) { ImageLoader* anImage = sAllImages[i]; + // the use of inserted libraries alters search order + // so that inserted libraries are found before the main executable + if ( sInsertedDylibCount > 0 ) { + if ( i < sInsertedDylibCount ) + anImage = sAllImages[i+1]; + else if ( i == sInsertedDylibCount ) + anImage = sAllImages[0]; + } if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) { *sym = anImage->findExportedSymbol(name, NULL, false, image); if ( *sym != NULL ) { @@ -1844,18 +2458,18 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima return false; } -bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, ImageLoader** image) +bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) { return findExportedSymbol(name, false, sym, image); } -bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, ImageLoader** image) +bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) { return findExportedSymbol(name, true, sym, image); } -bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, ImageLoader** image) +bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image) { // search all images in order const unsigned int imageCount = sAllImages.size(); @@ -1872,52 +2486,137 @@ bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstri return false; } -static void getMappedRegions(ImageLoader::RegionsVector& regions) +static ImageLoader::MappedRegion* getMappedRegions(ImageLoader::MappedRegion* regions) { - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i){ - ImageLoader* anImage = sAllImages[i]; - anImage->addMappedRegions(regions); + ImageLoader::MappedRegion* end = regions; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + (*it)->getMappedRegions(end); + } + return end; +} + +void registerImageStateSingleChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) +{ + // mark the image that the handler is in as never-unload because dyld has a reference into it + ImageLoader* handlerImage = findImageContainingAddress((void*)handler); + if ( handlerImage != NULL ) + handlerImage->setNeverUnload(); + + // add to list of handlers + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + handlers->push_back(handler); + + // call callback with all existing images + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getPath(); + info.imageFileModDate = image->lastModified(); + // should only call handler if state == image->state + if ( image->getState() == state ) + (*handler)(state, 1, &info); + // ignore returned string, too late to do anything + } } } +void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) +{ + // mark the image that the handler is in as never-unload because dyld has a reference into it + ImageLoader* handlerImage = findImageContainingAddress((void*)handler); + if ( handlerImage != NULL ) + handlerImage->setNeverUnload(); + + // add to list of handlers + std::vector* handlers = stateToHandlers(state, sBatchHandlers); + if ( handlers != NULL ) { + // insert at front, so that gdb handler is always last + handlers->insert(handlers->begin(), handler); + + // call callback with all existing images + try { + notifyBatchPartial(state, true, handler); + } + catch (const char* msg) { + // ignore request to abort during registration + } + } +} -static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const char* rpath[]) +static ImageLoader* libraryLocator(const char* libraryName, bool search, bool findDLL, const char* origin, const ImageLoader::RPathChain* rpaths) { dyld::LoadContext context; context.useSearchPaths = search; context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; context.dontLoad = false; context.mustBeBundle = false; context.mustBeDylib = true; - context.matchByInstallName = false; + context.findDLL = findDLL; context.origin = origin; - context.rpath = rpath; + context.rpath = rpaths; return load(libraryName, context); } - -static void setContext(int argc, const char* argv[], const char* envp[], const char* apple[]) +static const char* basename(const char* path) +{ + const char* last = path; + for (const char* s = path; *s != '\0'; s++) { + if (*s == '/') + last = s+1; + } + return last; +} + +static void setContext(const struct mach_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) { gLinkContext.loadLibrary = &libraryLocator; - gLinkContext.imageNotification = &imageNotification; gLinkContext.terminationRecorder = &terminationRecorder; gLinkContext.flatExportFinder = &flatFindExportedSymbol; gLinkContext.coalescedExportFinder = &findCoalescedExportedSymbol; gLinkContext.undefinedHandler = &undefinedHandler; +#if IMAGE_NOTIFY_SUPPORT gLinkContext.addImageNeedingNotification = &addImageNeedingNotification; gLinkContext.notifyAdding = ¬ifyAdding; +#endif gLinkContext.getAllMappedRegions = &getMappedRegions; gLinkContext.bindingHandler = NULL; + gLinkContext.notifySingle = ¬ifySingle; + gLinkContext.notifyBatch = ¬ifyBatch; + gLinkContext.removeImage = &removeImage; + gLinkContext.registerDOFs = ®isterDOFs; + gLinkContext.clearAllDepths = &clearAllDepths; + gLinkContext.imageCount = &imageCount; + gLinkContext.notifySharedCacheInvalid= ¬ifySharedCacheInvalid; +#if __i386__ + gLinkContext.makeSharedCacheImportSegmentsWritable = &makeSharedCacheImportSegmentsWritable; +#endif + gLinkContext.setNewProgramVars = &setNewProgramVars; +#if SUPPORT_OLD_CRT_INITIALIZATION + gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay; +#endif gLinkContext.bindingOptions = ImageLoader::kBindingNone; - gLinkContext.mainExecutable = sMainExecutable; gLinkContext.argc = argc; gLinkContext.argv = argv; gLinkContext.envp = envp; gLinkContext.apple = apple; + gLinkContext.progname = (argv[0] != NULL) ? basename(argv[0]) : ""; + gLinkContext.programVars.mh = mainExecutableMH; + gLinkContext.programVars.NXArgcPtr = &gLinkContext.argc; + gLinkContext.programVars.NXArgvPtr = &gLinkContext.argv; + gLinkContext.programVars.environPtr = &gLinkContext.envp; + gLinkContext.programVars.__prognamePtr=&gLinkContext.progname; + gLinkContext.mainExecutable = NULL; + gLinkContext.imageSuffix = NULL; + gLinkContext.prebindUsage = ImageLoader::kUseAllPrebinding; + gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; } -static bool isRosetta() +#if __ppc__ || __i386__ +bool isRosetta() { int mib[] = { CTL_KERN, KERN_CLASSIC, getpid() }; int is_classic = 0; @@ -1929,11 +2628,26 @@ static bool isRosetta() } return false; } +#endif + +#if 0 +static void printAllImages() +{ + dyld::log("printAllImages()\n"); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + dyld_image_states imageState = image->getState(); + dyld::log(" state=%d, refcount=%d, name=%s\n", imageState, image->referenceCount(), image->getShortName()); + image->printReferenceCounts(); + } +} +#endif + -void link(ImageLoader* image, ImageLoader::BindingLaziness bindness, ImageLoader::InitializerRunning runInitializers) +void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChain& loaderRPaths) { // add to list of known images. This did not happen at creation time for bundles - if ( image->isBundle() ) + if ( image->isBundle() && !image->isLinked() ) addImage(image); // we detect root images as those not linked in yet @@ -1951,11 +2665,11 @@ void link(ImageLoader* image, ImageLoader::BindingLaziness bindness, ImageLoader // process images try { - image->link(gLinkContext, bindness, runInitializers, sAddImageCallbacks.size()); + image->link(gLinkContext, forceLazysBound, false, loaderRPaths); } catch (const char* msg) { - sAllImagesMightContainUnlinkedImages = true; - throw msg; + garbageCollectImages(); + throw; } #if OLD_GDB_DYLD_INTERFACE @@ -1965,16 +2679,86 @@ void link(ImageLoader* image, ImageLoader::BindingLaziness bindness, ImageLoader } -// -// _pthread_keys is partitioned in a lower part that dyld will use; libSystem -// will use the upper part. We set __pthread_tsd_first to 1 as the start of -// the lower part. Libc will take #1 and c++ exceptions will take #2. There -// is one free key=3 left. -// -extern "C" { - extern int __pthread_tsd_first; +void runInitializers(ImageLoader* image) +{ + // do bottom up initialization + image->runInitializers(gLinkContext); +} + +void garbageCollectImages() +{ + // keep scanning list of images until entire list is scanned with no unreferenced images + bool mightBeUnreferencedImages = true; + while ( mightBeUnreferencedImages ) { + mightBeUnreferencedImages = false; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( (image->referenceCount() == 0) && !image->neverUnload() && !image->isBeingRemoved() ) { + try { + //dyld::log("garbageCollectImages: deleting %s\n", image->getPath()); + image->setBeingRemoved(); + removeImage(image); + delete image; + } + catch (const char* msg) { + dyld::warn("problem deleting image: %s\n", msg); + } + mightBeUnreferencedImages = true; + break; + } + } + } + //printAllImages(); +} + + +static void preflight_finally(ImageLoader* image) +{ + if ( image->isBundle() ) { + removeImageFromAllImages(image->machHeader()); + delete image; + } + sBundleBeingLoaded = NULL; + dyld::garbageCollectImages(); +} + + +void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths) +{ + try { + if ( image->isBundle() ) + sBundleBeingLoaded = image; // hack + image->link(gLinkContext, false, true, loaderRPaths); + } + catch (const char* msg) { + preflight_finally(image); + throw; + } + preflight_finally(image); +} + +static void loadInsertedDylib(const char* path) +{ + ImageLoader* image = NULL; + try { + LoadContext context; + context.useSearchPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = true; + context.findDLL = false; + context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES + context.rpath = NULL; + image = load(path, context); + image->setNeverUnload(); + } + catch (...) { + halt(dyld::mkstringf("could not load inserted library: %s\n", path)); + } } - // // Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which @@ -1983,10 +2767,9 @@ extern "C" { // Returns address of main() in target program which __dyld_start jumps to // uintptr_t -_main(const struct mach_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) +_main(const struct mach_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[]) { - // set pthread keys to dyld range - __pthread_tsd_first = 1; + setContext(mainExecutableMH, argc, argv, envp, apple); // Pickup the pointer to the exec path. sExecPath = apple[0]; @@ -1998,7 +2781,6 @@ _main(const struct mach_header* mainExecutableMH, int argc, const char* argv[], // we want any i386 dylibs (e.g. any used by Rosetta) to not load in the shared region // because the shared region is being used by ppc dylibs gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; - sExecPath = strdup(apple[0] + strlen(apple[0]) + 1); ignoreEnvironmentVariables = true; } #endif @@ -2016,7 +2798,8 @@ _main(const struct mach_header* mainExecutableMH, int argc, const char* argv[], } uintptr_t result = 0; sMainExecutableMachHeader = mainExecutableMH; - if ( issetugid() ) + sMainExecutableIsSetuid = issetugid(); + if ( sMainExecutableIsSetuid ) pruneEnvironmentVariables(envp, &apple); else checkEnvironmentVariables(envp, ignoreEnvironmentVariables); @@ -2025,75 +2808,69 @@ _main(const struct mach_header* mainExecutableMH, int argc, const char* argv[], if ( sEnv.DYLD_PRINT_ENV ) printEnvironmentVariables(envp); getHostInfo(); - setContext(argc, argv, envp, apple); - ImageLoader::BindingLaziness bindness = sEnv.DYLD_BIND_AT_LAUNCH ? ImageLoader::kLazyAndNonLazy : ImageLoader::kNonLazyOnly; + // 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); + sImageRoots.reserve(16); + sAddImageCallbacks.reserve(4); + sRemoveImageCallbacks.reserve(4); + sImageFilesNeedingTermination.reserve(16); + sImageFilesNeedingDOFUnregistration.reserve(8); - // load any inserted libraries before loading the main executable so that they are first in flat namespace - int insertLibrariesCount = 0; - if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) { - for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) { - insertLibrariesCount++; - } - } - ImageLoader* insertedImages[insertLibrariesCount]; - if ( insertLibrariesCount > 0 ) { - for (int i=0; i < insertLibrariesCount; ++i) { - try { - LoadContext context; - context.useSearchPaths = false; - context.useLdLibraryPath = false; - context.dontLoad = false; - context.mustBeBundle = false; - context.mustBeDylib = true; - context.matchByInstallName = false; - context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES - context.rpath = NULL; - insertedImages[i] = load(sEnv.DYLD_INSERT_LIBRARIES[i], context); - } - catch (...) { - char buf[strlen(sEnv.DYLD_INSERT_LIBRARIES[i])+50]; - sprintf(buf, "could not load inserted library: %s\n", sEnv.DYLD_INSERT_LIBRARIES[i]); - insertedImages[i] = NULL; - halt(buf); - } - } - } - - // load and link main executable try { - sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, sExecPath); + // instantiate ImageLoader for main executable + sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); + sMainExecutable->setNeverUnload(); gLinkContext.mainExecutable = sMainExecutable; + // load shared cache + checkSharedRegionDisable(); + #if DYLD_SHARED_CACHE_SUPPORT + if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) + mapSharedCache(); + #endif + // load any inserted libraries + if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) { + for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) + loadInsertedDylib(*lib); + } + // record count of inserted libraries so that a flat search will look at + // inserted libraries, then main, then others. + sInsertedDylibCount = sAllImages.size()-1; + + // link main executable + gLinkContext.linkingMainExecutable = true; + link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, ImageLoader::RPathChain(NULL, NULL)); + gLinkContext.linkingMainExecutable = false; if ( sMainExecutable->forceFlat() ) { gLinkContext.bindFlat = true; gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; } - link(sMainExecutable, bindness, ImageLoader::kDontRunInitializers); result = (uintptr_t)sMainExecutable->getMain(); + + // link any inserted libraries + // do this after linking main executable so that any dylibs pulled in by inserted + // dylibs (e.g. libSystem) will not be in front of dylibs the program uses + if ( sInsertedDylibCount > 0 ) { + for(unsigned int i=0; i < sInsertedDylibCount; ++i) { + ImageLoader* image = sAllImages[i+1]; + link(image, sEnv.DYLD_BIND_AT_LAUNCH, ImageLoader::RPathChain(NULL, NULL)); + } + } + + #if SUPPORT_OLD_CRT_INITIALIZATION + // Old way is to run initializers via a callback from crt1.o + if ( ! gRunInitializersOldWay ) + #endif + initializeMainExecutable(); // run all initializers } catch(const char* message) { halt(message); } catch(...) { - fprintf(stderr, "dyld: launch failed\n"); + dyld::log("dyld: launch failed\n"); } - - // Link in any inserted libraries. - // Do this after linking main executable so any extra libraries pulled in by inserted libraries are at end of flat namespace - if ( insertLibrariesCount > 0 ) { - for (int i=0; i < insertLibrariesCount; ++i) { - try { - if ( insertedImages[i] != NULL ) - link(insertedImages[i], bindness, ImageLoader::kDontRunInitializers); - } - catch (const char* message) { - char buf[strlen(sEnv.DYLD_INSERT_LIBRARIES[i])+50+strlen(message)]; - sprintf(buf, "could not link inserted library: %s\n%s\n", sEnv.DYLD_INSERT_LIBRARIES[i], message); - halt(buf); - } - } - } - return result; } diff --git a/src/dyld.exp b/src/dyld.exp index ee6ad95..4412747 100644 --- a/src/dyld.exp +++ b/src/dyld.exp @@ -1,5 +1,5 @@ # -# Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -28,9 +28,11 @@ # 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 diff --git a/src/dyld.h b/src/dyld.h index 72e0e2a..4ccbdd7 100644 --- a/src/dyld.h +++ b/src/dyld.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,6 +25,7 @@ #include #include "ImageLoader.h" +#include "mach-o/dyld_priv.h" @@ -38,12 +39,14 @@ namespace dyld { { bool useSearchPaths; bool useLdLibraryPath; + bool implicitRPath; bool matchByInstallName; bool dontLoad; bool mustBeBundle; bool mustBeDylib; - const char* origin; // path for expanding @loader_path - const char** rpath; // future support of -rpath + bool findDLL; + const char* origin; // path for expanding @loader_path + const ImageLoader::RPathChain* rpath; // paths for expanding @rpath }; @@ -52,36 +55,43 @@ namespace dyld { 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 const struct ThreadingHelpers* gThreadHelpers; - - + 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 link(ImageLoader* image, ImageLoader::BindingLaziness bindness, ImageLoader::InitializerRunning runInitializers); - extern void runTerminators(); + extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths); + extern void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChain& loaderRPaths); + extern void runInitializers(ImageLoader* image); + extern void runTerminators(void*); extern const char* getExecutablePath(); - extern bool validImage(ImageLoader*); + extern bool validImage(const ImageLoader*); extern ImageLoader* getIndexedImage(uint32_t index); extern uint32_t getImageCount(); extern ImageLoader* findImageByMachHeader(const struct mach_header* target); extern ImageLoader* findImageContainingAddress(const void* addr); extern ImageLoader* findImageByName(const char* path); extern ImageLoader* findLoadedImageByInstallPath(const char* path); - extern bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, ImageLoader** image); - extern bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, ImageLoader** image); + extern bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image); + extern bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image); extern ImageLoader* load(const char* path, const LoadContext& context); extern ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName); 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, int argc, const char* argv[], const char* envp[], const char* apple[]); + extern uintptr_t _main(const struct mach_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(); @@ -89,6 +99,9 @@ namespace dyld { extern bool mainExecutablePrebound(); extern ImageLoader* mainExecutable(); extern void processDyldEnvironmentVarible(const char* key, const char* value); + 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(); }; diff --git a/src/dyld64.exp b/src/dyld64.exp index 8184223..1c2a031 100644 --- a/src/dyld64.exp +++ b/src/dyld64.exp @@ -1,5 +1,5 @@ # -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -28,9 +28,11 @@ # 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 diff --git a/src/dyldAPIs.cpp b/src/dyldAPIs.cpp index 06d9def..6daf5f1 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-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,24 +27,40 @@ // // - +#define __STDC_LIMIT_MACROS #include -#include +#include #include +#include +#include + + +#include +#include +#include + #include #include #include extern "C" mach_port_name_t task_self_trap(void); // can't include because it is missing extern C -#include "mach-o/dyld_gdb.h" +#include "mach-o/dyld_images.h" #include "mach-o/dyld.h" #include "mach-o/dyld_priv.h" -#include "dlfcn.h" +#include "mach-o/dyld-update-prebinding.h" #include "ImageLoader.h" #include "dyld.h" -#include "dyldLibSystemThreadHelpers.h" +#include "dyldLibSystemInterface.h" + +#undef _POSIX_C_SOURCE +#include "dlfcn.h" + + +#ifndef RTLD_DLL + #define RTLD_DLL 0x200 /* Mac OS X 10.5 and later */ +#endif static char sLastErrorFilePath[1024]; static NSLinkEditErrors sLastErrorFileCode; @@ -54,24 +70,21 @@ static int sLastErrorNo; // In 10.3.x and earlier all the NSObjectFileImage API's were implemeneted in libSystem.dylib // Beginning in 10.4 the NSObjectFileImage API's are implemented in dyld and libSystem just forwards // This conditional keeps support for old libSystem's which needed some help implementing the API's -#define OLD_LIBSYSTEM_SUPPORT 1 +#define OLD_LIBSYSTEM_SUPPORT (__ppc__ || __i386__) // The following functions have no prototype in any header. They are special cases // where _dyld_func_lookup() is used directly. static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit); +#if OLD_LIBSYSTEM_SUPPORT static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options); +#endif static void _dyld_register_binding_handler(void * (*)(const char *, const char *, void *), ImageLoader::BindingOptions); -static void _dyld_fork_prepare(); -static void _dyld_fork_parent(); static void _dyld_fork_child(); -static void _dyld_fork_child_final(); static void _dyld_make_delayed_module_initializer_calls(); -static void _dyld_mod_term_funcs(); static bool NSMakePrivateModulePublic(NSModule module); static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header); -static void registerThreadHelpers(const dyld::ThreadingHelpers*); -static void _dyld_update_prebinding(int pathCount, const char* paths[], uint32_t flags); +static void registerThreadHelpers(const dyld::LibSystemHelpers*); static const struct dyld_all_image_infos* _dyld_get_all_image_infos(); // The following functions are dyld API's, but since dyld links with a static copy of libc.a @@ -91,38 +104,37 @@ struct dyld_func { }; static struct dyld_func dyld_funcs[] = { - {"__dyld_image_count", (void*)_dyld_image_count }, - {"__dyld_get_image_header", (void*)_dyld_get_image_header }, - {"__dyld_get_image_vmaddr_slide", (void*)_dyld_get_image_vmaddr_slide }, - {"__dyld_get_image_name", (void*)_dyld_get_image_name }, + {"__dyld_register_thread_helpers", (void*)registerThreadHelpers }, + {"__dyld_register_func_for_add_image", (void*)_dyld_register_func_for_add_image }, + {"__dyld_register_func_for_remove_image", (void*)_dyld_register_func_for_remove_image }, + {"__dyld_make_delayed_module_initializer_calls", (void*)_dyld_make_delayed_module_initializer_calls }, + {"__dyld_fork_child", (void*)_dyld_fork_child }, + {"__dyld_dyld_register_image_state_change_handler", (void*)dyld_register_image_state_change_handler }, + {"__dyld_dladdr", (void*)dladdr }, + {"__dyld_dlclose", (void*)dlclose }, + {"__dyld_dlerror", (void*)dlerror }, + {"__dyld_dlopen", (void*)dlopen }, + {"__dyld_dlsym", (void*)dlsym }, + {"__dyld_dlopen_preflight", (void*)dlopen_preflight }, + {"__dyld_get_image_header_containing_address", (void*)_dyld_get_image_header_containing_address }, + {"__dyld_image_count", (void*)_dyld_image_count }, + {"__dyld_get_image_header", (void*)_dyld_get_image_header }, + {"__dyld_get_image_vmaddr_slide", (void*)_dyld_get_image_vmaddr_slide }, + {"__dyld_get_image_name", (void*)_dyld_get_image_name }, + {"__dyld__NSGetExecutablePath", (void*)_NSGetExecutablePath }, + + // the rest of these are either deprecated or SPIs {"__dyld_lookup_and_bind", (void*)client_dyld_lookup_and_bind }, {"__dyld_lookup_and_bind_with_hint", (void*)_dyld_lookup_and_bind_with_hint }, - {"__dyld_lookup_and_bind_objc", (void*)unimplemented }, {"__dyld_lookup_and_bind_fully", (void*)_dyld_lookup_and_bind_fully }, {"__dyld_install_handlers", (void*)_dyld_install_handlers }, {"__dyld_link_edit_error", (void*)NSLinkEditError }, -#if OLD_LIBSYSTEM_SUPPORT - {"__dyld_link_module", (void*)_dyld_link_module }, -#endif {"__dyld_unlink_module", (void*)NSUnLinkModule }, - {"__dyld_register_func_for_add_image", (void*)_dyld_register_func_for_add_image }, - {"__dyld_register_func_for_remove_image", (void*)_dyld_register_func_for_remove_image }, - {"__dyld_register_func_for_link_module", (void*)unimplemented }, - {"__dyld_register_func_for_unlink_module", (void*)unimplemented }, - {"__dyld_register_func_for_replace_module", (void*)unimplemented }, - {"__dyld_get_objc_module_sect_for_module", (void*)unimplemented }, {"__dyld_bind_objc_module", (void*)_dyld_bind_objc_module }, {"__dyld_bind_fully_image_containing_address", (void*)_dyld_bind_fully_image_containing_address }, {"__dyld_image_containing_address", (void*)_dyld_image_containing_address }, - {"__dyld_get_image_header_containing_address", (void*)_dyld_get_image_header_containing_address }, {"__dyld_moninit", (void*)_dyld_moninit }, - {"__dyld_register_binding_handler", (void*)_dyld_register_binding_handler }, - {"__dyld_fork_prepare", (void*)_dyld_fork_prepare }, - {"__dyld_fork_parent", (void*)_dyld_fork_parent }, - {"__dyld_fork_child", (void*)_dyld_fork_child }, - {"__dyld_fork_child_final", (void*)_dyld_fork_child_final }, - {"__dyld_fork_mach_init", (void*)unimplemented }, - {"__dyld_make_delayed_module_initializer_calls",(void*)_dyld_make_delayed_module_initializer_calls }, + {"__dyld_register_binding_handler", (void*)_dyld_register_binding_handler }, {"__dyld_NSNameOfSymbol", (void*)NSNameOfSymbol }, {"__dyld_NSAddressOfSymbol", (void*)NSAddressOfSymbol }, {"__dyld_NSModuleForSymbol", (void*)NSModuleForSymbol }, @@ -139,15 +151,12 @@ static struct dyld_func dyld_funcs[] = { {"__dyld_NSAddLibrary", (void*)NSAddLibrary }, {"__dyld_NSAddLibraryWithSearching", (void*)NSAddLibraryWithSearching }, {"__dyld_NSAddImage", (void*)NSAddImage }, - {"__dyld__NSGetExecutablePath", (void*)_NSGetExecutablePath }, {"__dyld_launched_prebound", (void*)_dyld_launched_prebound }, {"__dyld_all_twolevel_modules_prebound", (void*)_dyld_all_twolevel_modules_prebound }, {"__dyld_call_module_initializers_for_dylib", (void*)_dyld_call_module_initializers_for_dylib }, - {"__dyld_mod_term_funcs", (void*)_dyld_mod_term_funcs }, {"__dyld_install_link_edit_symbol_handlers", (void*)dyld::registerZeroLinkHandlers }, {"__dyld_NSCreateObjectFileImageFromFile", (void*)NSCreateObjectFileImageFromFile }, {"__dyld_NSCreateObjectFileImageFromMemory", (void*)NSCreateObjectFileImageFromMemory }, - {"__dyld_NSCreateCoreFileImageFromFile", (void*)unimplemented }, {"__dyld_NSDestroyObjectFileImage", (void*)NSDestroyObjectFileImage }, {"__dyld_NSLinkModule", (void*)NSLinkModule }, {"__dyld_NSHasModInitObjectFileImage", (void*)NSHasModInitObjectFileImage }, @@ -158,13 +167,10 @@ static struct dyld_func dyld_funcs[] = { {"__dyld_NSSymbolReferenceCountInObjectFileImage", (void*)NSSymbolReferenceCountInObjectFileImage }, {"__dyld_NSGetSectionDataInObjectFileImage", (void*)NSGetSectionDataInObjectFileImage }, {"__dyld_NSFindSectionAndOffsetInObjectFileImage", (void*)NSFindSectionAndOffsetInObjectFileImage }, - {"__dyld_register_thread_helpers", (void*)registerThreadHelpers }, - {"__dyld_dladdr", (void*)dladdr }, - {"__dyld_dlclose", (void*)dlclose }, - {"__dyld_dlerror", (void*)dlerror }, - {"__dyld_dlopen", (void*)dlopen }, - {"__dyld_dlsym", (void*)dlsym }, - {"__dyld_update_prebinding", (void*)_dyld_update_prebinding }, +#if OLD_LIBSYSTEM_SUPPORT + {"__dyld_link_module", (void*)_dyld_link_module }, +#endif + {"__dyld_dlord", (void*)dlord }, {"__dyld_get_all_image_infos", (void*)_dyld_get_all_image_infos }, {NULL, 0} }; @@ -182,7 +188,7 @@ inline const ImageLoader::Symbol* NSSymbolToSymbol(NSSymbol sym) } // dyld's abstract type NSModule is implemented as ImageLoader* -inline NSModule ImageLoaderToNSModule(ImageLoader* image) +inline NSModule ImageLoaderToNSModule(const ImageLoader* image) { return (NSModule)image; } @@ -215,7 +221,7 @@ static std::vector sObjectFileImages; static void dyldAPIhalt(const char* apiName, const char* errorMsg) { - fprintf(stderr, "dyld: %s() error\n", apiName); + dyld::log("dyld: %s() error\n", apiName); dyld::halt(errorMsg); } @@ -244,7 +250,7 @@ static void setLastError(NSLinkEditErrors code, int errnum, const char* file, co int _NSGetExecutablePath(char* buf, uint32_t *bufsize) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(...)\n", __func__); + dyld::log("%s(...)\n", __func__); const char* exePath = dyld::getExecutablePath(); if(*bufsize < strlen(exePath) + 1){ *bufsize = strlen(exePath) + 1; @@ -267,7 +273,7 @@ int _NSGetExecutablePath(char* buf, uint32_t *bufsize) static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header) { if ( dyld::gLogAPIs ) - fprintf(stderr, "__initialize_Cplusplus()\n"); + dyld::log("__initialize_Cplusplus()\n"); // for now, do nothing... } @@ -276,15 +282,15 @@ static void _dyld_call_module_initializers_for_dylib(const struct mach_header* m void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", %p, %p)\n", __func__, symbolName, address, module); + dyld::log("%s(\"%s\", %p, %p)\n", __func__, symbolName, address, module); ImageLoader* image; const ImageLoader::Symbol* sym; dyld::clearErrorMessage(); - if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { + if ( dyld::flatFindExportedSymbol(symbolName, &sym, (const ImageLoader**)&image) ) { try { - dyld::link(image, ImageLoader::kLazyOnly, ImageLoader::kDontRunInitializers); + image->bindAllLazyPointers(dyld::gLinkContext, true); if ( address != NULL) - *address = (void*)image->getExportedSymbolAddress(sym); + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); if ( module != NULL) *module = ImageLoaderToNSModule(image); } @@ -306,12 +312,12 @@ void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModul static void client_dyld_lookup_and_bind(const char* symbolName, void** address, NSModule* module) { if ( dyld::gLogAPIs ) - fprintf(stderr, "_dyld_lookup_and_bind(\"%s\", %p, %p)\n", symbolName, address, module); - ImageLoader* image; + dyld::log("_dyld_lookup_and_bind(\"%s\", %p, %p)\n", symbolName, address, module); + const ImageLoader* image; const ImageLoader::Symbol* sym; if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { if ( address != NULL) - *address = (void*)image->getExportedSymbolAddress(sym); + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); if ( module != NULL) *module = ImageLoaderToNSModule(image); } @@ -327,14 +333,14 @@ static void client_dyld_lookup_and_bind(const char* symbolName, void** address, void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* library_name_hint, void** address, NSModule* module) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", \"%s\", %p, %p)\n", __func__, symbolName, library_name_hint, address, module); - ImageLoader* image; + dyld::log("%s(\"%s\", \"%s\", %p, %p)\n", __func__, symbolName, library_name_hint, address, module); + const ImageLoader* image; const ImageLoader::Symbol* sym; // Look for library whose path contains the hint. If that fails search everywhere if ( dyld::flatFindExportedSymbolWithHint(symbolName, library_name_hint, &sym, &image) || dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { if ( address != NULL) - *address = (void*)image->getExportedSymbolAddress(sym); + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); if ( module != NULL) *module = ImageLoaderToNSModule(image); } @@ -351,8 +357,8 @@ void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* library NSSymbol NSLookupAndBindSymbol(const char *symbolName) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\")\n", __func__, symbolName); - ImageLoader* image; + dyld::log("%s(\"%s\")\n", __func__, symbolName); + const ImageLoader* image; const ImageLoader::Symbol* sym; if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { return SymbolToNSSymbol(sym); @@ -364,8 +370,8 @@ NSSymbol NSLookupAndBindSymbol(const char *symbolName) NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); - ImageLoader* image; + dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); + const ImageLoader* image; const ImageLoader::Symbol* sym; bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); if ( ! found ) { @@ -377,21 +383,21 @@ NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libra // return NULL on failure and log if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", \"%s\") => NULL \n", __func__, symbolName, libraryNameHint); + dyld::log("%s(\"%s\", \"%s\") => NULL \n", __func__, symbolName, libraryNameHint); return NULL; } uint32_t _dyld_image_count(void) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); + dyld::log("%s()\n", __func__); return dyld::getImageCount(); } const struct mach_header* _dyld_get_image_header(uint32_t image_index) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%u)\n", __func__, image_index); + dyld::log("%s(%u)\n", __func__, image_index); ImageLoader* image = dyld::getIndexedImage(image_index); if ( image != NULL ) return (struct mach_header*)image->machHeader(); @@ -404,28 +410,41 @@ static __attribute__((noinline)) const struct mach_header* addImage(void* callerAddress, const char* path, bool search, bool dontLoad, bool matchInstallName, bool abortOnError) { ImageLoader* image = NULL; + std::vector rpathsFromCallerImage; try { dyld::clearErrorMessage(); ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + // like dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) { + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } dyld::LoadContext context; context.useSearchPaths = search; context.useLdLibraryPath = false; + context.implicitRPath = false; context.matchByInstallName = matchInstallName; context.dontLoad = dontLoad; context.mustBeBundle = false; context.mustBeDylib = true; + context.findDLL = false; context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path - context.rpath = NULL; // support not yet implemented + context.rpath = &callersRPaths; // rpaths from caller and main executable image = load(path, context); if ( image != NULL ) { if ( context.matchByInstallName ) image->setMatchInstallPath(true); - dyld::link(image, ImageLoader::kNonLazyOnly, ImageLoader::kRunInitializers); - return image->machHeader(); + dyld::link(image, false, callersRPaths); + dyld::runInitializers(image); + // images added with NSAddImage() can never be unloaded + image->setNeverUnload(); } } catch (const char* msg) { + dyld::garbageCollectImages(); if ( abortOnError) { char pathMsg[strlen(msg)+strlen(path)+4]; strcpy(pathMsg, msg); @@ -435,18 +454,29 @@ const struct mach_header* addImage(void* callerAddress, const char* path, bool s } // not halting, so set error state for NSLinkEditError to find setLastError(NSLinkEditOtherError, 0, path, msg); + free((void*)msg); + image = NULL; } - return NULL; + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + if ( image == NULL ) + return NULL; + else + return image->machHeader(); } + const struct mach_header* NSAddImage(const char* path, uint32_t options) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", 0x%08X)\n", __func__, path, options); + dyld::log("%s(\"%s\", 0x%08X)\n", __func__, path, options); const bool dontLoad = ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 ); const bool search = ( (options & NSADDIMAGE_OPTION_WITH_SEARCHING) != 0 ); const bool matchInstallName = ( (options & NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME) != 0 ); - const bool abortOnError = ( (options & NSADDIMAGE_OPTION_RETURN_ON_ERROR) == 0 ); + const bool abortOnError = ( (options & NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) == 0 ); void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue return addImage(callerAddress, path, search, dontLoad, matchInstallName, abortOnError); } @@ -454,7 +484,7 @@ const struct mach_header* NSAddImage(const char* path, uint32_t options) bool NSAddLibrary(const char* path) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\")\n", __func__, path); + dyld::log("%s(\"%s\")\n", __func__, path); void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue return (addImage(callerAddress, path, false, false, false, false) != NULL); } @@ -462,7 +492,7 @@ bool NSAddLibrary(const char* path) bool NSAddLibraryWithSearching(const char* path) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\")\n", __func__, path); + dyld::log("%s(\"%s\")\n", __func__, path); void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue return (addImage(callerAddress, path, true, false, false, false) != NULL); } @@ -476,7 +506,7 @@ bool NSAddLibraryWithSearching(const char* path) bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, \"%s\")\n", __func__, (void *)mh, symbolName); + 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) @@ -488,17 +518,17 @@ bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symb NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, \"%s\", 0x%08X)\n", __func__, mh, symbolName, options); + dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, mh, symbolName, options); const ImageLoader::Symbol* symbol = NULL; dyld::clearErrorMessage(); ImageLoader* image = dyld::findImageByMachHeader(mh); if ( image != NULL ) { try { if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY ) { - dyld::link(image, ImageLoader::kLazyOnly, ImageLoader::kDontRunInitializers); + image->bindAllLazyPointers(dyld::gLinkContext, true); } else if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW ) { - dyld::link(image, ImageLoader::kLazyOnlyNoDependents, ImageLoader::kDontRunInitializers); + image->bindAllLazyPointers(dyld::gLinkContext, false); } } catch (const char* msg) { @@ -509,7 +539,7 @@ NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolN symbol = image->findExportedSymbol(symbolName, NULL, true, NULL); } if ( dyld::gLogAPIs && (symbol == NULL) ) - fprintf(stderr, "%s(%p, \"%s\", 0x%08X) ==> NULL\n", __func__, mh, symbolName, options); + dyld::log("%s(%p, \"%s\", 0x%08X) ==> NULL\n", __func__, mh, symbolName, options); return SymbolToNSSymbol(symbol); } @@ -519,8 +549,8 @@ NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolN static bool client_NSIsSymbolNameDefined(const char* symbolName) { if ( dyld::gLogAPIs ) - fprintf(stderr, "NSIsSymbolNameDefined(\"%s\")\n", symbolName); - ImageLoader* image; + dyld::log("NSIsSymbolNameDefined(\"%s\")\n", symbolName); + const ImageLoader* image; const ImageLoader::Symbol* sym; return dyld::flatFindExportedSymbol(symbolName, &sym, &image); } @@ -528,8 +558,8 @@ static bool client_NSIsSymbolNameDefined(const char* symbolName) bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); - ImageLoader* image; + dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); + const ImageLoader* image; const ImageLoader::Symbol* sym; bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); if ( ! found ) { @@ -537,14 +567,14 @@ bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNa found = dyld::flatFindExportedSymbol(symbolName, &sym, &image); } if ( !found && dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", \"%s\") => false \n", __func__, symbolName, libraryNameHint); + dyld::log("%s(\"%s\", \"%s\") => false \n", __func__, symbolName, libraryNameHint); return found; } const char* NSNameOfSymbol(NSSymbol symbol) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, (void *)symbol); + dyld::log("%s(%p)\n", __func__, (void *)symbol); const char* result = NULL; ImageLoader* image = dyld::findImageContainingAddress(symbol); if ( image != NULL ) @@ -555,18 +585,18 @@ const char* NSNameOfSymbol(NSSymbol symbol) void* NSAddressOfSymbol(NSSymbol symbol) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, (void *)symbol); + dyld::log("%s(%p)\n", __func__, (void *)symbol); void* result = NULL; ImageLoader* image = dyld::findImageContainingAddress(symbol); if ( image != NULL ) - result = (void*)image->getExportedSymbolAddress(NSSymbolToSymbol(symbol)); + result = (void*)image->getExportedSymbolAddress(NSSymbolToSymbol(symbol), dyld::gLinkContext); return result; } NSModule NSModuleForSymbol(NSSymbol symbol) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, (void *)symbol); + dyld::log("%s(%p)\n", __func__, (void *)symbol); NSModule result = NULL; ImageLoader* image = dyld::findImageContainingAddress(symbol); if ( image != NULL ) @@ -578,7 +608,7 @@ NSModule NSModuleForSymbol(NSSymbol symbol) intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%u)\n", __func__, image_index); + dyld::log("%s(%u)\n", __func__, image_index); ImageLoader* image = dyld::getIndexedImage(image_index); if ( image != NULL ) return image->getSlide(); @@ -589,7 +619,7 @@ intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) const char* _dyld_get_image_name(uint32_t image_index) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%u)\n", __func__, image_index); + dyld::log("%s(%u)\n", __func__, image_index); ImageLoader* image = dyld::getIndexedImage(image_index); if ( image != NULL ) return image->getLogicalPath(); @@ -602,14 +632,14 @@ const char* _dyld_get_image_name(uint32_t image_index) bool _dyld_all_twolevel_modules_prebound(void) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); + dyld::log("%s()\n", __func__); return FALSE; // fixme } void _dyld_bind_objc_module(const void *objc_module) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, objc_module); + dyld::log("%s(%p)\n", __func__, objc_module); // do nothing, with new dyld everything already bound } @@ -617,12 +647,12 @@ void _dyld_bind_objc_module(const void *objc_module) bool _dyld_bind_fully_image_containing_address(const void* address) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, address); + dyld::log("%s(%p)\n", __func__, address); dyld::clearErrorMessage(); ImageLoader* image = dyld::findImageContainingAddress(address); if ( image != NULL ) { try { - dyld::link(image, ImageLoader::kLazyAndNonLazy, ImageLoader::kDontRunInitializers); + image->bindAllLazyPointers(dyld::gLinkContext, true); return true; } catch (const char* msg) { @@ -635,7 +665,7 @@ bool _dyld_bind_fully_image_containing_address(const void* address) bool _dyld_image_containing_address(const void* address) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, address); + dyld::log("%s(%p)\n", __func__, address); ImageLoader *imageLoader = dyld::findImageContainingAddress(address); return (NULL != imageLoader); } @@ -653,7 +683,7 @@ static NSObjectFileImage createObjectImageFile(ImageLoader* image, const void* a NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(\"%s\", ...)\n", __func__, pathName); + dyld::log("%s(\"%s\", ...)\n", __func__, pathName); try { void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); @@ -661,10 +691,12 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName dyld::LoadContext context; context.useSearchPaths = false; context.useLdLibraryPath = false; + context.implicitRPath = false; context.matchByInstallName = false; context.dontLoad = false; context.mustBeBundle = true; context.mustBeDylib = false; + context.findDLL = false; context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path context.rpath = NULL; // support not yet implemented @@ -680,7 +712,9 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName } } catch (const char* msg) { - //fprintf(stderr, "dyld: NSCreateObjectFileImageFromFile() error: %s\n", msg); + //dyld::log("dyld: NSCreateObjectFileImageFromFile() error: %s\n", msg); + dyld::garbageCollectImages(); + free((void*)msg); return NSObjectFileImageInappropriateFile; } return NSObjectFileImageFailure; @@ -690,13 +724,13 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* address, size_t size, NSObjectFileImage *objectFileImage) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, %lu, %p)\n", __func__, address, size, objectFileImage); + dyld::log("%s(%p, %lu, %p)\n", __func__, address, size, objectFileImage); try { ImageLoader* image = dyld::loadFromMemory((const uint8_t*)address, size, NULL); if ( ! image->isBundle() ) { // this API can only be used with bundles... - delete image; + dyld::garbageCollectImages(); return NSObjectFileImageInappropriateFile; } // Note: We DO NOT link the image! NSLinkModule will do that @@ -706,7 +740,9 @@ NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* addres } } catch (const char* msg) { - fprintf(stderr, "dyld: NSCreateObjectFileImageFromMemory() error: %s\n", msg); + free((void*)msg); + dyld::garbageCollectImages(); + //dyld::log("dyld: NSCreateObjectFileImageFromMemory() error: %s\n", msg); } return NSObjectFileImageFailure; } @@ -724,14 +760,19 @@ static bool validOFI(NSObjectFileImage objectFileImage) bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, objectFileImage); + dyld::log("%s(%p)\n", __func__, objectFileImage); if ( validOFI(objectFileImage) ) { - // if the image has never been linked or has been unlinked, the image is not in the list of valid images - // and we should delete it - bool linkedImage = dyld::validImage(objectFileImage->image); - if ( ! linkedImage ) - delete objectFileImage->image; + // a failure during NSLinkModule will delete the image + if ( objectFileImage->image != NULL ) { + // if the image has never been linked or has been unlinked, the image is not in the list of valid images + // and we should delete it + bool linkedImage = dyld::validImage(objectFileImage->image); + if ( ! linkedImage ) { + delete objectFileImage->image; + objectFileImage->image = NULL; + } + } // remove from list of ofi's for (std::vector::iterator it=sObjectFileImages.begin(); it != sObjectFileImages.end(); it++) { @@ -758,21 +799,21 @@ bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) bool NSHasModInitObjectFileImage(NSObjectFileImage objectFileImage) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, objectFileImage); + dyld::log("%s(%p)\n", __func__, objectFileImage); return objectFileImage->image->needsInitialization(); } uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, objectFileImage); + dyld::log("%s(%p)\n", __func__, objectFileImage); return objectFileImage->image->getExportedSymbolCount(); } const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p,%d)\n", __func__, objectFileImage, ordinal); + dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedExportedSymbol(ordinal); return objectFileImage->image->getExportedSymbolName(sym); } @@ -780,7 +821,7 @@ const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFile uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, objectFileImage); + dyld::log("%s(%p)\n", __func__, objectFileImage); return objectFileImage->image->getImportedSymbolCount(); } @@ -788,7 +829,7 @@ const char * NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFile bool* tentative_definition) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p,%d)\n", __func__, objectFileImage, ordinal); + 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); @@ -804,7 +845,7 @@ void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, unsigned long* size) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p,%s, %s)\n", __func__, objectFileImage, segmentName, sectionName); + dyld::log("%s(%p,%s, %s)\n", __func__, objectFileImage, segmentName, sectionName); void* start; size_t length; @@ -821,7 +862,7 @@ void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p,%s)\n", __func__, objectFileImage, symbolName); + dyld::log("%s(%p,%s)\n", __func__, objectFileImage, symbolName); const ImageLoader::Symbol* sym = objectFileImage->image->findExportedSymbol(symbolName, NULL, true, NULL); return ( sym != NULL ); } @@ -845,9 +886,9 @@ NSFindSectionAndOffsetInObjectFileImage(NSObjectFileImage objectFileImage, unsigned long* sectionOffset) /* can be NULL */ { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, objectFileImage); + dyld::log("%s(%p)\n", __func__, objectFileImage); - return objectFileImage->image->findSection((char*)(objectFileImage->image->getBaseAddress())+imageOffset, segmentName, sectionName, sectionOffset); + return objectFileImage->image->findSection(((char*)(objectFileImage->image->machHeader()))+imageOffset, segmentName, sectionName, sectionOffset); } @@ -855,7 +896,7 @@ NSFindSectionAndOffsetInObjectFileImage(NSObjectFileImage objectFileImage, NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, \"%s\", 0x%08X)\n", __func__, objectFileImage, moduleName, options); + dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, objectFileImage, moduleName, options); dyld::clearErrorMessage(); try { @@ -864,7 +905,7 @@ NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, if ( objectFileImage->image->isLinked() ) { // already linked, so clone a new one and link it #if 0 - fprintf(stderr, "dyld: warning: %s(0x%08X, \"%s\", 0x%08X) called more than once for 0x%08X\n", + 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); @@ -887,34 +928,40 @@ NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, objectFileImage->image->setHideExports(); // set up linking options - ImageLoader::BindingLaziness bindness = ImageLoader::kNonLazyOnly; - if ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ) - bindness = ImageLoader::kLazyAndNonLazy; - ImageLoader::InitializerRunning runInitializers = ImageLoader::kRunInitializers; - if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) != 0 ) - runInitializers = ImageLoader::kDontRunInitializersButTellObjc; + bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); // load libraries, rebase, bind, to make this image usable - dyld::link(objectFileImage->image, bindness, runInitializers); + dyld::link(objectFileImage->image, forceLazysBound, ImageLoader::RPathChain(NULL,NULL)); + // 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) { + dyld::garbageCollectImages(); if ( (options & NSLINKMODULE_OPTION_RETURN_ON_ERROR) == 0 ) dyldAPIhalt(__func__, msg); // not halting, so set error state for NSLinkEditError to find - dyld::removeImage(objectFileImage->image); setLastError(NSLinkEditOtherError, 0, moduleName, msg); + // dyld::link() deleted the image so lose our reference + objectFileImage->image = NULL; + free((void*)msg); return NULL; } } + #if OLD_LIBSYSTEM_SUPPORT // This is for compatibility with old libSystems (libdyld.a) which process ObjectFileImages outside dyld static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, \"%s\", 0x%08X)\n", "NSLinkModule", object_addr, moduleName, options); // note name/args translation + dyld::log("%s(%p, \"%s\", 0x%08X)\n", "NSLinkModule", object_addr, moduleName, options); // note name/args translation ImageLoader* image = NULL; dyld::clearErrorMessage(); try { @@ -933,15 +980,14 @@ static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_s image->setHideExports(); // set up linking options - ImageLoader::BindingLaziness bindness = ImageLoader::kNonLazyOnly; - if ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ) - bindness = ImageLoader::kLazyAndNonLazy; - ImageLoader::InitializerRunning runInitializers = ImageLoader::kRunInitializers; - if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) != 0 ) - runInitializers = ImageLoader::kDontRunInitializersButTellObjc; + bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); // load libraries, rebase, bind, to make this image usable - dyld::link(image, bindness, runInitializers); + dyld::link(image, forceLazysBound, ImageLoader::RPathChain(NULL,NULL)); + + // run initializers unless magic flag says not to + if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) + dyld::runInitializers(image); } } catch (const char* msg) { @@ -955,6 +1001,7 @@ static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_s delete image; } image = NULL; + free((void*)msg); } return ImageLoaderToNSModule(image); } @@ -963,7 +1010,7 @@ static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_s NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, \"%s\")\n", __func__, (void *)module, symbolName); + dyld::log("%s(%p, \"%s\")\n", __func__, (void *)module, symbolName); ImageLoader* image = NSModuleToImageLoader(module); if ( image == NULL ) return NULL; @@ -973,7 +1020,7 @@ NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) const char* NSNameOfModule(NSModule module) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, module); + dyld::log("%s(%p)\n", __func__, module); ImageLoader* image = NSModuleToImageLoader(module); if ( image == NULL ) return NULL; @@ -983,7 +1030,7 @@ const char* NSNameOfModule(NSModule module) const char* NSLibraryNameForModule(NSModule module) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, module); + dyld::log("%s(%p)\n", __func__, module); ImageLoader* image = NSModuleToImageLoader(module); if ( image == NULL ) return NULL; @@ -993,7 +1040,7 @@ const char* NSLibraryNameForModule(NSModule module) bool NSUnLinkModule(NSModule module, uint32_t options) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, 0x%08X)\n", __func__, module, options); + dyld::log("%s(%p, 0x%08X)\n", __func__, module, options); if ( module == NULL ) return false; ImageLoader* image = NSModuleToImageLoader(module); @@ -1025,7 +1072,7 @@ bool NSUnLinkModule(NSModule module, uint32_t options) static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit) { if ( dyld::gLogAPIs ) - fprintf(stderr, "NSLinkEditErrorHandlers()\n"); + dyld::log("NSLinkEditErrorHandlers()\n"); dyld::registerUndefinedHandler((dyld::UndefinedHandler)undefined); // no support for multiple or linkedit handlers @@ -1034,7 +1081,7 @@ static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEd const struct mach_header * _dyld_get_image_header_containing_address(const void* address) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, address); + dyld::log("%s(%p)\n", __func__, address); ImageLoader* image = dyld::findImageContainingAddress(address); if ( image != NULL ) return image->machHeader(); @@ -1045,31 +1092,28 @@ const struct mach_header * _dyld_get_image_header_containing_address(const void* void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, (void *)func); + 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 ) - fprintf(stderr, "%s(%p)\n", __func__, (void *)func); + dyld::log("%s(%p)\n", __func__, (void *)func); dyld::registerRemoveCallback(func); } -// called by atexit() function installed by crt -static void _dyld_mod_term_funcs() -{ - if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); - dyld::runTerminators(); -} // called by crt before main static void _dyld_make_delayed_module_initializer_calls() { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); - dyld::initializeMainExecutable(); + dyld::log("%s()\n", __func__); + +#if SUPPORT_OLD_CRT_INITIALIZATION + if ( dyld::gRunInitializersOldWay ) + dyld::initializeMainExecutable(); +#endif } @@ -1085,30 +1129,17 @@ void NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileNam static void _dyld_register_binding_handler(void * (*bindingHandler)(const char *, const char *, void *), ImageLoader::BindingOptions bindingOptions) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); + dyld::log("%s()\n", __func__); dyld::gLinkContext.bindingHandler = bindingHandler; dyld::gLinkContext.bindingOptions = bindingOptions; } -// Call by fork() in libSystem before the kernel trap is done -static void _dyld_fork_prepare() -{ - if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); -} - -// Call by fork() in libSystem after the kernel trap is done on the parent side -static void _dyld_fork_parent() -{ - if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); -} // Call by fork() in libSystem after the kernel trap is done on the child side static void _dyld_fork_child() { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); + dyld::log("%s()\n", __func__); // The implementation of fork() in libSystem knows to reset the variable mach_task_self_ // in libSystem for the child of a fork. But dyld is built with a static copy // of libc.a and has its own copy of mach_task_self_ which we reset here. @@ -1120,13 +1151,6 @@ static void _dyld_fork_child() mach_task_self_ = task_self_trap(); } -// Call by fork() in libSystem after the kernel trap is done on the child side after -// other libSystem child side fixups are done -static void _dyld_fork_child_final() -{ - if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); -} typedef void (*MonitorProc)(char *lowpc, char *highpc); @@ -1155,7 +1179,7 @@ void _dyld_moninit(MonitorProc proc) bool _dyld_launched_prebound() { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); + dyld::log("%s()\n", __func__); // ¥¥¥Êif we deprecate prebinding, we may want to consider always returning true or false here return dyld::mainExecutablePrebound(); @@ -1188,7 +1212,7 @@ bool lookupDyldFunction(const char* name, uintptr_t* address) for (const dyld_func* p = dyld_funcs; p->name != NULL; ++p) { if ( strcmp(p->name, name) == 0 ) { if( p->implementation == unimplemented ) - fprintf(stderr, "unimplemented dyld function: %s\n", p->name); + dyld::log("unimplemented dyld function: %s\n", p->name); *address = (uintptr_t)p->implementation; return true; } @@ -1197,80 +1221,179 @@ bool lookupDyldFunction(const char* name, uintptr_t* address) return false; } - -static void registerThreadHelpers(const dyld::ThreadingHelpers* helpers) +static bool readOnlyBootVolume() { - // We need to make sure libSystem's lazy pointer's are bound - // before installing thred helpers. - // The reason for this is that if accessing the lock requires - // a lazy pointer to be bound (and it does when multi-module - // libSystem) is not prebound, the lazy handler will be - // invoked which tries to acquire the lock again...an infinite - // loop. - ImageLoader* image = dyld::findImageContainingAddress(helpers); - dyld::link(image, ImageLoader::kLazyOnly, ImageLoader::kDontRunInitializers); + 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; +} - dyld::gThreadHelpers = helpers; + +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::gSharedCacheNeedsUpdating ) + (*helpers->dyld_shared_cache_out_of_date)(); + } + } + } +#endif } static void dlerrorClear() { - if ( dyld::gThreadHelpers != NULL ) { - char* buffer = (*dyld::gThreadHelpers->getThreadBufferFor_dlerror)(1); + if ( dyld::gLibSystemHelpers != NULL ) { + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); buffer[0] = '\0'; + buffer[1] = '\0'; } } static void dlerrorSet(const char* msg) { - if ( dyld::gThreadHelpers != NULL ) { - char* buffer = (*dyld::gThreadHelpers->getThreadBufferFor_dlerror)(strlen(msg)+1); - strcpy(buffer, msg); + if ( dyld::gLibSystemHelpers != NULL ) { + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(strlen(msg)+2); + buffer[0] = '\1'; + strcpy(&buffer[1], msg); } } +bool dlopen_preflight(const char* path) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%s)\n", __func__, path); + + dlerrorClear(); + + bool result = false; + std::vector rpathsFromCallerImage; + try { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + // for dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) { + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + + ImageLoader* image = NULL; + dyld::LoadContext context; + context.useSearchPaths = true; + context.useLdLibraryPath= (strchr(path, '/') == NULL); // a leafname implies should search + context.implicitRPath = (path[0] != '/'); // a non-absolute path implies try rpath searching + context.matchByInstallName = true; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = false; + context.findDLL = false; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = &callersRPaths; // rpaths from caller and main executable + + image = load(path, context); + if ( image != NULL ) { + dyld::preflight(image, callersRPaths); // image object deleted by dyld::preflight() + result = true; + } + } + catch (const char* msg) { + const char* str = dyld::mkstringf("dlopen_preflight(%s): %s", path, msg); + dlerrorSet(str); + free((void*)str); + } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + return result; +} + void* dlopen(const char* path, int mode) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%s, 0x%08X)\n", __func__, path, mode); + dyld::log("%s(%s, 0x%08X)\n", __func__, ((path==NULL) ? "NULL" : path), mode); dlerrorClear(); // passing NULL for path means return magic object if ( path == NULL ) { - return RTLD_DEFAULT; + // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images + if ( (mode & RTLD_FIRST) != 0 ) + return RTLD_MAIN_ONLY; + else + return RTLD_DEFAULT; } + // acquire global dyld lock (dlopen is special - libSystem glue does not do locking) + bool lockHeld = false; + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 4) ) { + dyld::gLibSystemHelpers->acquireGlobalDyldLock(); + lockHeld = true; + } + + void* result = NULL; + ImageLoader* image = NULL; + std::vector rpathsFromCallerImage; try { void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - - ImageLoader* image = NULL; + // for dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) { + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + 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.matchByInstallName = true; - context.dontLoad = ( (mode & RTLD_NOLOAD) != 0 ); + context.dontLoad = ( (mode & RTLD_NOLOAD) != 0 ); context.mustBeBundle = false; context.mustBeDylib = false; + context.findDLL = ( (mode & RTLD_DLL) != 0 ); context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path - context.rpath = NULL; // support not yet implemented + context.rpath = &callersRPaths; // rpaths from caller and main executable image = load(path, context); if ( image != NULL ) { - image->incrementReferenceCount(); - if ( ! image->isLinked() ) { - ImageLoader::BindingLaziness bindiness = ImageLoader::kNonLazyOnly; - if ( (mode & RTLD_NOW) != 0 ) - bindiness = ImageLoader::kLazyAndNonLazy; - dyld::link(image, bindiness, ImageLoader::kRunInitializers); - // only hide exports if image is not already in use - if ( (mode & RTLD_LOCAL) != 0 ) - image->setHideExports(true); + // bump reference count. Do this before link() so that if an initializer calls dlopen and fails + // this image is not garbage collected + image->incrementDlopenReferenceCount(); + // link in all dependents + if ( (mode & RTLD_NOLOAD) == 0 ) { + bool alreadyLinked = image->isLinked(); + bool forceLazysBound = ( (mode & RTLD_NOW) != 0 ); + dyld::link(image, forceLazysBound, callersRPaths); + if ( ! alreadyLinked ) { + // only hide exports if image is not already in use + if ( (mode & RTLD_LOCAL) != 0 ) + image->setHideExports(true); + } } + // RTLD_NODELETE means don't unmap image even after dlclosed. This is what dlcompat did on Mac OS X 10.3 // On other *nix OS's, it means dlclose() should do nothing, but the handle should be invalidated. // The subtle differences are: @@ -1278,34 +1401,77 @@ void* dlopen(const char* path, int mode) // 2) If someone does a supsequent dlopen() on the same image, whether the same address should be used. if ( (mode & RTLD_NODELETE) != 0 ) image->setLeaveMapped(); - return image; + + // release global dyld lock early, this enables initializers to do threaded operations + if ( lockHeld ) { + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + lockHeld = false; + } + + // RTLD_NOLOAD means dlopen should fail unless path is already loaded. + // don't run initializers when RTLD_NOLOAD is set. This only matters if dlopen() is + // called from within an initializer because it can cause initializers to run + // out of order. Most uses of RTLD_NOLOAD are "probes". If they want initialzers + // to run, then don't use RTLD_NOLOAD. + if ( (mode & RTLD_NOLOAD) == 0 ) { + // run initializers + dyld::runInitializers(image); + } + + // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images + // this is tracked by setting the low bit of the handle, which is usually zero by malloc alignment + if ( (mode & RTLD_FIRST) != 0 ) + result = (void*)(((uintptr_t)image)|1); + else + result = image; } } catch (const char* msg) { - const char* format = "dlopen(%s, %d): %s"; - char temp[strlen(format)+strlen(path)+strlen(msg)+10]; - sprintf(temp, format, path, mode, msg); - dlerrorSet(temp); + if ( image != NULL ) { + // load() succeeded but, link() failed + // back down reference count and do GC + image->decrementDlopenReferenceCount(); + dyld::garbageCollectImages(); + } + const char* str = dyld::mkstringf("dlopen(%s, %d): %s", path, mode, msg); + dlerrorSet(str); + free((void*)str); + result = NULL; } - return NULL; + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + + if ( lockHeld ) + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + return result; } + + int dlclose(void* handle) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p)\n", __func__, handle); + dyld::log("%s(%p)\n", __func__, handle); - ImageLoader* image = (ImageLoader*)handle; + // silently accept magic handles for main executable + if ( handle == RTLD_MAIN_ONLY ) + return 0; + if ( handle == RTLD_DEFAULT ) + return 0; + + ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits if ( dyld::validImage(image) ) { - if ( image->decrementReferenceCount() ) { - // for now, only bundles can be unloaded - // to unload dylibs we would need to track all direct and indirect uses - if ( image->isBundle() ) { - dyld::removeImage(image); - delete image; - } - } dlerrorClear(); + // decrement use count + if ( image->decrementDlopenReferenceCount() ) { + dlerrorSet("dlclose() called too many times"); + return -1; + } + // remove image if reference count went to zero + dyld::garbageCollectImages(); return 0; } else { @@ -1319,7 +1485,7 @@ int dlclose(void* handle) int dladdr(const void* address, Dl_info* info) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, %p)\n", __func__, address, info); + dyld::log("%s(%p, %p)\n", __func__, address, info); ImageLoader* image = dyld::findImageContainingAddress(address); if ( image != NULL ) { @@ -1331,14 +1497,16 @@ int dladdr(const void* address, Dl_info* info) 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); + const void* symAddr = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); if ( (symAddr <= address) && (bestAddr < symAddr) ) { bestSym = sym; bestAddr = symAddr; } } if ( bestSym != NULL ) { - info->dli_sname = image->getExportedSymbolName(bestSym) + 1; // strip off leading underscore + info->dli_sname = image->getExportedSymbolName(bestSym); + if ( info->dli_sname[0] == '_' ) + info->dli_sname = info->dli_sname +1; // strip off leading underscore info->dli_saddr = (void*)bestAddr; } else { @@ -1354,13 +1522,15 @@ int dladdr(const void* address, Dl_info* info) char* dlerror() { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); - - if ( dyld::gThreadHelpers != NULL ) { - char* buffer = (*dyld::gThreadHelpers->getThreadBufferFor_dlerror)(1); - // if no error set, return NULL - if ( buffer[0] != '\0' ) - return buffer; + dyld::log("%s()\n", __func__); + + if ( dyld::gLibSystemHelpers != NULL ) { + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); + if ( buffer[0] != '\0' ) { // if valid buffer + buffer[0] = '\0'; // mark invalid, so next call to dlerror returns NULL + return &buffer[1]; // return message + } } return NULL; } @@ -1368,11 +1538,11 @@ char* dlerror() void* dlsym(void* handle, const char* symbolName) { if ( dyld::gLogAPIs ) - fprintf(stderr, "%s(%p, %s)\n", __func__, handle, symbolName); + dyld::log("%s(%p, %s)\n", __func__, handle, symbolName); dlerrorClear(); - ImageLoader* image; + const ImageLoader* image; const ImageLoader::Symbol* sym; // dlsym() assumes symbolName passed in is same as in C source code @@ -1384,12 +1554,24 @@ void* dlsym(void* handle, const char* symbolName) // magic "search all" handle if ( handle == RTLD_DEFAULT ) { if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) { - return (void*)image->getExportedSymbolAddress(sym); + return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } - const char* format = "dlsym(RTLD_DEFAULT, %s): symbol not found"; - char temp[strlen(format)+strlen(symbolName)+2]; - sprintf(temp, format, symbolName); - dlerrorSet(temp); + const char* str = dyld::mkstringf("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + return NULL; + } + + // 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 + if ( sym != NULL ) { + return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + } + const char* str = dyld::mkstringf("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); return NULL; } @@ -1397,43 +1579,42 @@ void* dlsym(void* handle, const char* symbolName) if ( handle == RTLD_NEXT ) { void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - sym = callerImage->findExportedSymbolInDependentImages(underscoredName, &image); // don't search image, but do search what it links against + sym = callerImage->findExportedSymbolInDependentImages(underscoredName, dyld::gLinkContext, &image); // don't search image, but do search what it links against if ( sym != NULL ) { - return (void*)image->getExportedSymbolAddress(sym); + return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } - const char* format = "dlsym(RTLD_NEXT, %s): symbol not found"; - char temp[strlen(format)+strlen(symbolName)+2]; - sprintf(temp, format, symbolName); - dlerrorSet(temp); + const char* str = dyld::mkstringf("dlsym(RTLD_NEXT, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); return NULL; } -#ifdef RTLD_SELF // magic "search me, then what I would see" handle if ( handle == RTLD_SELF ) { void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); - sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, &image); // search image and what it links against + sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against if ( sym != NULL ) { - return (void*)image->getExportedSymbolAddress(sym); + return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } - const char* format = "dlsym(RTLD_SELF, %s): symbol not found"; - char temp[strlen(format)+strlen(symbolName)+2]; - sprintf(temp, format, symbolName); - dlerrorSet(temp); + const char* str = dyld::mkstringf("dlsym(RTLD_SELF, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); return NULL; } -#endif // real handle - image = (ImageLoader*)handle; + image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits if ( dyld::validImage(image) ) { - sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, &image); // search image and what it links against + if ( (((uintptr_t)handle) & 1) != 0 ) + sym = image->findExportedSymbol(underscoredName, NULL, true, &image); // search RTLD_FIRST way + else + sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against + if ( sym != NULL ) { - return (void*)image->getExportedSymbolAddress(sym); + return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); } - const char* format = "dlsym(%p, %s): symbol not found"; - char temp[strlen(format)+strlen(symbolName)+20]; - sprintf(temp, format, handle, symbolName); - dlerrorSet(temp); + const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName); + dlerrorSet(str); + free((void*)str); } else { dlerrorSet("invalid handle passed to dlsym()"); @@ -1442,196 +1623,69 @@ void* dlsym(void* handle, const char* symbolName) } -static void commitRepreboundFiles(std::vector files, bool unmapOld) + +void* dlord(void* handle, uint32_t ordinal) { - // tell file system to flush all dirty buffers to disk - // after this sync, the _redoprebinding files will be on disk - sync(); + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %u)\n", __func__, handle, ordinal); - // now commit (swap file) for each re-prebound image - // this only updates directories, since the files have already been flushed by previous sync() - for (std::vector::iterator it=files.begin(); it != files.end(); it++) { - (*it)->reprebindCommit(dyld::gLinkContext, true, unmapOld); - } + dlerrorClear(); - // tell file system to flush all dirty buffers to disk - // this should flush out all directory changes caused by the file swapping - sync(); + + // 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; } -#define UPDATE_PREBINDING_DRY_RUN 0x00000001 -#define UPDATE_PREBINDING_PROGRESS 0x00000002 -// -// SPI called only by update_prebinding tool to redo prebinding in all prebound files specified -// There must be no dylibs loaded when this fnction is called. -// -__attribute__((noreturn)) -static void _dyld_update_prebinding(int pathCount, const char* paths[], uint32_t flags) -{ - if ( dyld::gLogAPIs ) - fprintf(stderr, "%s()\n", __func__); - // list of requested dylibs actually loaded - std::vector preboundImages; - - try { - // verify no dylibs loaded - if ( dyld::getImageCount() != 1 ) - throw "_dyld_update_prebinding cannot be called with dylib already loaded"; - - // note that we are prebinding - dyld::gLinkContext.prebinding = true; - const uint32_t max_allowed_link_errors = 100; - uint32_t link_error_count = 0; - - // load and link each dylib - for (int i=0; i < pathCount; ++i) { - dyld::LoadContext context; - context.useSearchPaths = false; - context.matchByInstallName = true; - context.dontLoad = false; - context.mustBeBundle = false; - context.mustBeDylib = true; - context.origin = NULL; // @loader_path not allowed in prebinding list - context.rpath = NULL; // support not yet implemented - - ImageLoader* image = NULL; - try { - image = dyld::load(paths[i], context); - // bind lazy and non-lazy symbols, but don't run initializers - // this may bring in other dylibs later in the list or missing from list, but that is ok - dyld::link(image, ImageLoader::kLazyAndNonLazy, ImageLoader::kDontRunInitializers); - // recored images we successfully loaded - preboundImages.push_back(image); - } - catch (const char* msg) { - if ( dyld::gLinkContext.verbosePrebinding || (UPDATE_PREBINDING_DRY_RUN & flags) ) { - const char *stage; - if ( image == NULL ) // load exception - stage = "load"; - else // link exception - stage = "link"; - fprintf(stderr, "update_prebinding: warning: could not %s %s: %s\n", stage, paths[i], msg); - } - if ( (image != NULL) && image->isPrebindable() ) // don't count top level dylib being unprebound as an error - link_error_count++; - if ( link_error_count > max_allowed_link_errors ) { - fprintf(stderr, "update_prebinding: too many errors (%d) \n", link_error_count); - throw "terminating"; - } - } - } - - // find missing images - uint32_t loadedImageCount = dyld::getImageCount(); - if ( loadedImageCount > (preboundImages.size()+1) ) { - if ( dyld::gLinkContext.verbosePrebinding || (UPDATE_PREBINDING_DRY_RUN & flags) ) - fprintf(stderr, "update_prebinding: warning: the following dylibs were loaded but will not have their prebinding updated because they are not in the list of paths to reprebind\n"); - for (uint32_t i=1; i < loadedImageCount; ++i) { - ImageLoader* target = dyld::getIndexedImage(i); - bool found = false; - for (std::vector::iterator it=preboundImages.begin(); it != preboundImages.end(); it++) { - if ( *it == target ) { - found = true; - break; - } - } - if ( !found ) - if ( dyld::gLinkContext.verbosePrebinding || (UPDATE_PREBINDING_DRY_RUN & flags) ) - fprintf(stderr, " %s\n", target->getPath()); - } - } - // warn about unprebound files in the list - bool unpreboundWarned = false; - for (std::vector::iterator it=preboundImages.begin(); it != preboundImages.end(); it++) { - if ( ! (*it)->isPrebindable() && (*it != dyld::mainExecutable()) ) { - if ( ! unpreboundWarned ) { - if ( dyld::gLinkContext.verbosePrebinding || (UPDATE_PREBINDING_DRY_RUN & flags) ) - fprintf(stderr, "update_prebinding: warning: the following dylibs were specified but were not built prebound\n"); - unpreboundWarned = true; - } - if ( dyld::gLinkContext.verbosePrebinding || (UPDATE_PREBINDING_DRY_RUN & flags) ) - fprintf(stderr, " %s\n", (*it)->getPath()); - } - } - - if(UPDATE_PREBINDING_DRY_RUN & flags) { - fprintf(stderr, "update_prebinding: dry-run: no changes were made to the filesystem.\n"); - } - else { - uint32_t imageCount = preboundImages.size(); - uint32_t imageNumber = 1; - - // on Intel system, update_prebinding is run twice: i386, then emulated ppc - // calculate fudge factors so that progress output represents both runs - int denomFactor = 1; - int numerAddend = 0; - if (UPDATE_PREBINDING_PROGRESS & flags) { - #if __i386__ - // i386 half runs first, just double denominator - denomFactor = 2; - #endif - #if __ppc__ - // if emulated ppc, double denominator and shift numerator - int mib[] = { CTL_KERN, KERN_CLASSIC, getpid() }; - int is_emulated = 0; - size_t len = sizeof(int); - int ret = sysctl(mib, 3, &is_emulated, &len, NULL, 0); - if ((ret != -1) && is_emulated) { - denomFactor = 2; - numerAddend = imageCount; - } - #endif - } - // tell each image to write itself out re-prebound - struct timeval currentTime = { 0 , 0 }; - gettimeofday(¤tTime, NULL); - time_t timestamp = currentTime.tv_sec; - std::vector updatedImages; - for (std::vector::iterator it=preboundImages.begin(); it != preboundImages.end(); it++) { - uint64_t freespace = (*it)->reprebind(dyld::gLinkContext, timestamp); - updatedImages.push_back(*it); - if(UPDATE_PREBINDING_PROGRESS & flags) { - fprintf(stdout, "update_prebinding: progress: %3u/%u\n", imageNumber+numerAddend, imageCount*denomFactor); - fflush(stdout); - imageNumber++; - } - // see if we are running low on disk space (less than 32MB is "low") - const uint64_t kMinFreeSpace = 32*1024*1024; - if ( freespace < kMinFreeSpace ) { - if ( dyld::gLinkContext.verbosePrebinding || (UPDATE_PREBINDING_DRY_RUN & flags) ) - fprintf(stderr, "update_prebinding: disk space down to %lluMB, committing %lu prebound files\n", freespace/(1024*1024), updatedImages.size()); - // commit files processed so far, to free up more disk space - commitRepreboundFiles(updatedImages, true); - // empty list of temp files - updatedImages.clear(); - } - } - - // commit them, don't need to unmap old, cause we are done - commitRepreboundFiles(updatedImages, false); - } - } - catch (const char* msg) { - // delete temp files - try { - for (std::vector::iterator it=preboundImages.begin(); it != preboundImages.end(); it++) { - (*it)->reprebindCommit(dyld::gLinkContext, false, false); - } - } - catch (const char* commitMsg) { - fprintf(stderr, "update_prebinding: error: %s\n", commitMsg); - } - fprintf(stderr, "update_prebinding: error: %s\n", msg); - exit(1); - } - exit(0); -} @@ -1642,6 +1696,19 @@ static const struct dyld_all_image_infos* _dyld_get_all_image_infos() +void dyld_register_image_state_change_handler(dyld_image_states state, bool batch, + dyld_image_state_change_handler handler) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%d, %d, %p)\n", __func__, state, batch, handler); + if ( batch ) + dyld::registerImageStateBatchChangeHandler(state, handler); + else + dyld::registerImageStateSingleChangeHandler(state, handler); +} + + + diff --git a/src/dyldAPIsInLibSystem.cpp b/src/dyldAPIsInLibSystem.cpp index 67f4eb7..fbc172f 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-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -31,6 +31,9 @@ #include "dyldLock.h" +extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso); + + /* * names_match() takes an install_name from an LC_LOAD_DYLIB command and a * libraryName (which is -lx or -framework Foo argument passed to the static @@ -87,7 +90,7 @@ const char* libraryName) void NSInstallLinkEditErrorHandlers( const NSLinkEditErrorHandlers* handlers) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)( void (*undefined)(const char* symbol_name), NSModule (*multiple)(NSSymbol s, NSModule old, NSModule newhandler), @@ -103,7 +106,7 @@ const char* NSNameOfModule( NSModule module) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static const char* (*p)(NSModule module) = NULL; if(p == NULL) @@ -115,7 +118,7 @@ const char* NSLibraryNameForModule( NSModule module) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static const char* (*p)(NSModule module) = NULL; if(p == NULL) @@ -127,7 +130,7 @@ bool NSIsSymbolNameDefined( const char* symbolName) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(const char* symbolName) = NULL; if(p == NULL) @@ -140,7 +143,7 @@ NSIsSymbolNameDefinedWithHint( const char* symbolName, const char* libraryNameHint) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(const char* symbolName, const char* libraryNameHint) = NULL; @@ -154,7 +157,7 @@ NSIsSymbolNameDefinedInImage( const struct mach_header *image, const char* symbolName) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(const struct mach_header *image, const char* symbolName) = NULL; @@ -167,7 +170,7 @@ NSSymbol NSLookupAndBindSymbol( const char* symbolName) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSSymbol (*p)(const char* symbolName) = NULL; if(p == NULL) @@ -180,7 +183,7 @@ NSLookupAndBindSymbolWithHint( const char* symbolName, const char* libraryNameHint) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSSymbol (*p)(const char* symbolName, const char* libraryNameHint) = NULL; @@ -194,7 +197,7 @@ NSLookupSymbolInModule( NSModule module, const char* symbolName) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSSymbol (*p)(NSModule module, const char* symbolName) = NULL; if(p == NULL) @@ -208,7 +211,7 @@ const struct mach_header *image, const char* symbolName, uint32_t options) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSSymbol (*p)(const struct mach_header *image, const char* symbolName, uint32_t options) = NULL; @@ -222,7 +225,7 @@ const char* NSNameOfSymbol( NSSymbol symbol) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static char * (*p)(NSSymbol symbol) = NULL; if(p == NULL) @@ -234,7 +237,7 @@ void * NSAddressOfSymbol( NSSymbol symbol) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void * (*p)(NSSymbol symbol) = NULL; if(p == NULL) @@ -246,7 +249,7 @@ NSModule NSModuleForSymbol( NSSymbol symbol) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSModule (*p)(NSSymbol symbol) = NULL; if(p == NULL) @@ -258,7 +261,7 @@ bool NSAddLibrary( const char* pathName) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(const char* pathName) = NULL; if(p == NULL) @@ -270,7 +273,7 @@ bool NSAddLibraryWithSearching( const char* pathName) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(const char* pathName) = NULL; if(p == NULL) @@ -283,7 +286,7 @@ NSAddImage( const char* image_name, uint32_t options) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static const struct mach_header * (*p)(const char* image_name, uint32_t options) = NULL; @@ -311,30 +314,30 @@ const char* libraryName) struct load_command *load_commands, *lc; struct dylib_command *dl; char *install_name; -#ifndef __OPENSTEP__ +#if __LP64__ + static struct mach_header_64 *mh = NULL; +#else static struct mach_header *mh = NULL; +#endif if(mh == NULL) mh = _NSGetMachExecuteHeader(); -#else /* defined(__OPENSTEP__) */ -#ifdef __DYNAMIC__ - static struct mach_header *mh = NULL; - if(mh == NULL) - _dyld_lookup_and_bind("__mh_execute_header", &mh, NULL); + load_commands = (struct load_command *) +#if __LP64__ + ((char *)mh + sizeof(struct mach_header_64)); #else - struct mach_header *mh; - mh = (struct mach_header *)&_mh_execute_header; + ((char *)mh + sizeof(struct mach_header)); #endif -#endif /* __OPENSTEP__ */ - load_commands = (struct load_command *) - ((char *)mh + sizeof(struct mach_header)); lc = load_commands; for(i = 0; i < mh->ncmds; i++){ - if(lc->cmd == LC_LOAD_DYLIB){ - dl = (struct dylib_command *)lc; - install_name = (char *)dl + dl->dylib.name.offset; - if(names_match(install_name, libraryName) == TRUE) - return(dl->dylib.current_version); - } + switch ( lc->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + dl = (struct dylib_command *)lc; + install_name = (char *)dl + dl->dylib.name.offset; + if(names_match(install_name, libraryName) == TRUE) + return(dl->dylib.current_version); + break; + } lc = (struct load_command *)((char *)lc + lc->cmdsize); } return(-1); @@ -363,7 +366,11 @@ const char* libraryName) if(mh->filetype != MH_DYLIB) continue; load_commands = (struct load_command *) +#if __LP64__ + ((char *)mh + sizeof(struct mach_header_64)); +#else ((char *)mh + sizeof(struct mach_header)); +#endif lc = load_commands; for(j = 0; j < mh->ncmds; j++){ if(lc->cmd == LC_ID_DYLIB){ @@ -390,7 +397,7 @@ NSCreateObjectFileImageFromFile( const char* pathName, NSObjectFileImage *objectFileImage) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; if(p == NULL) @@ -412,7 +419,7 @@ const void* address, size_t size, NSObjectFileImage *objectFileImage) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSObjectFileImageReturnCode (*p)(const void*, size_t, NSObjectFileImage*) = NULL; if(p == NULL) @@ -420,6 +427,7 @@ NSObjectFileImage *objectFileImage) return p(address, size, objectFileImage); } +#if OBSOLETE_DYLD_API /* * NSCreateCoreFileImageFromFile() creates an NSObjectFileImage for the * specified core file name if the file is a correct Mach-O core file. @@ -431,19 +439,20 @@ NSCreateCoreFileImageFromFile( const char* pathName, NSObjectFileImage *objectFileImage) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_NSCreateCoreFileImageFromFile", (void**)&p); return p(pathName, objectFileImage); } +#endif bool NSDestroyObjectFileImage( NSObjectFileImage objectFileImage) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(NSObjectFileImage) = NULL; if(p == NULL) @@ -458,7 +467,7 @@ NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static NSModule (*p)(NSObjectFileImage, const char*, unsigned long) = NULL; if(p == NULL) @@ -485,7 +494,7 @@ const char** segmentName, /* can be NULL */ const char** sectionName, /* can be NULL */ unsigned long* sectionOffset) /* can be NULL */ { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(NSObjectFileImage, unsigned long, const char**, const char**, unsigned long*) = NULL; if(p == NULL) @@ -503,7 +512,7 @@ uint32_t NSSymbolDefinitionCountInObjectFileImage( NSObjectFileImage objectFileImage) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static unsigned long (*p)(NSObjectFileImage) = NULL; if(p == NULL) @@ -523,7 +532,7 @@ NSSymbolDefinitionNameInObjectFileImage( NSObjectFileImage objectFileImage, uint32_t ordinal) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static const char* (*p)(NSObjectFileImage, uint32_t) = NULL; if(p == NULL) @@ -540,7 +549,7 @@ uint32_t NSSymbolReferenceCountInObjectFileImage( NSObjectFileImage objectFileImage) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static unsigned long (*p)(NSObjectFileImage) = NULL; if(p == NULL) @@ -561,7 +570,7 @@ NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) /* can be NULL */ { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static const char* (*p)(NSObjectFileImage, uint32_t, bool*) = NULL; if(p == NULL) @@ -579,7 +588,7 @@ NSIsSymbolDefinedInObjectFileImage( NSObjectFileImage objectFileImage, const char* symbolName) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(NSObjectFileImage, const char*) = NULL; if(p == NULL) @@ -602,7 +611,7 @@ const char* segmentName, const char* sectionName, unsigned long *size) /* can be NULL */ { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void* (*p)(NSObjectFileImage, const char*, const char*, unsigned long*) = NULL; if(p == NULL) @@ -621,7 +630,7 @@ bool NSHasModInitObjectFileImage( NSObjectFileImage objectFileImage) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(NSObjectFileImage) = NULL; if(p == NULL) @@ -637,7 +646,7 @@ int *errorNumber, const char* *fileName, const char* *errorString) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(NSLinkEditErrors *c, int *errorNumber, const char* *fileName, @@ -654,7 +663,7 @@ NSUnLinkModule( NSModule module, uint32_t options) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(NSModule module, uint32_t options) = NULL; if(p == NULL) @@ -663,6 +672,7 @@ uint32_t options) return p(module, options); } +#if OBSOLETE_DYLD_API NSModule NSReplaceModule( NSModule moduleToReplace, @@ -671,6 +681,7 @@ uint32_t options) { return(NULL); } +#endif /* *_NSGetExecutablePath copies the path of the executable into the buffer and @@ -686,7 +697,7 @@ _NSGetExecutablePath( char *buf, uint32_t *bufsize) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static int (*p)(char *buf, uint32_t *bufsize) = NULL; if(p == NULL) @@ -700,7 +711,7 @@ const char* symbol_name, void** address, NSModule* module) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(const char*, void** , NSModule*) = NULL; if(p == NULL) @@ -715,7 +726,7 @@ const char* library_name_hint, void** address, NSModule* module) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(const char*, const char*, void**, NSModule*) = NULL; if(p == NULL) @@ -723,19 +734,21 @@ NSModule* module) p(symbol_name, library_name_hint, address, module); } +#if OBSOLETE_DYLD_API void _dyld_lookup_and_bind_objc( const char* symbol_name, void** address, NSModule* module) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(const char* , void**, NSModule*) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_lookup_and_bind_objc", (void**)&p); p(symbol_name, address, module); } +#endif void _dyld_lookup_and_bind_fully( @@ -743,7 +756,7 @@ const char* symbol_name, void** address, NSModule* module) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(const char*, void**, NSModule*) = NULL; if(p == NULL) @@ -755,7 +768,7 @@ bool _dyld_bind_fully_image_containing_address( const void* address) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(const void*) = NULL; if(p == NULL) @@ -774,7 +787,7 @@ void _dyld_register_func_for_add_image( void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) = NULL; if(p == NULL) @@ -791,7 +804,7 @@ void _dyld_register_func_for_remove_image( void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) = NULL; if(p == NULL) @@ -799,6 +812,7 @@ void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) p(func); } +#if OBSOLETE_DYLD_API /* * _dyld_register_func_for_link_module registers the specified function to be * called when a module is bound into the program. When this function is first @@ -809,7 +823,7 @@ void _dyld_register_func_for_link_module( void (*func)(NSModule module)) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(void (*func)(NSModule module)) = NULL; if(p == NULL) @@ -825,7 +839,7 @@ void _dyld_register_func_for_unlink_module( void (*func)(NSModule module)) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(void (*func)(NSModule module)) = NULL; if(p == NULL) @@ -841,7 +855,7 @@ void _dyld_register_func_for_replace_module( void (*func)(NSModule oldmodule, NSModule newmodule)) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(void (*func)(NSModule oldmodule, NSModule newmodule)) = NULL; @@ -862,7 +876,7 @@ NSModule module, void **objc_module, unsigned long *size) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(NSModule module, void **objc_module, unsigned long *size) = NULL; @@ -878,45 +892,37 @@ unsigned long *size) * to be bound. */ void -_dyld_bind_objc_module( -const void* objc_module) +_dyld_bind_objc_module(const void* objc_module) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(const void *objc_module) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_bind_objc_module", (void**)&p); p(objc_module); } +#endif - -#if __DYNAMIC__ bool -_dyld_present( -void) +_dyld_present(void) { - // hmmm, this code is in libSystem.dylib, which is loaded by dyld... + // this function exists for compatiblity only return true; } -#endif uint32_t -_dyld_image_count( -void) +_dyld_image_count(void) { DYLD_NO_LOCK_THIS_BLOCK; static unsigned long (*p)(void) = NULL; - if(_dyld_present() == 0) - return(0); if(p == NULL) _dyld_func_lookup("__dyld_image_count", (void**)&p); return(p()); } const struct mach_header * -_dyld_get_image_header( -uint32_t image_index) +_dyld_get_image_header(uint32_t image_index) { DYLD_NO_LOCK_THIS_BLOCK; static struct mach_header * (*p)(uint32_t image_index) = NULL; @@ -927,8 +933,7 @@ uint32_t image_index) } intptr_t -_dyld_get_image_vmaddr_slide( -uint32_t image_index) +_dyld_get_image_vmaddr_slide(uint32_t image_index) { DYLD_NO_LOCK_THIS_BLOCK; static unsigned long (*p)(uint32_t image_index) = NULL; @@ -939,8 +944,7 @@ uint32_t image_index) } const char* -_dyld_get_image_name( -uint32_t image_index) +_dyld_get_image_name(uint32_t image_index) { DYLD_NO_LOCK_THIS_BLOCK; static const char* (*p)(uint32_t image_index) = NULL; @@ -951,10 +955,9 @@ uint32_t image_index) } bool -_dyld_image_containing_address( -const void* address) +_dyld_image_containing_address(const void* address) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(const void*) = NULL; if(p == NULL) @@ -966,7 +969,7 @@ const struct mach_header * _dyld_get_image_header_containing_address( const void* address) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static const struct mach_header * (*p)(const void*) = NULL; if(p == NULL) @@ -977,7 +980,7 @@ const void* address) void _dyld_moninit( void (*monaddition)(char *lowpc, char *highpc)) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void (*p)(void (*monaddition)(char *lowpc, char *highpc)) = NULL; if(p == NULL) @@ -985,10 +988,9 @@ void (*monaddition)(char *lowpc, char *highpc)) p(monaddition); } -bool _dyld_launched_prebound( -void) +bool _dyld_launched_prebound(void) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(void) = NULL; if(p == NULL) @@ -996,10 +998,9 @@ void) return(p()); } -bool _dyld_all_twolevel_modules_prebound( -void) +bool _dyld_all_twolevel_modules_prebound(void) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static bool (*p)(void) = NULL; if(p == NULL) @@ -1013,11 +1014,14 @@ void) #include #include #include -#include "dyldLock.h" -#include "dyldLibSystemThreadHelpers.h" +#include +#include "dyldLibSystemInterface.h" +#include "dyld_shared_cache_user.h" + // pthread key used to access per-thread dlerror message static pthread_key_t dlerrorPerThreadKey; +static bool dlerrorPerThreadKeyInitialized = false; // data kept per-thread struct dlerrorPerThreadData @@ -1029,6 +1033,13 @@ struct dlerrorPerThreadData // function called by dyld to get buffer to store dlerror message static char* getPerThreadBufferFor_dlerror(uint32_t sizeRequired) { + // ok to create key lazily because this function is called within dyld lock, so there is no race condition + if (!dlerrorPerThreadKeyInitialized ) { + // create key and tell pthread package to call free() on any data associated with key if thread dies + pthread_key_create(&dlerrorPerThreadKey, &free); + dlerrorPerThreadKeyInitialized = true; + } + const int size = (sizeRequired < 256) ? 256 : sizeRequired; dlerrorPerThreadData* data = (dlerrorPerThreadData*)pthread_getspecific(dlerrorPerThreadKey); if ( data == NULL ) { @@ -1049,37 +1060,77 @@ static char* getPerThreadBufferFor_dlerror(uint32_t sizeRequired) return data->message; } + + +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 + +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); +} + +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); +} + + + // that table passed to dyld containing thread helpers -static dyld::ThreadingHelpers sThreadHelpers = { 1, &lockForLazyBinding, &unlockForLazyBinding, &getPerThreadBufferFor_dlerror }; +static dyld::LibSystemHelpers sHelpers = { 4, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, + &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, + &shared_cache_missing, &shared_cache_out_of_date, + NULL, NULL }; + // // during initialization of libSystem this routine will run -// and call dyld, registering the threading helpers. -// +// and call dyld, registering the helper functions. // -static int registerWithDyld() +extern "C" void _dyld_initializer() __attribute__((visibility("hidden"))); +void _dyld_initializer() { - static void (*p)(dyld::ThreadingHelpers*) = NULL; - - // create key and tell pthread package to call free() on any data associated with key if thread dies - pthread_key_create(&dlerrorPerThreadKey, &free); + DYLD_LOCK_INITIALIZER; - if(p == NULL) - _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p); + void (*p)(dyld::LibSystemHelpers*); + + _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p); if(p != NULL) - p(&sThreadHelpers); - - return 0; + p(&sHelpers); } -// should be able to use __attribute__((constructor)) on registerWithDyld, but compiler has bug (3679135) -// instead use initialization of global to force it to run. -static int hack = registerWithDyld(); - char* dlerror() { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static char* (*p)() = NULL; if(p == NULL) @@ -1089,7 +1140,7 @@ char* dlerror() int dladdr(const void* addr, Dl_info* info) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static int (*p)(const void* , Dl_info*) = NULL; if(p == NULL) @@ -1099,7 +1150,7 @@ int dladdr(const void* addr, Dl_info* info) int dlclose(void* handle) { - DYLD_WRITER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static int (*p)(void* handle) = NULL; if(p == NULL) @@ -1108,18 +1159,36 @@ int dlclose(void* handle) } void* dlopen(const char* path, int mode) -{ - DYLD_WRITER_LOCK_THIS_BLOCK; +{ + // dlopen is special. locking is done inside dyld to allow initializer to run without lock + DYLD_NO_LOCK_THIS_BLOCK; + static void* (*p)(const char* path, int) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_dlopen", (void**)&p); - return(p(path, mode)); + void* result = p(path, mode); + // use asm block to prevent tail call optimization + // this is needed because dlopen uses __builtin_return_address() and depends on this glue being in the frame chain + // + __asm__ volatile(""); + + return result; +} + +bool dlopen_preflight(const char* path) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* path) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlopen_preflight", (void**)&p); + return(p(path)); } void* dlsym(void* handle, const char* symbol) { - DYLD_READER_LOCK_THIS_BLOCK; + DYLD_LOCK_THIS_BLOCK; static void* (*p)(void* handle, const char* symbol) = NULL; if(p == NULL) @@ -1127,6 +1196,17 @@ void* dlsym(void* handle, const char* symbol) return(p(handle, symbol)); } +void dyld_register_image_state_change_handler(dyld_image_states state, + bool batch, dyld_image_state_change_handler handler) +{ + DYLD_LOCK_THIS_BLOCK; + static void* (*p)(dyld_image_states, bool, dyld_image_state_change_handler) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dyld_register_image_state_change_handler", (void**)&p); + p(state, batch, handler); +} + diff --git a/src/dyldExceptions.c b/src/dyldExceptions.c index ea022cc..133e978 100644 --- a/src/dyldExceptions.c +++ b/src/dyldExceptions.c @@ -24,8 +24,8 @@ #include #include -#include #include +#include // // BEGIN copy of code from libgcc.a source file unwind-dw2-fde-darwin.c @@ -98,8 +98,8 @@ const Tinfo_Node* __keymgr_global[3] = { NULL, NULL, &keymgr_info }; static __attribute__((noreturn)) void dyld_abort() { - fprintf(stderr, "internal dyld error\n"); - abort(); + //dyld::log("internal dyld error\n"); + _exit(1); } void* _keymgr_get_and_lock_processwide_ptr(unsigned int key) diff --git a/src/dyldInitialization.cpp b/src/dyldInitialization.cpp index a4e8cc3..61b23b3 100644 --- a/src/dyldInitialization.cpp +++ b/src/dyldInitialization.cpp @@ -22,8 +22,11 @@ * @APPLE_LICENSE_HEADER_END@ */ +#define __STDC_LIMIT_MACROS +#include #include #include +#include #include #include #include @@ -36,6 +39,11 @@ #endif #include "dyld.h" +#ifndef MH_PIE + #define MH_PIE 0x200000 +#endif + + #if __LP64__ #define macho_header mach_header_64 #define LC_SEGMENT_COMMAND LC_SEGMENT_64 @@ -56,6 +64,10 @@ #define POINTER_RELOC GENERIC_RELOC_VANILLA #endif +// from dyld.cpp +namespace dyld { extern bool isRosetta(); }; + + // // Code to bootstrap dyld into a runnable state // @@ -194,7 +206,7 @@ static void segmentProtectDyld(const struct macho_header* mh, intptr_t slide) vm_size_t size = seg->vmsize; const bool setCurrentPermissions = false; vm_protect(mach_task_self(), addr, size, setCurrentPermissions, seg->initprot); - //fprintf(stderr, "dyld: segment %s, 0x%08X -> 0x%08X, set to %d\n", seg->segname, addr, addr+size-1, seg->initprot); + //dyld::log("dyld: segment %s, 0x%08X -> 0x%08X, set to %d\n", seg->segname, addr, addr+size-1, seg->initprot); } break; } @@ -203,9 +215,114 @@ 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) +{ +#if __ppc__ + // don't slide PIE programs running under rosetta + if ( dyld::isRosetta() ) + return orgMH; +#endif + // count segments + uint32_t segCount = 0; + const uint32_t cmd_count = orgMH->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)orgMH)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; + // page-zero and custom stacks don't move + if ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) ) + ++segCount; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // make copy of segment info + macho_segment_command segs[segCount]; + uint32_t index = 0; + uintptr_t highestAddressUsed = 0; + uintptr_t lowestAddressUsed = UINTPTR_MAX; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; + if ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) ) { + segs[index++] = *segCmd; + if ( (segCmd->vmaddr + segCmd->vmsize) > highestAddressUsed ) + highestAddressUsed = ((segCmd->vmaddr + segCmd->vmsize) + 4095) & -4096; + if ( segCmd->vmaddr < lowestAddressUsed ) + lowestAddressUsed = segCmd->vmaddr; + // do nothing if kernel has already randomized load address + if ( (strcmp(segCmd->segname, "__TEXT") == 0) && (segCmd->vmaddr != (uintptr_t)orgMH) ) + return orgMH; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // choose a random new base address +#if __LP64__ + uintptr_t highestAddressPossible = highestAddressUsed + 0x100000000ULL; +#else + uintptr_t highestAddressPossible = 0x80000000; +#endif + uintptr_t sizeNeeded = highestAddressUsed-lowestAddressUsed; + if ( (highestAddressPossible-sizeNeeded) < highestAddressUsed ) { + // new and old segments will overlap + // need better algorithm for remapping + // punt and don't re-map + return orgMH; + } + uintptr_t possibleRange = (highestAddressPossible-sizeNeeded) - highestAddressUsed; + uintptr_t newBaseAddress = highestAddressUsed + ((arc4random() % possibleRange) & -4096); + + vm_address_t addr = newBaseAddress; + // reserve new address range + if ( vm_allocate(mach_task_self(), &addr, sizeNeeded, VM_FLAGS_FIXED) == KERN_SUCCESS ) { + // copy each segment to new address + for (uint32_t i = 0; i < segCount; ++i) { + uintptr_t newSegAddress = segs[i].vmaddr - lowestAddressUsed + newBaseAddress; + if ( (vm_copy(mach_task_self(), segs[i].vmaddr, segs[i].vmsize, newSegAddress) != KERN_SUCCESS) + || (vm_protect(mach_task_self(), newSegAddress, segs[i].vmsize, true, segs[i].maxprot) != KERN_SUCCESS) + || (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"); + return orgMH; + } + } + // unmap original segments + vm_deallocate(mach_task_self(), lowestAddressUsed, highestAddressUsed-lowestAddressUsed); + + // run with newly mapped executable + *appsSlide = newBaseAddress - lowestAddressUsed; + return (const struct mach_header*)newBaseAddress; + } + + // can't get new range, so don't slide to random address + return orgMH; +} + + extern "C" void dyld_exceptions_init(const struct macho_header*, uintptr_t slide); // in dyldExceptions.cpp extern "C" void mach_init(); +// +// _pthread_keys is partitioned in a lower part that dyld will use; libSystem +// will use the upper part. We set __pthread_tsd_first to 1 as the start of +// the lower part. Libc will take #1 and c++ exceptions will take #2. There +// is one free key=3 left. +// +extern "C" { + extern int __pthread_tsd_first; + extern void _pthread_keys_init(); +} + + // // 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. @@ -221,6 +338,12 @@ uintptr_t start(const struct mach_header* appsMachHeader, int argc, const char* rebaseDyld(dyldsMachHeader, slide); } + 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); @@ -241,8 +364,12 @@ uintptr_t start(const struct mach_header* appsMachHeader, int argc, const char* // run all C++ initializers inside dyld runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); + // if main executable was linked -pie, then randomize its load address + if ( appsMachHeader->flags & MH_PIE ) + appsMachHeader = randomizeExecutableLoadAddress(appsMachHeader, &appsSlide); + // now that we are done bootstrapping dyld, call dyld's main - return dyld::_main(appsMachHeader, argc, argv, envp, apple); + return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple); } diff --git a/src/dyldLibSystemThreadHelpers.h b/src/dyldLibSystemInterface.h similarity index 76% rename from src/dyldLibSystemThreadHelpers.h rename to src/dyldLibSystemInterface.h index ac5e3dc..c110616 100644 --- a/src/dyldLibSystemThreadHelpers.h +++ b/src/dyldLibSystemInterface.h @@ -35,12 +35,22 @@ namespace dyld { // This struct is implemented in in libSystem (where pthreads is available) // and passed to dyld to use. // - struct ThreadingHelpers + struct LibSystemHelpers { uintptr_t version; - void (*lockForReading)(); - void (*unlockForReading)(); + void (*acquireGlobalDyldLock)(); + void (*releaseGlobalDyldLock)(); char* (*getThreadBufferFor_dlerror)(uint32_t sizeRequired); + // addded in version 2 + void* (*malloc)(size_t); + void (*free)(void*); + int (*cxa_atexit)(void (*)(void*), void*, void*); + // addded in version 3 + void (*dyld_shared_cache_missing)(); + void (*dyld_shared_cache_out_of_date)(); + // addded in version 4 + void (*acquireDyldInitializerLock)(); + void (*releaseDyldInitializerLock)(); }; }; diff --git a/src/dyldLock.cpp b/src/dyldLock.cpp index 496ed92..b94551b 100644 --- a/src/dyldLock.cpp +++ b/src/dyldLock.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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -26,262 +26,42 @@ #include "dyldLock.h" -// until the reader/writer locks are fully tested, we just use a simple recursive mutex -#define REAL_READER_WRITER_LOCK 0 - -// -// This class implements a recursive reader/writer lock. -// Recursive means a thread that has already aquired the lock can re-acquire it. -// The lock allows either: -// a) one "writer" thread to hold lock -// b) multiple "reader" threads to hold -// -// A thread with the writer can acquire a reader lock. -// A thread with a reader lock can acquire a writer lock iff it is the only thread with a reader lock -// -class RecursiveReaderWriterLock -{ -public: - RecursiveReaderWriterLock() __attribute__((visibility("hidden"))); - void initIfNeeded(); - - - void lockForSingleWritingThread() __attribute__((visibility("hidden"))); - void unlockForSingleWritingThread() __attribute__((visibility("hidden"))); - - void lockForMultipleReadingThreads() __attribute__((visibility("hidden"))); - void unlockForMultipleReadingThreads() __attribute__((visibility("hidden"))); - -private: -#if REAL_READER_WRITER_LOCK - struct ThreadRecursionCount { - pthread_t fThread; - uint32_t fCount; - }; - bool writerThreadIsAnyThreadBut(pthread_t thread); - void writerThreadRetain(pthread_t thread); - bool writerThreadRelease(pthread_t thread); - - enum { kMaxReaderThreads = 4 }; - bool readerThreadSetRetain(pthread_t thread); - bool readerThreadSetRelease(pthread_t thread); - bool readerThreadSetContainsAnotherThread(pthread_t thread); - - ThreadRecursionCount fWriterThread; - ThreadRecursionCount fReaderThreads[kMaxReaderThreads]; - pthread_cond_t fLockFree; - pthread_mutex_t fMutex; -#else - pthread_mutex_t fMutex; -#endif - bool fInitialized; // assumes this is statically initialized to false because sLock is static -}; +static pthread_mutex_t sGlobalMutex; // -// initIfNeeded() is a hack so that when libSystem_debug.dylb is useable. -// The problem is that Objective-C +load methods are called before C++ initialziers are run -// If +load method makes a call into dyld, sLock is not initialized. +// This initializer can go away once the following is available: +// implement PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP // -// The long term solution is for objc and dyld to work more closely together so that instead -// of running all +load methods before all initializers, we run each image's +load then its -// initializers all in bottom up order. -// -// This lazy initialization is not thread safe, but as long as someone does not create a -// new thread in a +load method, the C++ constructor for sLock will be called before main() -// so there will only be one thead. -// -void RecursiveReaderWriterLock::initIfNeeded() -{ - if ( ! fInitialized ) { - pthread_mutexattr_t recursiveMutexAttr; - pthread_mutexattr_init(&recursiveMutexAttr); - pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&fMutex, &recursiveMutexAttr); - #if REAL_READER_WRITER_LOCK - pthread_cond_init(&fLockFree, NULL); - fWriterThread.fThread = NULL; - fWriterThread.fCount = 0; - for (int i=0; i < kMaxReaderThreads; ++i) { - fReaderThreads[i].fThread = NULL; - fReaderThreads[i].fCount = 0; - } - #endif - fInitialized = true; - } -} - -RecursiveReaderWriterLock::RecursiveReaderWriterLock() -{ - initIfNeeded(); -} - -void RecursiveReaderWriterLock::lockForSingleWritingThread() -{ - this->initIfNeeded(); -#if REAL_READER_WRITER_LOCK - pthread_mutex_lock(&fMutex); - pthread_t thisThread = pthread_self(); - // wait as long as there is another writer or any readers on a different thread - while ( writerThreadIsAnyThreadBut(thisThread) || readerThreadSetContainsAnotherThread(thisThread) ) { - pthread_cond_wait(&fLockFree, &fMutex); - } - writerThreadRetain(thisThread); - pthread_mutex_unlock(&fMutex); -#else - pthread_mutex_lock(&fMutex); -#endif -} - -void RecursiveReaderWriterLock::unlockForSingleWritingThread() -{ - this->initIfNeeded(); -#if REAL_READER_WRITER_LOCK - pthread_mutex_lock(&fMutex); - if ( writerThreadRelease(pthread_self()) ) { - pthread_cond_broadcast(&fLockFree); - } - pthread_mutex_unlock(&fMutex); -#else - pthread_mutex_unlock(&fMutex); -#endif -} - - -void RecursiveReaderWriterLock::lockForMultipleReadingThreads() -{ - this->initIfNeeded(); -#if REAL_READER_WRITER_LOCK - pthread_mutex_lock(&fMutex); - pthread_t thisThread = pthread_self(); - // wait as long as there is a writer on another thread or too many readers already - while ( writerThreadIsAnyThreadBut(thisThread) || !readerThreadSetRetain(thisThread) ) { - pthread_cond_wait(&fLockFree, &fMutex); - } - pthread_mutex_unlock(&fMutex); -#else - pthread_mutex_lock(&fMutex); -#endif -} - - -void RecursiveReaderWriterLock::unlockForMultipleReadingThreads() -{ - this->initIfNeeded(); -#if REAL_READER_WRITER_LOCK - pthread_mutex_lock(&fMutex); - if ( readerThreadSetRelease(pthread_self()) ) { - pthread_cond_broadcast(&fLockFree); - } - pthread_mutex_unlock(&fMutex); -#else - pthread_mutex_unlock(&fMutex); -#endif -} - -#if REAL_READER_WRITER_LOCK -bool RecursiveReaderWriterLock::writerThreadIsAnyThreadBut(pthread_t thread) -{ - return ( (fWriterThread.fThread != NULL) && (fWriterThread.fThread != thread) ); -} - -void RecursiveReaderWriterLock::writerThreadRetain(pthread_t thread) +void dyldGlobalLockInitialize() { - ++fWriterThread.fCount; + pthread_mutexattr_t recursiveMutexAttr; + pthread_mutexattr_init(&recursiveMutexAttr); + pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&sGlobalMutex, &recursiveMutexAttr); } -bool RecursiveReaderWriterLock::writerThreadRelease(pthread_t thread) -{ - return ( --fWriterThread.fCount == 0 ); -} - -bool RecursiveReaderWriterLock::readerThreadSetRetain(pthread_t thread) -{ - // if thread is already in set, bump its count - for (int i=0; i < kMaxReaderThreads; ++i) { - if ( fReaderThreads[i].fThread == thread ) { - ++fReaderThreads[i].fCount; - return true; - } - } - // find empty slot in set - for (int i=0; i < kMaxReaderThreads; ++i) { - if ( fReaderThreads[i].fThread == NULL ) { - fReaderThreads[i].fThread = thread; - fReaderThreads[i].fCount = 1; - return true; - } - } - - // all reader slots full - return false; -} - -bool RecursiveReaderWriterLock::readerThreadSetRelease(pthread_t thread) -{ - for (int i=0; i < kMaxReaderThreads; ++i) { - if ( fReaderThreads[i].fThread == thread ) { - if ( --fReaderThreads[i].fCount == 0 ) { - fReaderThreads[i].fThread = NULL; - return true; - } - return false; - } - } - // should never get here - return false; -} - -bool RecursiveReaderWriterLock::readerThreadSetContainsAnotherThread(pthread_t thread) -{ - for (int i=0; i < kMaxReaderThreads; ++i) { - if ( (fReaderThreads[i].fThread != NULL) && (fReaderThreads[i].fThread != thread) ) - return true; - } - return false; -} -#endif - - -// dyld's global reader/writer lock -static RecursiveReaderWriterLock sLock; - - -LockReaderHelper::LockReaderHelper() -{ - sLock.lockForMultipleReadingThreads(); -} - -LockReaderHelper::~LockReaderHelper() +LockHelper::LockHelper() { - sLock.unlockForMultipleReadingThreads(); + pthread_mutex_lock(&sGlobalMutex); } - -LockWriterHelper::LockWriterHelper() +LockHelper::~LockHelper() { - sLock.lockForSingleWritingThread(); + pthread_mutex_unlock(&sGlobalMutex); } -LockWriterHelper::~LockWriterHelper() -{ - sLock.unlockForSingleWritingThread(); -} - - -// needed by lazy binding -void lockForLazyBinding() +void dyldGlobalLockAcquire() { - sLock.lockForMultipleReadingThreads(); + pthread_mutex_lock(&sGlobalMutex); } -void unlockForLazyBinding() +void dyldGlobalLockRelease() { - sLock.unlockForMultipleReadingThreads(); + pthread_mutex_unlock(&sGlobalMutex); } - diff --git a/src/dyldLock.h b/src/dyldLock.h index d6291fb..2f4ec59 100644 --- a/src/dyldLock.h +++ b/src/dyldLock.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-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -27,26 +27,17 @@ #define __DYLDLOCK__ // -// This file contains the syncronization utilities used by dyld to be thread safe. -// Access to all dyld data structures is protected by a reader-writer lock. -// This lock allows multiple "reader" threads to access dyld data structures at -// the same time. If there is a "writer" thread, it is the only thread allowed -// access to dyld data structures. +// This file contains the synchronization utilities used to make dyld APIs be thread safe. // -// The implementation of all dyld API's must acquire the global lock around accesses -// to dyld's data structures. This is done using the macros DYLD_*_LOCK_THIS_BLOCK. +// The implementation of all dyld API's must hold acquire global lock (in libSystem) +// before calling into dyld proper, and release the lock after returning from dyld. +// This is done using the macros DYLD_LOCK_THIS_BLOCK. // Example: // -// void dyld_api_modifying_dyld() { -// DYLD_WRITER_LOCK_THIS_BLOCK; +// void dyld_api() { +// DYLD_LOCK_THIS_BLOCK; // // free to do stuff here -// // that modifies dyld data structures -// } -// -// void dyld_api_examinging_dyld() { -// DYLD_READER_LOCK_THIS_BLOCK -// // can only do stuff here -// // that examines but does not modify dyld data structures +// // that accesses dyld internal data structures // } // // void dyld_api_state_free() { @@ -56,32 +47,23 @@ // } // - -#define DYLD_READER_LOCK_THIS_BLOCK LockReaderHelper _dyld_lock; -#define DYLD_WRITER_LOCK_THIS_BLOCK LockWriterHelper _dyld_lock; +#define DYLD_LOCK_INITIALIZER dyldGlobalLockInitialize() +#define DYLD_LOCK_THIS_BLOCK LockHelper _dyld_lock; #define DYLD_NO_LOCK_THIS_BLOCK // used by dyld wrapper functions in libSystem -class LockReaderHelper -{ -public: - LockReaderHelper() __attribute__((visibility("hidden"))); - ~LockReaderHelper() __attribute__((visibility("hidden"))); -}; - -// used by dyld wrapper functions in libSystem -class LockWriterHelper +class __attribute__((visibility("hidden"))) LockHelper { public: - LockWriterHelper() __attribute__((visibility("hidden"))); - ~LockWriterHelper() __attribute__((visibility("hidden"))); + LockHelper(); + ~LockHelper(); }; -// used by lazy binding -extern void lockForLazyBinding() __attribute__((visibility("hidden"))); -extern void unlockForLazyBinding() __attribute__((visibility("hidden"))); - +// to initialize +extern void dyldGlobalLockInitialize() __attribute__((visibility("hidden"))); +extern void dyldGlobalLockAcquire() __attribute__((visibility("hidden"))); +extern void dyldGlobalLockRelease() __attribute__((visibility("hidden"))); #endif // __DYLDLOCK__ diff --git a/src/dyldNew.cpp b/src/dyldNew.cpp index a74d858..4a5dcad 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-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -22,53 +22,91 @@ * @APPLE_LICENSE_HEADER_END@ */ -#include -#include -//#include +#include +#include +#include + +extern "C" void* __dso_handle; + +#include "dyld.h" +#include "dyldLibSystemInterface.h" // -// dyld does not use malloc anywhere, instead C++ new is used. -// All dyld allocations go in dyld-only zone so as to be not co-mingled with target proccess's allocations -// -// +// dyld initially allocates all memory from a pool inside dyld. +// Once libSystem.dylib is initialized, dyld uses libSystem's malloc/free. // -static malloc_zone_t* sZone = NULL; // could be initialized to malloc_create_zone, but that would require careful ordering of initializers - +#if __LP64__ + // room for about ~1000 initial dylibs + #define DYLD_INITIAL_POOL_SIZE 400*1024 +#else + // room for about ~900 initial dylibs + #define DYLD_INITIAL_POOL_SIZE 200*1024 +#endif +static uint8_t dyldPool[DYLD_INITIAL_POOL_SIZE]; +static uint8_t* curPoolPtr = dyldPool; -void* operator new(std::size_t len) throw (std::bad_alloc) +void* malloc(size_t size) { - if ( sZone == NULL ) { - sZone = malloc_create_zone(40960, 0); - malloc_set_zone_name(sZone, "dyld heap"); + if ( dyld::gLibSystemHelpers != NULL) { + void* p = dyld::gLibSystemHelpers->malloc(size); + //dyld::log("malloc(%lu) => %p from libSystem\n", size, p); + return p; + } + 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); + } + curPoolPtr += size; + //dyld::log("%p = malloc(%lu) from pool, total = %d\n", result, size, curPoolPtr-dyldPool); + return result; } - //fprintf(stderr, "new(%d)\n", len); - return malloc_zone_malloc(sZone, len); } -void* operator new[](std::size_t len) throw (std::bad_alloc) + +void free(void* ptr) { - if ( sZone == NULL ) { - sZone = malloc_create_zone(40960, 0); - malloc_set_zone_name(sZone, "dyld heap"); + // 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])) ) { + //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); } - //fprintf(stderr, "new[](%d)\n", len); - return malloc_zone_malloc(sZone, len); } -void operator delete(void* obj) throw() +void* calloc(size_t count, size_t size) { - //fprintf(stderr, "delete(%p)\n", obj); - malloc_zone_free(sZone, obj); + if ( dyld::gLibSystemHelpers != NULL ) { + void* result = dyld::gLibSystemHelpers->malloc(size); + bzero(result, size); + return result; + } + else { + return malloc(count*size); + } } -void operator delete[](void* obj) throw() +void* realloc(void *ptr, size_t size) { - //fprintf(stderr, "delete[](%p)\n", obj); - malloc_zone_free(sZone, obj); + void* result = malloc(size); + memcpy(result, ptr, size); + return result; } +// void* reallocf(void *ptr, size_t size); +// void* valloc(size_t size); + +// needed __libc_init() +extern "C" int _malloc_lock; +int _malloc_lock = 0; + diff --git a/src/dyldStartup.s b/src/dyldStartup.s index 2afa8d4..1bf7931 100644 --- a/src/dyldStartup.s +++ b/src/dyldStartup.s @@ -113,7 +113,7 @@ L__dyld_start_picbase: addl $8,%esp # remove the mh argument, and debugger end # frame marker movl $0,%ebp # restore ebp back to zero - jmp %eax # jump to the entry point + jmp *%eax # jump to the entry point .globl dyld_stub_binding_helper @@ -164,6 +164,7 @@ __dyld_start: addq $16,%rsp # remove the mh argument, and debugger end frame marker movq $0,%rbp # restore ebp back to zero jmp *%rax # jump to the entry point + #endif /* __x86_64__ */ @@ -234,3 +235,24 @@ L_end: #endif /* __ppc__ */ +/* + * dyld calls this function to terminate a process. + * It has a label so that CrashReporter can distinguish this + * termination from a random crash. rdar://problem/4764143 + */ + .text + .align 2 + .globl _dyld_fatal_error +_dyld_fatal_error: +#if __ppc__ || __ppc64__ + trap +#elif __x86_64__ || __i386__ + int3 +#else + #error unknown architecture +#endif + + + + + diff --git a/src/dyld_debug.c b/src/dyld_debug.c index 56c4cec..632efab 100644 --- a/src/dyld_debug.c +++ b/src/dyld_debug.c @@ -24,7 +24,6 @@ int dummy_dyld_symbol = 1; -#include #include // The following API's are deprecated. @@ -164,9 +163,10 @@ void (*func)(struct dyld_debug_error_data *e)) -// Examine a mach_header in another process and determine its slid +// Examine a mach_header in another process and determine its slide static ptrdiff_t slideForHeader(task_port_t target_task, const struct mach_header* otherAddressHeader) { + ptrdiff_t result = 0; const struct mach_header* mh = xprocess_read(target_task, otherAddressHeader, 0x2000); if ( mh != NULL ) { int i; @@ -176,14 +176,15 @@ static ptrdiff_t slideForHeader(task_port_t target_task, const struct mach_heade for (i = 0; i < mh->ncmds; i++){ if (sgp->cmd == LC_SEGMENT) { if (sgp->fileoff == 0 && sgp->filesize != 0) { - return (uintptr_t)mh - (uintptr_t)sgp->vmaddr; + result = (uintptr_t)mh - (uintptr_t)sgp->vmaddr; + break; } } sgp = (const struct segment_command *)((char *)sgp + sgp->cmdsize); } free((void*)mh); } - return 0; + return result; } diff --git a/src/dyld_gdb.cpp b/src/dyld_gdb.cpp index 89a1cd7..456f048 100644 --- a/src/dyld_gdb.cpp +++ b/src/dyld_gdb.cpp @@ -31,9 +31,11 @@ #include #include "mach-o/dyld_gdb.h" +#include "mach-o/dyld_images.h" +#define OLD_GDB_DYLD_INTERFACE __ppc__ || __i386__ -// old gdb interface to dyld only supported on 32-bit ppc and i386 (not ppc64_ +// old gdb interface to dyld only supported on 32-bit ppc and i386 #if OLD_GDB_DYLD_INTERFACE unsigned int gdb_dyld_version = 2; @@ -80,8 +82,8 @@ 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 }; +object_images object_images;// = { {}, 0 , NULL }; +library_images library_images;// = { {}, 0 , NULL }; void send_event(const struct dyld_event* event); } @@ -170,6 +172,10 @@ 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); + // set infoArray to NULL to denote it is in-use dyld_all_image_infos.infoArray = NULL; @@ -214,14 +220,17 @@ static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, co { // do nothing // gdb sets a break point here to catch notifications - //fprintf(stderr, "dyld: gdb_image_notifier(%s, %d, ...)\n", mode ? "dyld_image_removing" : "dyld_image_adding", infoCount); + //dyld::log("dyld: gdb_image_notifier(%s, %d, ...)\n", mode ? "dyld_image_removing" : "dyld_image_adding", infoCount); + //for (uint32_t i=0; i < infoCount; ++i) + // dyld::log("dyld: %d loading at %p %s\n", i, info[i].imageLoadAddress, info[i].imageFilePath); //for (uint32_t i=0; i < dyld_all_image_infos.infoArrayCount; ++i) - // fprintf(stderr, "dyld: %d loading at %p %s\n", i, dyld_all_image_infos.infoArray[i].imageLoadAddress, dyld_all_image_infos.infoArray[i].imageFilePath); + // dyld::log("dyld: %d loading at %p %s\n", i, dyld_all_image_infos.infoArray[i].imageLoadAddress, dyld_all_image_infos.infoArray[i].imageFilePath); } struct dyld_all_image_infos dyld_all_image_infos = { 1, 0, NULL, &gdb_image_notifier, false }; +struct dyld_shared_cache_ranges dyld_shared_cache_ranges; diff --git a/src/glue.c b/src/glue.c index f44e198..cce0963 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-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,155 +25,65 @@ #include #include #include -#include -#include #include +#include -// -// Stub functions needed for dyld to link with libc.a instead of libSystem.dylib -// -// -static char* dyld_progname = "dyld"; - -// -// libc has calls to _dyld_lookup_and_bind to find args and environment. Since -// dyld links with a staic copy of libc, those calls need to find dyld's args and env -// not the host process. We implement a special _dyld_lookup_and_bind() that -// just knows how to bind the few symbols needed by dyld. -// -void _dyld_lookup_and_bind(const char* symbolName, void** address, void* module) + +// abort called by C++ unwinding code +void abort() { - if ( strcmp(symbolName, "_environ") == 0 ) { - // dummy up empty environment list - static char *p = NULL; - static char **pp = &p; - *address = &pp; - return; - } - else if ( strcmp(symbolName, "___progname") == 0 ) { - *address = &dyld_progname; - return; - } - - fprintf(stderr, "dyld: internal error: unknown symbol '%s'\n", symbolName); + _exit(1); } - -int NSIsSymbolNameDefined(const char* symbolName) +// std::terminate called by C++ unwinding code +void _ZSt9terminatev() { - if ( strcmp(symbolName, "___progname") == 0 ) { - return 1; - } - fprintf(stderr, "dyld: internal error: unknown symbol '%s'\n", symbolName); - return 0; + _exit(1); } - -/* - * To avoid linking in libm. These variables are defined as they are used in - * pthread_init() to put in place a fast sqrt(). - */ -size_t hw_sqrt_len = 0; - -double -sqrt(double x) +// std::unexpected called by C++ unwinding code +void _ZSt10unexpectedv() { - return(0.0); -} -double -hw_sqrt(double x) -{ - return(0.0); + _exit(1); } -/* - * More stubs to avoid linking in libm. This works as along as we don't use - * long doubles. - */ -long -__fpclassifyd(double x) +// __cxxabiv1::__terminate(void (*)()) called to terminate process +void _ZN10__cxxabiv111__terminateEPFvvE() { - return(0); + _exit(1); } -long -__fpclassify(long double x) +// __cxxabiv1::__unexpected(void (*)()) called to terminate process +void _ZN10__cxxabiv112__unexpectedEPFvvE() { - return(0); + _exit(1); } +// __cxxabiv1::__terminate_handler +void* _ZN10__cxxabiv119__terminate_handlerE = &_ZSt9terminatev; -char* __hdtoa(double d, const char *xdigs, int ndigits, int *decpt, int *sign, char **rve) -{ - return NULL; -} +// __cxxabiv1::__unexpected_handler +void* _ZN10__cxxabiv120__unexpected_handlerE = &_ZSt10unexpectedv; -char* __hldtoa(/*long*/ double e, const char *xdigs, int ndigits, int *decpt, int *sign, char **rve) -{ - return NULL; -} -int __fegetfltrounds(void) -{ - return 1; /* FE_NEAREST */ -} -int fegetround(void) + +// real cthread_set_errno_self() has error handling that pulls in +// pthread_exit() which pulls in fprintf() +extern int* __error(void); +void cthread_set_errno_self(int err) { - return 1; + int* ep = __error(); + *ep = err; } /* * We have our own localtime() to avoid needing the notify API which is used - * by the code in libc.a for localtime() but is in libnotify. + * by the code in libc.a for localtime() which is used by arc4random(). */ struct tm* localtime(const time_t* t) { return (struct tm*)NULL; } -struct tm* localtime_r(const time_t* t, struct tm *result) -{ - return result; -} -time_t mktime(struct tm *timeptr) -{ - return 0; -} - - - - -/* - * On ppc64, the C++ runtime references strftime & wcsftime, but they - * never actually get called, and they try to use all the localtime() - * machinery, so stub them out. - */ - -size_t strftime(char * __restrict p1, size_t p2, const char * __restrict p3, - const struct tm * __restrict p4) -{ - return 0; -} -size_t wcsftime(wchar_t * __restrict p1, size_t p2, - const wchar_t * __restrict p3, - const struct tm * __restrict p4) -{ - return 0; -} - -int __strtopdd (const char* p1, char** p2, double* p3) -{ - return 0; -} - -char* __ldtoa(long double *ld, int mode, int ndigits, int *decpt, int *sign, char **rve) -{ - return "__ldtoa"; -} - -int __hexnan_D2A(const char **sp, void *fpi, unsigned long *x0) -{ - return 0; -} diff --git a/src/strip.exp b/src/strip.exp new file mode 100644 index 0000000..8a2f778 --- /dev/null +++ b/src/strip.exp @@ -0,0 +1,4 @@ + +# local symbols to suppress +*PE* +*Win* diff --git a/src/stub_binding_helper.s b/src/stub_binding_helper.s index 4b13088..331a5cd 100644 --- a/src/stub_binding_helper.s +++ b/src/stub_binding_helper.s @@ -26,9 +26,8 @@ #ifdef __i386__ /* * This is the interface for the stub_binding_helper for i386: - * 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. + * The caller has pushed the address of the a lazy pointer to be filled in + * 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 @@ -36,24 +35,22 @@ * Some inter-image function calls pass parameters in registers EAX, ECX, EDX, or XXM0-3, * Therefore those registers need to be preserved during the lazy binding. * - * 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. + * After the symbol has been resolved and the lazy pointer filled in, this jumps + * to the target address. */ -#define MH_PARAM_BP 4 -#define LP_PARAM_BP 8 -#define RESULT_BP 8 /* in order to trash no registers, the target is stored back on the stack then ret it done to it */ - -#define MH_PARAM_OUT 0 -#define LP_PARAM_OUT 4 -#define EAX_SAVE 8 -#define ECX_SAVE 12 -#define EDX_SAVE 16 -#define XMMM0_SAVE 32 /* 16-byte align */ -#define XMMM1_SAVE 48 -#define XMMM2_SAVE 64 -#define XMMM3_SAVE 80 -#define STACK_SIZE 96 /* (XMMM3_SAVE+16) must be 16 byte aligned too */ - +#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 .text .align 4,0x90 @@ -61,38 +58,44 @@ _fast_stub_binding_helper_interface: pushl $0 .globl _stub_binding_helper_interface + .globl _misaligned_stack_error _stub_binding_helper_interface: - pushl %ebp - movl %esp,%ebp - subl $STACK_SIZE,%esp # at this point stack is 16-byte aligned because two meta-parameters where pushed - movl %eax,EAX_SAVE(%esp) # save registers that might be used as parameters + 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) - movl MH_PARAM_BP(%ebp),%eax # call dyld::bindLazySymbol(mh, lazy_ptr) +_stub_binding_helper_interface2: + movl MH_LOCAL(%esp),%eax # call dyld::bindLazySymbol(mh, lazy_ptr) movl %eax,MH_PARAM_OUT(%esp) - movl LP_PARAM_BP(%ebp),%eax + movl LP_LOCAL(%esp),%eax movl %eax,LP_PARAM_OUT(%esp) call __ZN4dyld14bindLazySymbolEPK11mach_headerPm - movl %eax,RESULT_BP(%ebp) # store target for ret movdqa XMMM0_SAVE(%esp),%xmm0 # restore registers movdqa XMMM1_SAVE(%esp),%xmm1 movdqa XMMM2_SAVE(%esp),%xmm2 movdqa XMMM3_SAVE(%esp),%xmm3 - movl EAX_SAVE(%esp),%eax movl ECX_SAVE(%esp),%ecx movl EDX_SAVE(%esp),%edx - addl $STACK_SIZE,%esp - popl %ebp - addl $4,%esp # remove meta-parameter, other meta-parmaeter now holds target for ret - ret + movl %eax,%ebp # move target address to epb + movl EAX_SAVE(%esp),%eax # restore eaz + 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__ /* * This is the interface for the stub_binding_helper for x86_64: @@ -100,7 +103,7 @@ _stub_binding_helper_interface: * 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+8 address of lazy pointer * sp+0 address of mach header * * All parameters registers must be preserved. @@ -176,6 +179,7 @@ _stub_binding_helper_interface: #endif + #if __ppc__ || __ppc64__ #include /* diff --git a/unit-tests/bin/exit-non-zero-pass.pl b/unit-tests/bin/exit-non-zero-pass.pl index 149724f..f29a1bc 100755 --- a/unit-tests/bin/exit-non-zero-pass.pl +++ b/unit-tests/bin/exit-non-zero-pass.pl @@ -21,6 +21,9 @@ sub FAIL my $pass_string = shift @ARGV; my $fail_string = shift @ARGV; + +# redirect stderr to stdout +open(STDERR, ">/tmp/exit-non-zero.tmp") || die("$!"); if(0 != system(@ARGV)) { PASS($pass_string); @@ -29,5 +32,12 @@ else { FAIL($fail_string); } +close(STDERR) || die("$!"); +open(OUT, ") +{ + print $_; +} +close(OUT) || die("$!"); exit 0; diff --git a/unit-tests/bin/exit-zero-pass.pl b/unit-tests/bin/exit-zero-pass.pl index 54f4b32..43de2a3 100755 --- a/unit-tests/bin/exit-zero-pass.pl +++ b/unit-tests/bin/exit-zero-pass.pl @@ -21,6 +21,8 @@ sub FAIL my $pass_string = shift @ARGV; my $fail_string = shift @ARGV; +# redirect stderr to stdout +open(STDERR, ">/tmp/exit-zero-pass.tmp") || die("$!"); if(0 == system(@ARGV)) { PASS($pass_string); @@ -29,5 +31,12 @@ else { FAIL($fail_string); } +close(STDERR) || die("$!"); +open(OUT, ") +{ + print $_; +} +close(OUT) || die("$!"); exit 0; diff --git a/unit-tests/bin/result-filter.pl b/unit-tests/bin/result-filter.pl index db07357..7d6007f 100755 --- a/unit-tests/bin/result-filter.pl +++ b/unit-tests/bin/result-filter.pl @@ -44,7 +44,6 @@ if(length($entry)) # show totals my $percentage = $pass_count * 100 / $total_count; -print "\n"; printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage; @@ -98,17 +97,29 @@ sub process_entry } my $seen_result = 0; + #if there was any output to stderr, mark this as a failure + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + return; + } + # scan all stdout looking for lines that start with PASS or FAIL foreach $line (@{$$tbl{stdout}}) { if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) { - printf "%-40s %s\n", $test_name, $line; $total_count++; if($line =~ m/^PASS.+/) { $pass_count++; } + else + { + # only print failure lines + printf "%-40s %s\n", $test_name, $line; + } $seen_result = 1; } } diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 67c89c3..243fc2d 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -2,11 +2,17 @@ SHELL = /bin/sh -CC = gcc-4.0 ${ARCH} -CCFLAGS = -Wall -g -std=c99 +# set default to be host +ARCH ?= $(shell arch) -CXX = g++-4.0 ${ARCH} -CXXFLAGS = -Wall -g +# set default to be all +VALID_ARCHS ?= "ppc ppc64 i386 x86_64" + +CC = gcc-4.0 -arch ${ARCH} +CCFLAGS = -Wall -std=c99 + +CXX = g++-4.0 -arch ${ARCH} +CXXFLAGS = -Wall RM = rm RMFLAGS = -rf diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index ef857d1..0cfeb45 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -3,6 +3,10 @@ # cd into test-cases directory cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` +# save crash reporter state +CRSTATE=`defaults read com.apple.CrashReporter DialogType` +defaults write com.apple.CrashReporter DialogType basic + echo "" echo " * * * Running all unit tests for 32-bits * * *" @@ -22,11 +26,11 @@ then ../bin/make-recursive.pl clean > /dev/null # build 64-bit architecture - ../bin/make-recursive.pl ARCH="-arch ppc64" | ../bin/result-filter.pl + ../bin/make-recursive.pl ARCH="ppc64" | ../bin/result-filter.pl fi # if Intel, then also run all test cases under emulation -if [ `sysctl -n hw.machine` = "i386" ] +if [ "`sysctl -n hw.machine`" = "i386" ] then echo "" echo " * * * Running all unit tests for emulated 32-bits * * *" @@ -35,18 +39,24 @@ then ../bin/make-recursive.pl clean > /dev/null # build ppc architecture - ../bin/make-recursive.pl ARCH="-arch ppc" | ../bin/result-filter.pl -fi + ../bin/make-recursive.pl ARCH="ppc" | ../bin/result-filter.pl -# if 64-bit capable Intel, then also run all test cases for 64-bits -if [ `sysctl -n hw.optional.x86_64` = "1" ] -then - echo "" - echo " * * * Running all unit tests for 64-bits * * *" - - # make clean - ../bin/make-recursive.pl clean > /dev/null - # build x86_64 architecture - ../bin/make-recursive.pl ARCH="-arch x86_64" | ../bin/result-filter.pl + # if 64-bit capable Intel, then also run all test cases for 64-bits + if [ `sysctl -n hw.optional.x86_64` = "1" ] + then + echo "" + echo " * * * Running all unit tests for 64-bits * * *" + + # make clean + ../bin/make-recursive.pl clean > /dev/null + + # build x86_64 architecture + ../bin/make-recursive.pl ARCH="x86_64" | ../bin/result-filter.pl + fi fi + +# restore crash reporter state +defaults write com.apple.CrashReporter DialogType ${CRSTATE} + + diff --git a/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile index 3aeab86..49c689a 100644 --- a/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile +++ b/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile @@ -29,13 +29,13 @@ run: all all: main main : main.c libbar.dylib libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main libfoo.dylib : foo.c libbar.dylib ${CC} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib libbar.dylib libbar.dylib : bar.c - ${CC} -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib -install_name /usr/local/hide/libbar.dylib + ${CC} -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib -install_name /usr/local/hide/libbar.dylib 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 e02d140..7d4b9f1 100644 --- a/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile +++ b/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile @@ -29,7 +29,7 @@ run: all all: main main : main.c - ${CC} -I${TESTROOT}/include main.c -o main + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main clean: ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/NSAddImage-leafname/Makefile b/unit-tests/test-cases/NSAddImage-leafname/Makefile index 38794a0..f46b18e 100644 --- a/unit-tests/test-cases/NSAddImage-leafname/Makefile +++ b/unit-tests/test-cases/NSAddImage-leafname/Makefile @@ -29,7 +29,7 @@ run: all all: main hide/libzzz.dylib main : main.c - ${CC} -I${TESTROOT}/include main.c -o main + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main hide/libzzz.dylib: zzz.c mkdir -p hide diff --git a/unit-tests/test-cases/big-stack/Makefile b/unit-tests/test-cases/big-stack/Makefile new file mode 100644 index 0000000..cc85b85 --- /dev/null +++ b/unit-tests/test-cases/big-stack/Makefile @@ -0,0 +1,33 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ${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 + +clean: + ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/big-stack/main.c b/unit-tests/test-cases/big-stack/main.c new file mode 100644 index 0000000..dc1339a --- /dev/null +++ b/unit-tests/test-cases/big-stack/main.c @@ -0,0 +1,76 @@ +/* + * 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 // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +// +// 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 +void foo(unsigned long long stackSize, char* stackStart) +{ + char buffer[32*1024*1024]; + keepAlive = buffer; + if ( (stackStart - buffer) > stackSize ) + return; + else + foo(stackSize, stackStart); +} + +#if __ppc__ +static bool isRosetta() +{ + int mib[] = { CTL_KERN, KERN_CLASSIC, getpid() }; + int is_classic = 0; + size_t len = sizeof(int); + int ret = sysctl(mib, 3, &is_classic, &len, NULL, 0); + if ((ret != -1) && is_classic) { + // we're running under Rosetta + return true; + } + return false; +} +#endif + +int +main() +{ + char start; +#if __ppc__ + // programs running under rosetta cannot use large amounts of stack + if ( isRosetta() ) + foo(0x02000000, &start); + else +#endif + foo(0x81000000, &start); // 2.1 GB stack + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/bundle-basic/Makefile b/unit-tests/test-cases/bundle-basic/Makefile index 2c905f2..d2c407c 100644 --- a/unit-tests/test-cases/bundle-basic/Makefile +++ b/unit-tests/test-cases/bundle-basic/Makefile @@ -29,7 +29,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o 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 diff --git a/unit-tests/test-cases/bundle-dont-gc/Makefile b/unit-tests/test-cases/bundle-dont-gc/Makefile new file mode 100644 index 0000000..6728b7a --- /dev/null +++ b/unit-tests/test-cases/bundle-dont-gc/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + +main: main.c foo.bundle bar.bundle + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c + +bar.bundle : bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle bar.bundle + diff --git a/unit-tests/test-cases/bundle-dont-gc/bar.c b/unit-tests/test-cases/bundle-dont-gc/bar.c new file mode 100644 index 0000000..3aee7ab --- /dev/null +++ b/unit-tests/test-cases/bundle-dont-gc/bar.c @@ -0,0 +1,3 @@ + +void bar() {} + diff --git a/unit-tests/test-cases/bundle-dont-gc/foo.c b/unit-tests/test-cases/bundle-dont-gc/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/bundle-dont-gc/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/bundle-dont-gc/main.c b/unit-tests/test-cases/bundle-dont-gc/main.c new file mode 100644 index 0000000..69e2f2c --- /dev/null +++ b/unit-tests/test-cases/bundle-dont-gc/main.c @@ -0,0 +1,91 @@ +/* + * 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 // 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[]) +{ + // load foo.bundle with old API + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("foo.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("bundle-dont-gc: NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "foo.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("bundle-dont-gc: NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_foo"); + if ( sym == NULL ) { + FAIL("bundle-dont-gc: NSLookupSymbolInModule failed"); + return 1; + } + + void* fooAddr = NSAddressOfSymbol(sym); + if ( fooAddr == NULL ) { + FAIL("bundle-dont-gc: NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("bundle-dont-gc: NSDestroyObjectFileImage failed"); + return 1; + } + + // open and close bar.bundle with new API, which causes a gc of images + void* h1 = dlopen("bar.bundle", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("bundle-dont-gc: can't dlopen bar.bundle: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h1); + + // make sure foo is still loaded + Dl_info info; + if ( dladdr(fooAddr, &info) == 0 ) { + FAIL("bundle-dont-gc: dladdr() failed"); + exit(0); + } + + // unload foo.bundle with old API + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + + PASS("bundle-dont-gc"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/bundle-memory-load-bad/Makefile b/unit-tests/test-cases/bundle-memory-load-bad/Makefile new file mode 100644 index 0000000..4b3865d --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-bad/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## + + +# +# The test case verifies that dyld can cleanly recover from +# a main executable (test.bundle) being used with +# NSCreateObjectFileImageFromMemory() +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-memory-load-bad/bundle.c b/unit-tests/test-cases/bundle-memory-load-bad/bundle.c new file mode 100644 index 0000000..7556138 --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-bad/bundle.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int main() +{ + return 0; +} diff --git a/unit-tests/test-cases/bundle-memory-load-bad/main.c b/unit-tests/test-cases/bundle-memory-load-bad/main.c new file mode 100644 index 0000000..fe100da --- /dev/null +++ b/unit-tests/test-cases/bundle-memory-load-bad/main.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +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 = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 1; + } + + close(fd); + + // we are using a file not of type MH_BUNDLE, so NSCreateObjectFileImageFromMemory should fail + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) + PASS("bundle-memory-load-bad"); + else + FAIL("bundle-memory-load-bad"); + + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-memory-load-fat/Makefile b/unit-tests/test-cases/bundle-memory-load-fat/Makefile index 01eab8b..7c57314 100644 --- a/unit-tests/test-cases/bundle-memory-load-fat/Makefile +++ b/unit-tests/test-cases/bundle-memory-load-fat/Makefile @@ -31,10 +31,10 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c test.bundle : bundle.c - ${CC} ${FATFLAGS} -bundle -o test.bundle bundle.c + gcc ${FATFLAGS} -bundle -o test.bundle bundle.c clean: ${RM} ${RMFLAGS} *~ main test.bundle diff --git a/unit-tests/test-cases/bundle-memory-load/Makefile b/unit-tests/test-cases/bundle-memory-load/Makefile index 2c905f2..a44aeca 100644 --- a/unit-tests/test-cases/bundle-memory-load/Makefile +++ b/unit-tests/test-cases/bundle-memory-load/Makefile @@ -29,7 +29,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o 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 diff --git a/unit-tests/test-cases/bundle-multi-link/Makefile b/unit-tests/test-cases/bundle-multi-link/Makefile index ce5b2d4..c044f7f 100644 --- a/unit-tests/test-cases/bundle-multi-link/Makefile +++ b/unit-tests/test-cases/bundle-multi-link/Makefile @@ -29,7 +29,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o 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 diff --git a/unit-tests/test-cases/bundle-multi-link/main.c b/unit-tests/test-cases/bundle-multi-link/main.c index 267d540..10d9fce 100644 --- a/unit-tests/test-cases/bundle-multi-link/main.c +++ b/unit-tests/test-cases/bundle-multi-link/main.c @@ -62,7 +62,9 @@ int main() NSModule mod2 = NSLinkModule(ofi, "test2.bundle", NSLINKMODULE_OPTION_NONE); if ( mod2 == NULL ) { - FAIL("2nd NSLookupSymbolInModule failed"); + NSLinkEditErrors c; int errorNumber; const char* fileName; const char* errorString; + NSLinkEditError(&c, &errorNumber, &fileName, &errorString); + FAIL("2nd NSLinkModule failed: %s", errorString); return 0; } if ( mod == mod2 ) { @@ -150,9 +152,9 @@ int main() } // check that this is really a new copy by verifying the getValue() returns zero - NSSymbol sym4getter = NSLookupSymbolInModule(mod2, "_getValue"); + NSSymbol sym4getter = NSLookupSymbolInModule(mod4, "_getValue"); if ( sym4getter == NULL ) { - FAIL("2nd NSLookupSymbolInModule failed"); + FAIL("4th NSLookupSymbolInModule failed"); return 0; } getter func4getter = NSAddressOfSymbol(sym4getter); diff --git a/unit-tests/test-cases/bundle-multi-load/Makefile b/unit-tests/test-cases/bundle-multi-load/Makefile index ce5b2d4..c044f7f 100644 --- a/unit-tests/test-cases/bundle-multi-load/Makefile +++ b/unit-tests/test-cases/bundle-multi-load/Makefile @@ -29,7 +29,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o 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 diff --git a/unit-tests/test-cases/bundle-multi-load/main.c b/unit-tests/test-cases/bundle-multi-load/main.c index 5d4d052..b9eb8b3 100644 --- a/unit-tests/test-cases/bundle-multi-load/main.c +++ b/unit-tests/test-cases/bundle-multi-load/main.c @@ -51,7 +51,7 @@ int main() } void* func = NSAddressOfSymbol(sym); - fprintf(stderr, "1st address of foo() = %p in module %p in OFI %p\n", func, mod, ofi); + //fprintf(stderr, "1st address of foo() = %p in module %p in OFI %p\n", func, mod, ofi); NSObjectFileImage ofi2; @@ -77,7 +77,7 @@ int main() } void* func2 = NSAddressOfSymbol(sym2); - fprintf(stderr, "2nd address of foo() = %p in module %p in OFI %p\n", func2, mod2, ofi2); + //fprintf(stderr, "2nd address of foo() = %p in module %p in OFI %p\n", func2, mod2, ofi2); if ( func == func2 ) { FAIL("2nd NSAddressOfSymbol return same function address as 1st\n"); return 0; @@ -109,7 +109,7 @@ int main() return 0; } void* func3 = NSAddressOfSymbol(sym3); - fprintf(stderr, "3rd address of foo() = %p in module %p in OFI %p\n", func3, mod3, ofi3); + //fprintf(stderr, "3rd address of foo() = %p in module %p in OFI %p\n", func3, mod3, ofi3); if ( func3 == func ) { FAIL("3rd NSAddressOfSymbol return same function address as 1st\n"); return 0; diff --git a/unit-tests/test-cases/bundle-name-ownership/Makefile b/unit-tests/test-cases/bundle-name-ownership/Makefile new file mode 100644 index 0000000..f2d2d0a --- /dev/null +++ b/unit-tests/test-cases/bundle-name-ownership/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-name-ownership/bundle.c b/unit-tests/test-cases/bundle-name-ownership/bundle.c new file mode 100644 index 0000000..64232df --- /dev/null +++ b/unit-tests/test-cases/bundle-name-ownership/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-name-ownership/main.c b/unit-tests/test-cases/bundle-name-ownership/main.c new file mode 100644 index 0000000..4244489 --- /dev/null +++ b/unit-tests/test-cases/bundle-name-ownership/main.c @@ -0,0 +1,83 @@ +/* + * 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 "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ + const char* path = "test.bundle"; + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 0; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 0; + } + + Dl_info info; + if ( dladdr(func, &info) == 0 ) { + FAIL("dladdr(func, &info) failed"); + return 0; + } + + if ( info.dli_fname == path ) { + FAIL("NSLinkModule() did not make a copy of the path"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 0; + } + + PASS("bundle-name-ownership"); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-private/Makefile b/unit-tests/test-cases/bundle-private/Makefile index 2c905f2..29d5d41 100644 --- a/unit-tests/test-cases/bundle-private/Makefile +++ b/unit-tests/test-cases/bundle-private/Makefile @@ -29,7 +29,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o 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 diff --git a/unit-tests/test-cases/bundle-reload/Makefile b/unit-tests/test-cases/bundle-reload/Makefile index 194c744..97eb0d5 100644 --- a/unit-tests/test-cases/bundle-reload/Makefile +++ b/unit-tests/test-cases/bundle-reload/Makefile @@ -23,7 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -ifeq "-arch ppc" "$(ARCH)" +ifeq "ppc" "$(ARCH)" CXX_VERSION = g++-3.3 else CXX_VERSION = ${CXX} @@ -35,7 +35,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c test.bundle : bundle.cxx ${CXX} ${CXXFLAGS} -bundle -o test.bundle bundle.cxx diff --git a/unit-tests/test-cases/bundle-reload/bundle.cxx b/unit-tests/test-cases/bundle-reload/bundle.cxx index ec27354..a8651c4 100644 --- a/unit-tests/test-cases/bundle-reload/bundle.cxx +++ b/unit-tests/test-cases/bundle-reload/bundle.cxx @@ -27,3 +27,4 @@ void foo() { } +int __attribute__((weak)) blah = 10; diff --git a/unit-tests/test-cases/bundle-unlinkable/Makefile b/unit-tests/test-cases/bundle-unlinkable/Makefile index e7f9fd2..bb9c30d 100644 --- a/unit-tests/test-cases/bundle-unlinkable/Makefile +++ b/unit-tests/test-cases/bundle-unlinkable/Makefile @@ -29,7 +29,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c test.bundle : bundle.c libstuff.dylib ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c libstuff.dylib diff --git a/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile b/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile index 2c905f2..f2d2d0a 100644 --- a/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile +++ b/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile @@ -29,7 +29,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o 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 diff --git a/unit-tests/test-cases/bundle-v-dylib/Makefile b/unit-tests/test-cases/bundle-v-dylib/Makefile index e2df5ed..952c3a5 100644 --- a/unit-tests/test-cases/bundle-v-dylib/Makefile +++ b/unit-tests/test-cases/bundle-v-dylib/Makefile @@ -28,8 +28,8 @@ run: all all: main -main : main.c bar.dylib foo.bundle foo.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c bar.dylib +main : main.c bar.dylib foo.bundle foo.dylib foo2.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c bar.dylib foo.bundle : foo.c ${CC} ${CCFLAGS} -bundle -o foo.bundle foo.c @@ -37,9 +37,12 @@ foo.bundle : foo.c foo.dylib : foo.c ${CC} ${CCFLAGS} -dynamiclib -o foo.dylib foo.c +foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -o foo2.dylib foo.c + bar.dylib : bar.c ${CC} ${CCFLAGS} -dynamiclib -o bar.dylib bar.c clean: - ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib bar.dylib + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib foo2.dylib bar.dylib diff --git a/unit-tests/test-cases/bundle-v-dylib/main.c b/unit-tests/test-cases/bundle-v-dylib/main.c index 4f0e3a9..8caba38 100644 --- a/unit-tests/test-cases/bundle-v-dylib/main.c +++ b/unit-tests/test-cases/bundle-v-dylib/main.c @@ -24,32 +24,67 @@ #include #include #include +#include +#include +#include +#include +#include #include "test.h" // PASS(), FAIL() +void loadAsBundleFromMemory(const char* path) +{ + int fd = open(path, O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("bundle-v-dylib: open() failed"); + exit(0); + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("bundle-v-dylib: fstat() failed"); + exit(0); + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("bundle-v-dylib: mmap() failed"); + exit(0); + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) == NSObjectFileImageSuccess ) { + FAIL("bundle-v-dylib: NSCreateObjectFileImageFromMemory() incorrectly allowed %s to be loaded", path); + exit(0); + } +} + void loadAsBundle(const char* path) { NSObjectFileImage ofi; if ( NSCreateObjectFileImageFromFile(path, &ofi) == NSObjectFileImageSuccess ) { - FAIL("NSCreateObjectFileImageFromFile() incorrectly allowed %s to be loaded", path); - exit(1); + FAIL("bundle-v-dylib: NSCreateObjectFileImageFromFile() incorrectly allowed %s to be loaded", path); + exit(0); } } void loadAsDylib(const char* path) { if ( NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR) != NULL ) { - FAIL("NSAddImage() incorrectly allowed %s to be loaded", path); - exit(1); + FAIL("bundle-v-dylib: NSAddImage() incorrectly allowed %s to be loaded", path); + exit(0); } } - extern void bar(); int main() { + int dummy; + // verify that NSAddImage fails to load MH_BUNDLE loadAsDylib("foo.bundle"); @@ -58,9 +93,16 @@ int main() // verify that NSCreateObjectFileImageFromFile fails to load MH_DYLIB already linked against main loadAsBundle("bar.dylib"); + // verify that bar.dylib was not unloaded when above failed bar(); + // try loading a dylib from memory using bundle API's + loadAsBundleFromMemory("foo2.dylib"); + + // verify that dyld data structures are not wanked by scanning all images + _dyld_get_image_header_containing_address(&dummy); + PASS("bundle-v-dylib"); return 0; } \ No newline at end of file diff --git a/unit-tests/test-cases/bundle-weak/Makefile b/unit-tests/test-cases/bundle-weak/Makefile new file mode 100644 index 0000000..3cf265b --- /dev/null +++ b/unit-tests/test-cases/bundle-weak/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.cxx + ${CXX} ${CCXXFLAGS} -bundle -o test.bundle bundle.cxx + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-weak/bundle.cxx b/unit-tests/test-cases/bundle-weak/bundle.cxx new file mode 100644 index 0000000..fb36871 --- /dev/null +++ b/unit-tests/test-cases/bundle-weak/bundle.cxx @@ -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@ + */ +#include +#include +#include +#include + +bool test() +{ + try { + std::vector().at(1); + } + catch(const std::out_of_range&) { + //fprintf(stderr, "caught out_of_range\n"); + return true; + } + catch(...) { + //fprintf(stderr, "caught something\n"); + } + return false; +} diff --git a/unit-tests/test-cases/bundle-weak/main.c b/unit-tests/test-cases/bundle-weak/main.c new file mode 100644 index 0000000..2de9a58 --- /dev/null +++ b/unit-tests/test-cases/bundle-weak/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ + void* handle = dlopen("test.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\test.bundle\") failed"); + return 0; + } + + CheckFunc func = (CheckFunc)dlsym(handle, "_Z4testv"); + if ( func == NULL ) { + FAIL("dlsym(handle, \"__Z4testv\") failed"); + return 0; + } + + if ( func() ) + PASS("bundle-weak"); + else + FAIL("bundle-weak"); + + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/crt-apple/Makefile b/unit-tests/test-cases/crt-apple/Makefile new file mode 100644 index 0000000..0905421 --- /dev/null +++ b/unit-tests/test-cases/crt-apple/Makefile @@ -0,0 +1,57 @@ +## +# 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 that apple[0] parameter is correct by comparing to argv[1] +# + +run: all + ./main-10.4 ./main-10.4 + ./main-10.5 ./main-10.5 + ./main-10.4.stripped ./main-10.4.stripped + ./main-10.5.stripped ./main-10.5.stripped + `pwd`/main-10.4 `pwd`/main-10.4 + `pwd`/main-10.5 `pwd`/main-10.5 + `pwd`/main-10.4.stripped `pwd`/main-10.4.stripped + `pwd`/main-10.5.stripped `pwd`/main-10.5.stripped + +all: main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped + +main-10.4: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4 -w + +main-10.4.stripped: main-10.4 + strip main-10.4 -o main-10.4.stripped + +main-10.5: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5 -w + +main-10.5.stripped: main-10.5 + strip main-10.5 -o main-10.5.stripped + +clean: + ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped + + diff --git a/unit-tests/test-cases/crt-apple/main.c b/unit-tests/test-cases/crt-apple/main.c new file mode 100644 index 0000000..05959bb --- /dev/null +++ b/unit-tests/test-cases/crt-apple/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// verify that apple[0] parameter is correct by comparing to argv[1] +/// + +int +main(int argc, const char* argv[], const char* env[], const char* apple[]) +{ + if ( strcmp(apple[0], argv[1]) == 0 ) + PASS("crt-apple %s", argv[0]); + else + FAIL("crt-apple %s", argv[0]); + + return EXIT_SUCCESS; +} + diff --git a/unit-tests/test-cases/crt-argv-NULL/Makefile b/unit-tests/test-cases/crt-argv-NULL/Makefile new file mode 100644 index 0000000..9df7f9b --- /dev/null +++ b/unit-tests/test-cases/crt-argv-NULL/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that crt glue can handle argv[0] = NULL +# + +run: all + ${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 + + +all: main-10.4 main-10.5 + +main-10.4: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4 + +main-10.5: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5 + +clean: + ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 + + diff --git a/unit-tests/test-cases/crt-argv-NULL/main.c b/unit-tests/test-cases/crt-argv-NULL/main.c new file mode 100644 index 0000000..1adc975 --- /dev/null +++ b/unit-tests/test-cases/crt-argv-NULL/main.c @@ -0,0 +1,14 @@ + + #include + + +int main(int argc, const char* argv[]) +{ + if ( argv[0] != NULL ) { + // re-exec with empty argv[] array + char* const emptyArgv[] = { NULL }; + execv(argv[0], emptyArgv); + } + + return 0; +} diff --git a/unit-tests/test-cases/crt-custom/Makefile b/unit-tests/test-cases/crt-custom/Makefile new file mode 100644 index 0000000..d07ac32 --- /dev/null +++ b/unit-tests/test-cases/crt-custom/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that 10.4 binaries with a custom entry point +# have the entry point called before initializers are run +# + +run: all + ./main-10.4 + ./main-10.5 + + +all: main-10.4 main-10.5 + +main-10.4: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 mystart.s main.c -mmacosx-version-min=10.4 -e _mystart + +main-10.5: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 mystart.s main.c -mmacosx-version-min=10.5 -e _mystart + + +clean: + ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 + + diff --git a/unit-tests/test-cases/crt-custom/main.c b/unit-tests/test-cases/crt-custom/main.c new file mode 100644 index 0000000..0986fd0 --- /dev/null +++ b/unit-tests/test-cases/crt-custom/main.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// the value of flag is altered by mystart +int flag = 0; + + +#if __LP64__ + // for 64-bit binaries initializers are always called before entry point + #define ENTRY_BEFORE_INIT 0 +#else + // for pre 10.5, 32-bit binaries, entry point is called which then calls initializers + #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #define ENTRY_BEFORE_INIT 1 + #else + #define ENTRY_BEFORE_INIT 0 + #endif +#endif + + +void __attribute__((constructor)) myinit() +{ +#if ENTRY_BEFORE_INIT + if ( flag != 2 ) { + FAIL("crt-custom entry point not called before initializer"); + exit(0); + } +#endif + flag = 1; +} + + +int main() +{ +#if ENTRY_BEFORE_INIT + if ( flag != 1 ) { + FAIL("crt-custom initializer not called"); + exit(0); + } +#else + if ( flag != 2 ) { + FAIL("crt-custom entry not called"); + exit(0); + } +#endif + + PASS("crt-custom"); + + return 0; +} diff --git a/unit-tests/test-cases/crt-custom/mystart.s b/unit-tests/test-cases/crt-custom/mystart.s new file mode 100644 index 0000000..b4cb9be --- /dev/null +++ b/unit-tests/test-cases/crt-custom/mystart.s @@ -0,0 +1,20 @@ + + + + .text + .globl _mystart +_mystart: +#if __i386__ + movl $2, _flag + jmp start +#elif __x86_64__ + movl $2, _flag(%rip) + jmp start +#elif __ppc__ || __ppc64__ + li r0,2 + lis r2,ha16(_flag) + stw r0,lo16(_flag)(r2) + b start +#endif + + diff --git a/unit-tests/test-cases/crt-libSystem/Makefile b/unit-tests/test-cases/crt-libSystem/Makefile new file mode 100644 index 0000000..8d8b832 --- /dev/null +++ b/unit-tests/test-cases/crt-libSystem/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +# +# verifies that _NS* routines in libSystem properly find global variables in main executable. +# the mechanism for 10.4 and 10.5 is different +# + +run: all + ./main-10.4 + ./main-10.5 + ./main-10.4.stripped + ./main-10.5.stripped + +all: main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped + +main-10.4: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4 + +main-10.4.stripped: main-10.4 + strip main-10.4 -o main-10.4.stripped + +main-10.5: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5 + +main-10.5.stripped: main-10.5 + strip main-10.5 -o main-10.5.stripped + +clean: + ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped + + diff --git a/unit-tests/test-cases/crt-libSystem/main.c b/unit-tests/test-cases/crt-libSystem/main.c new file mode 100644 index 0000000..b7027df --- /dev/null +++ b/unit-tests/test-cases/crt-libSystem/main.c @@ -0,0 +1,120 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + char*** NXArgvPtr; + char*** environPtr; + char** __prognamePtr; +}; +static const struct ProgramVars* sVars; + +// global variables defeined in crt1.o +extern char** NXArgv; +extern int NXArgc; +extern char** environ; +extern char* __progname; + + +int +main(int argc, const char* argv[]) +{ + bool success = true; + + if ( _NSGetArgv() != &NXArgv ) { + FAIL("crt-libSystem _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); + success = false; + } + + if ( _NSGetArgc() != &NXArgc ) { + FAIL("crt-libSystem _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); + success = false; + } + + if ( _NSGetEnviron() != &environ ) { + FAIL("crt-libSystem _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); + success = false; + } + + if ( _NSGetProgname() != &__progname ) { + FAIL("crt-libSystem _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); + success = false; + } + + if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) { + FAIL("crt-libSystem _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); + success = false; + } + + if ( sVars->NXArgvPtr != &NXArgv ) { + FAIL("crt-libSystem sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); + success = false; + } + + if ( sVars->NXArgcPtr != &NXArgc ) { + FAIL("crt-libSystem sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); + success = false; + } + + if ( sVars->environPtr != &environ ) { + FAIL("crt-libSystem sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); + success = false; + } + + if ( sVars->__prognamePtr != &__progname ) { + FAIL("crt-libSystem sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); + success = false; + } + + if ( sVars->mh != &_mh_execute_header ) { + FAIL("crt-libSystem sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); + success = false; + } + + if ( success ) + PASS("crt-libSystem"); + + return EXIT_SUCCESS; +} + + + +void __attribute__((constructor)) +myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) +{ + sVars = vars; +} + + + + + diff --git a/unit-tests/test-cases/crt-result/Makefile b/unit-tests/test-cases/crt-result/Makefile new file mode 100644 index 0000000..8e7cbf3 --- /dev/null +++ b/unit-tests/test-cases/crt-result/Makefile @@ -0,0 +1,55 @@ +## +# 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 + +# +# verifies that the return value from main() makes it to shell +# for both crt1.0 and crt1.10.5.o +# + +run: all + ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good-10.4" "crt-result good-10.4" ./good-10.4 + ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good-10.5" "crt-result good-10.5" ./good-10.5 + ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad-10.4" "crt-result bad-10.4" ./bad-10.4 + ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad-10.5" "crt-result bad-10.5" ./bad-10.5 + + +all: good-10.4 good-10.5 bad-10.4 bad-10.5 + +good-10.4: good.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good-10.4 good.c -mmacosx-version-min=10.4 + +good-10.5: good.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good-10.5 good.c -mmacosx-version-min=10.5 + +bad-10.4: bad.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad-10.4 bad.c -mmacosx-version-min=10.4 + +bad-10.5: bad.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad-10.5 bad.c -mmacosx-version-min=10.5 + +clean: + ${RM} ${RMFLAGS} *~ good-10.4 good-10.5 bad-10.4 bad-10.5 + + diff --git a/unit-tests/test-cases/crt-result/bad.c b/unit-tests/test-cases/crt-result/bad.c new file mode 100644 index 0000000..40cbb54 --- /dev/null +++ b/unit-tests/test-cases/crt-result/bad.c @@ -0,0 +1 @@ +int main() { return 1; } diff --git a/unit-tests/test-cases/crt-result/good.c b/unit-tests/test-cases/crt-result/good.c new file mode 100644 index 0000000..76e8197 --- /dev/null +++ b/unit-tests/test-cases/crt-result/good.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/unit-tests/test-cases/deadlock/Makefile b/unit-tests/test-cases/deadlock/Makefile index 76d4808..c21045d 100644 --- a/unit-tests/test-cases/deadlock/Makefile +++ b/unit-tests/test-cases/deadlock/Makefile @@ -29,7 +29,7 @@ run: all all: main main : main.c bar.dylib foo.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c foo.dylib foo.dylib : foo.c ${CC} ${CCFLAGS} -dynamiclib -o foo.dylib foo.c diff --git a/unit-tests/test-cases/deadlock/main.c b/unit-tests/test-cases/deadlock/main.c index 07ad467..9727ccb 100644 --- a/unit-tests/test-cases/deadlock/main.c +++ b/unit-tests/test-cases/deadlock/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "test.h" diff --git a/unit-tests/test-cases/dladdr/Makefile b/unit-tests/test-cases/dladdr/Makefile index b0ee016..fcb934d 100644 --- a/unit-tests/test-cases/dladdr/Makefile +++ b/unit-tests/test-cases/dladdr/Makefile @@ -27,7 +27,7 @@ run: all ./main all: - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c clean: ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/dlclose-dylib-unload/Makefile b/unit-tests/test-cases/dlclose-dylib-unload/Makefile new file mode 100644 index 0000000..2597b53 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-unload/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main libfoo.dylib + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbar.dylib -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlclose-dylib-unload/bar.c b/unit-tests/test-cases/dlclose-dylib-unload/bar.c new file mode 100644 index 0000000..817b8cd --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-unload/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int bar() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlclose-dylib-unload/foo.c b/unit-tests/test-cases/dlclose-dylib-unload/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-unload/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlclose-dylib-unload/main.c b/unit-tests/test-cases/dlclose-dylib-unload/main.c new file mode 100644 index 0000000..5e48c00 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-unload/main.c @@ -0,0 +1,162 @@ +/* + * 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() + + + + + +void verifyfoo() +{ + // open same dylib three times + void* handle1 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "foo"); + if ( sym == NULL ) { + FAIL("dlclose-dylib-unload: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // symbol foo should no longer be accessible via dladdr() + Dl_info info; + if ( dladdr(sym, &info) != 0 ) { + FAIL("dlclose-dylib-unload: dladdr(foo_sym) != 0, but should have failed"); + //exit(0); + } + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-dylib-unload: dlclose(foo_handle4) == 0, but should have failed"); + //exit(0); + } + +} + + + +void verifybar() +{ + // open same dylib three times + void* handle1 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "bar"); + if ( sym == NULL ) { + FAIL("dlclose-dylib-unload: dlsym(handle1, \"bar\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // symbol bar should still longer be accessible via dladdr() because of external reference to libbar.dylib + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("dlclose-dylib-unload: dladdr(bar_sym) == 0, but should have succeeded"); + exit(0); + } + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-dylib-unload: dlclose(bar_handle4) == 0, but should have failed"); + exit(0); + } +} + + +// verify libbar.dylib can be loaded and unloaded +// verify libbar.dylib can be loaded, but cannot be unloaded (because main executable links against it) +int main() +{ + verifyfoo(); + verifybar(); + + PASS("dlclose-dylib-unload"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile b/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile new file mode 100644 index 0000000..4f19e3f --- /dev/null +++ b/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# Leopard (9a499): dyld crash with recursive calls to dlclose() + + +run: all + ./main + +all: main libfoo.dylib + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbar.dylib -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c b/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c new file mode 100644 index 0000000..6cd208d --- /dev/null +++ b/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int bar() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c b/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c new file mode 100644 index 0000000..e12dc61 --- /dev/null +++ b/unit-tests/test-cases/dlclose-terminator-dlclose/foo.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 + + +// Leopard (9a499): dyld crash with recursive calls to dlclose() + +static void* handle = NULL; + +static void __attribute__((constructor)) myinit() +{ + handle = dlopen("libbar.dylib", RTLD_LAZY); +} + +static void __attribute__((destructor)) myterm() +{ + // myterm() is called within dlclose. + // now call dlclose() on another (chainded dylib) to test dlclose is re-entrant + if ( handle != NULL ) + dlclose(handle); +} + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlclose-terminator-dlclose/main.c b/unit-tests/test-cases/dlclose-terminator-dlclose/main.c new file mode 100644 index 0000000..90ea779 --- /dev/null +++ b/unit-tests/test-cases/dlclose-terminator-dlclose/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#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("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + + if ( dlclose(handle) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + PASS("dlclose-terminator-dlclose"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlclose-unload-c++/Makefile b/unit-tests/test-cases/dlclose-unload-c++/Makefile new file mode 100644 index 0000000..8cce77e --- /dev/null +++ b/unit-tests/test-cases/dlclose-unload-c++/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main libfoo.dylib libbar.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/dlclose-unload-c++/bar.c b/unit-tests/test-cases/dlclose-unload-c++/bar.c new file mode 100644 index 0000000..c69ec99 --- /dev/null +++ b/unit-tests/test-cases/dlclose-unload-c++/bar.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +static void local() +{ +} + +extern void* common() __attribute__((weak)); + +void* common() +{ + return &local; +} + +void* bar() +{ + return common(); +} diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/foo.c b/unit-tests/test-cases/dlclose-unload-c++/foo.c similarity index 86% rename from unit-tests/test-cases/insert-libraries-with-initializer/foo.c rename to unit-tests/test-cases/dlclose-unload-c++/foo.c index 60a2d84..b6bd1e4 100644 --- a/unit-tests/test-cases/insert-libraries-with-initializer/foo.c +++ b/unit-tests/test-cases/dlclose-unload-c++/foo.c @@ -20,16 +20,20 @@ * * @APPLE_LICENSE_HEADER_END@ */ -#include -#include "test.h" -static -void -__attribute__((constructor)) -my_init() +static void local() { - PASS("initializer/constructor was called"); - exit(EXIT_SUCCESS); } +extern void* common() __attribute__((weak)); + +void* common() +{ + return &local; +} + +void* foo() +{ + return common(); +} diff --git a/unit-tests/test-cases/dlclose-unload-c++/main.c b/unit-tests/test-cases/dlclose-unload-c++/main.c new file mode 100644 index 0000000..cada146 --- /dev/null +++ b/unit-tests/test-cases/dlclose-unload-c++/main.c @@ -0,0 +1,98 @@ +/* + * 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() + +/// +/// This tests that if a C++ symbol (any weak symbol) is bound to an image +/// that is dynamically unloaed, the image is not unloaded until all its clients are +/// + +typedef void* (*proc)(void); + +bool inImage(void* x) +{ + Dl_info info; + return ( dladdr(x, &info) != 0 ); +} + + +int main() +{ + void* handle1 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-unload-c++: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + proc fooProc = (proc)dlsym(handle1, "foo"); + if ( fooProc == NULL ) { + FAIL("dlclose-unload-c++: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + void* handle2 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-unload-c++: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + proc barProc = (proc)dlsym(handle2, "bar"); + if ( barProc == NULL ) { + FAIL("dlclose-unload-c++: dlsym(handle2, \"bar\") failed"); + exit(0); + } + + // verify that uniquing is happening + void* fooResult = (*fooProc)(); + void* barResult = (*barProc)(); + if ( fooResult != barResult ) { + FAIL("dlclose-unload-c++: foo() and bar() returned different values"); + exit(0); + } + + // close libfoo, even though libbar is using libfoo + dlclose(handle1); + + // error if libfoo was unloaded + if ( !inImage(fooProc) ) { + FAIL("dlclose-unload-c++: libfoo should not have been unloaded"); + exit(0); + } + + // close libbar which should release libfoo + dlclose(handle2); + + // error if libfoo was not unloaded + if ( inImage(fooProc) ) { + FAIL("dlclose-unload-c++: libfoo should have been unloaded"); + exit(0); + } + + PASS("dlclose-unload-c++"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlerror-clear/Makefile b/unit-tests/test-cases/dlerror-clear/Makefile new file mode 100644 index 0000000..7337131 --- /dev/null +++ b/unit-tests/test-cases/dlerror-clear/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dlerror-clear/main.c b/unit-tests/test-cases/dlerror-clear/main.c new file mode 100644 index 0000000..f70cc86 --- /dev/null +++ b/unit-tests/test-cases/dlerror-clear/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that the dlerror message is cleared when dlerror is called +/// + +int main() +{ + // try to non-existent library + void* handle1 = dlopen("frobulite", RTLD_LAZY); + if ( handle1 != NULL ) { + FAIL("dlerror-clear: dlopen(\"frobulite\", RTLD_LAZY) succeeded but should have failed"); + exit(0); + } + + // verify there is an error message + const char* msg = dlerror(); + if ( msg == NULL ) { + FAIL("dlerror-clear: dlerror() returned NULL but should have returned an error message"); + exit(0); + } + + // verify error message was cleared + const char* msg2 = dlerror(); + if ( msg2 != NULL ) { + FAIL("dlerror-clear: dlerror() returned message but should have returned NULL"); + exit(0); + } + + PASS("dlerror-clear"); + return 0; +} diff --git a/unit-tests/test-cases/dlerror/Makefile b/unit-tests/test-cases/dlerror/Makefile new file mode 100644 index 0000000..7337131 --- /dev/null +++ b/unit-tests/test-cases/dlerror/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/dlerror/main.c b/unit-tests/test-cases/dlerror/main.c new file mode 100644 index 0000000..71ed8ab --- /dev/null +++ b/unit-tests/test-cases/dlerror/main.c @@ -0,0 +1,93 @@ +/* + * 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() + + +/// +/// This tests that the dlerror message is kept per thread +/// + +static void* work(void* arg) +{ + const char* str = (char*)arg; + for(int i=0; i < 1000; ++i) { + //fprintf(stderr, "dlopen(%s)\n", str); + void* handle = dlopen(str, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen(%s) unexpectedly succeeded", str); + exit(0); + } + char* msg = dlerror(); + //fprintf(stderr, "dlopen(%s) => %s\n", str, msg); + if ( (msg == NULL) || (strstr(msg, str) == NULL) ) { + FAIL("dlerror() did not contain library name that could not be loaded", str); + exit(0); + } + + + } + return 0; +} + + + +int main() +{ + dlsym(RTLD_DEFAULT, "foobar"); + //fprintf(stderr, "%s\n", dlerror()); + + 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); + + PASS("dlerror-thread-test"); + return 0; +} diff --git a/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile new file mode 100644 index 0000000..aee8633 --- /dev/null +++ b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + +run: all + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + + + diff --git a/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c new file mode 100644 index 0000000..fea884b --- /dev/null +++ b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c new file mode 100644 index 0000000..b86c5e5 --- /dev/null +++ b/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c @@ -0,0 +1,106 @@ +/* + * 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() + + +/// +/// When dlopen(NULL, RTLD_FIRST) is called, +/// any dlsym() looks against that handle should only look in +/// the main executable, and not subsequent images. +/// + + +struct info +{ + const char* path; + void* handle; +}; +typedef struct info info; + +static info dlopen_or_fail(const char* path, int options) +{ + info result; + result.path = path; + result.handle = dlopen(path, options); + if ( result.handle == NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlopen(\"%s\") failed: %s", path, dlerror()); + exit(0); + } + //fprintf(stderr, "dlopen(%s, 0x%0X) => %p\n", path, options, result.handle); + return result; +} + +static void dlsym_should_fail(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym != NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlsym(handle-%s, \"%s\") should have failed", hp.path, symbol); + exit(0); + } +} + +static void dlsym_should_succeed(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym == NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlsym(handle-%s, \"%s\") failed", hp.path, symbol); + exit(0); + } +} + + +int main() +{ + int result; + info mainFirst = dlopen_or_fail(NULL, RTLD_FIRST); + info mainDefault = dlopen_or_fail(NULL, 0); + + dlsym_should_succeed(mainFirst, "main_foo"); + dlsym_should_fail(mainFirst, "foo"); + + dlsym_should_succeed(mainDefault, "main_foo"); + dlsym_should_succeed(mainDefault, "foo"); + + result = dlclose(mainFirst.handle); + if ( result != 0 ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlclose(mainFirst.handle) failed: %s", dlerror()); + exit(0); + } + result = dlclose(mainDefault.handle); + if ( result != 0 ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlclose(mainDefault.handle) failed: %s", dlerror()); + exit(0); + } + + PASS("dlsym-NULL-RTLD_FIRST"); + return EXIT_SUCCESS; +} + + +void main_foo() {} + + diff --git a/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile b/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile new file mode 100644 index 0000000..170c483 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + +run: all + ./main + +all: main + +main : main.c foo.bundle bar.bundle libfoo.dylib libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c libbase.dylib + ${CC} ${CCFLAGS} -bundle foo.c libbase.dylib -o foo.bundle + +bar.bundle : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o bar.bundle + +libfoo.dylib : foo.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + +libbar.dylib : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o libbar.dylib -sub_library libbase + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib -install_name ${PWD}/libbase.dylib + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle bar.bundle libfoo.dylib libbar.dylib libbase.dylib + + + diff --git a/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c b/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c new file mode 100644 index 0000000..7ef59fb --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int bar() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c b/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c new file mode 100644 index 0000000..87bb46c --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int base() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c b/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c new file mode 100644 index 0000000..fea884b --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c b/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c new file mode 100644 index 0000000..95d3ce4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c @@ -0,0 +1,123 @@ +/* + * 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() + + +/// +/// When dlopen() is called with the RTLD_FIRST option, +/// any dlsym() looks against that handle should only look in +/// that handle, and not subsequent images. +/// + + +struct info +{ + const char* path; + void* handle; +}; +typedef struct info info; + +static info dlopen_or_fail(const char* path, int options) +{ + info result; + result.path = path; + result.handle = dlopen(path, options); + if ( result.handle == NULL ) { + FAIL("dlopen-RTLD_FIRST: dlopen(\"%s\") failed: %s", path, dlerror()); + exit(0); + } + //fprintf(stderr, "dlopen(%s, 0x%0X) => %p\n", path, options, result.handle); + return result; +} + +static void dlsym_should_fail(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym != NULL ) { + FAIL("dlopen-RTLD_FIRST: dlsym(handle-%s, \"%s\") should have failed", hp.path, symbol); + exit(0); + } +} + +static void dlsym_should_succeed(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym == NULL ) { + FAIL("dlopen-RTLD_FIRST: dlsym(handle-%s, \"%s\") failed", hp.path, symbol); + exit(0); + } +} + + +int main() +{ + info libFooFirst = dlopen_or_fail("libfoo.dylib", RTLD_FIRST); + info libFoo = dlopen_or_fail("libfoo.dylib", 0); + + info libBarFirst = dlopen_or_fail("libbar.dylib", RTLD_FIRST); + info libBar = dlopen_or_fail("libbar.dylib", 0); + + dlsym_should_succeed(libFooFirst, "foo"); + dlsym_should_fail(libFooFirst, "base"); + dlsym_should_fail(libFooFirst, "bar"); + + dlsym_should_succeed(libFoo, "foo"); + dlsym_should_succeed(libFoo, "base"); + + dlsym_should_succeed(libBarFirst, "bar"); + dlsym_should_succeed(libBarFirst, "base"); // libbar re-exports libbase + dlsym_should_fail(libBarFirst, "foo"); + + dlsym_should_succeed(libBar, "bar"); + dlsym_should_succeed(libBar, "base"); + + + + info bundleFooFirst = dlopen_or_fail("foo.bundle", RTLD_FIRST); + info bundleFoo = dlopen_or_fail("foo.bundle", 0); + + info bundleBarFirst = dlopen_or_fail("bar.bundle", RTLD_FIRST); + info bundleBar = dlopen_or_fail("bar.bundle", 0); + + dlsym_should_succeed(bundleFooFirst, "foo"); + dlsym_should_fail(bundleFooFirst, "base"); + dlsym_should_fail(bundleFooFirst, "bar"); + + dlsym_should_succeed(bundleFoo, "foo"); + dlsym_should_succeed(bundleFoo, "base"); + + dlsym_should_succeed(bundleBarFirst, "bar"); + dlsym_should_fail(bundleBarFirst, "base"); + dlsym_should_fail(bundleBarFirst, "foo"); + + dlsym_should_succeed(bundleBar, "bar"); + dlsym_should_succeed(bundleBar, "base"); + + + PASS("dlsym-RTLD_FIRST bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile new file mode 100644 index 0000000..3cbb8e5 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### +### main links with hide/libfoo.dylib +### main is run with DYLD_FALLBACK_LIBRARY_PATH pointing to hide +### main calls dlopen("/foo/bar/libfoo.dylib", RTLD_NOLOAD) +### dlopen should *not* find the already loaded libfoo.dylib because +### it is only supposed to search existing loaded images for one +### with a matching path. +### + + +run: all + export DYLD_FALLBACK_LIBRARY_PATH=hide && ./main /foo/bar/libfoo.dylib + +all: main + +main : main.c hide/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c hide/libfoo.dylib -o main + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -o hide/libfoo.dylib + + + + +clean: + ${RM} ${RMFLAGS} *~ main hide + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c new file mode 100644 index 0000000..f629da7 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD does not find an image via a fallback path +/// + + +int main(int argc, const char* argv[]) +{ + const char* path = argv[1]; + void* handle = dlopen(path, RTLD_NOLOAD); + if ( handle != NULL ) + FAIL("dlopen-RTLD_NOLOAD-fallback, dlopen(% RTLD_NOLOAD) should have failed"); + else + PASS("dlopen-RTLD_NOLOAD-fallback"); + + return 0; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile new file mode 100644 index 0000000..3bb5738 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +libfoo.dylib : foo.c libbar.dylib libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib libbaz.dylib -o libfoo.dylib + +libbar.dylib : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -I${TESTROOT}/include libbase.dylib -o libbar.dylib + +libbaz.dylib : baz.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c libbase.dylib -o libbaz.dylib + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib libbase.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c new file mode 100644 index 0000000..62fdfec --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern bool bazInitialized(); + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + if ( bazInitialized() ) { + FAIL("dlopen-RTLD_NOLOAD-in-initializer baz initialized too early"); + exit(0); + } + + // calling dlopen with RTLD_NOLOAD. + // this should not cause initialzers to run. + // in particular, baz should be be initialized + dlopen("libbaz.dylib", RTLD_NOLOAD); + + if ( bazInitialized() ) { + FAIL("dlopen-RTLD_NOLOAD-in-initializer baz initialized by dlopen"); + exit(0); + } +} + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c new file mode 100644 index 0000000..3bcd164 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.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 +#include // exit(), EXIT_SUCCESS +#include + +static bool inited = false; + +void setBazInitialized() { inited = true; } + +bool bazInitialized() { return inited; } + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c new file mode 100644 index 0000000..6a80314 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +extern void setBazInitialized(); + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + setBazInitialized(); +} + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c new file mode 100644 index 0000000..1e61ab3 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + + +bool foo() +{ + return true; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c new file mode 100644 index 0000000..9b67e37 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c @@ -0,0 +1,45 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef bool (*fooProc)(void); + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"libfoo.dylib\") failed with: %s", dlerror()); + exit(0); + } + + dlclose(handle); + + PASS("dlopen-RTLD_NOLOAD-in-initializer"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile new file mode 100644 index 0000000..92a0b79 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile @@ -0,0 +1,59 @@ +## +# 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 + + +### +### Test that RTLD_NOLOAD finds existing image +### even when symlinks are used to obscure it +### + +run: all + ./main libfoosym.dylib + ./main2 libbar.dylib + + +all: main main2 libfoosym.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoosym.dylib : libfoo.dylib + ln -sf libfoo.dylib libfoosym.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o `pwd`/libfoo.dylib + +main2 : main.c libbarsym.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbarsym.dylib -o main2 + +libbarsym.dylib : libbar.dylib + ln -sf libbar.dylib libbarsym.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name `pwd`/libbarsym.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main2 libfoo.dylib libfoosym.dylib libbar.dylib libbarsym.dylib + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c new file mode 100644 index 0000000..c58fc07 --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c @@ -0,0 +1,2 @@ +int foo() { return 0; } + diff --git a/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c new file mode 100644 index 0000000..4ce752c --- /dev/null +++ b/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD finds existing images +/// + + +int main(int argc, const char* argv[]) +{ + // main links against libfoo.dylib, so it is already loaded + // libbar.dylib is a symlink to libfoo.dylib, so it should succeed + void* handle = dlopen(argv[1], RTLD_NOLOAD); + if ( handle != NULL ) + PASS("dlopen-RTLD_NOLOAD-symlink"); + else + FAIL("dlopen-RTLD_NOLOAD-symlink"); + return 0; +} diff --git a/unit-tests/test-cases/dlopen-basic/main.c b/unit-tests/test-cases/dlopen-basic/main.c index 7ce2f89..f877537 100644 --- a/unit-tests/test-cases/dlopen-basic/main.c +++ b/unit-tests/test-cases/dlopen-basic/main.c @@ -31,7 +31,7 @@ static void trySO(const char* path) { void* handle = dlopen(path, RTLD_LAZY); if ( handle == NULL ) { - FAIL("dlopen(\"%s\") failed", path); + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); exit(0); } diff --git a/unit-tests/test-cases/dlopen-dyld-locking/Makefile b/unit-tests/test-cases/dlopen-dyld-locking/Makefile new file mode 100644 index 0000000..9902a73 --- /dev/null +++ b/unit-tests/test-cases/dlopen-dyld-locking/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main libbar.dylib + +main : main.c libfoo.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbase.dylib + +libfoo.dylib : foo.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib -I${TESTROOT}/include + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbase.dylib + + + diff --git a/unit-tests/test-cases/dlopen-dyld-locking/bar.c b/unit-tests/test-cases/dlopen-dyld-locking/bar.c new file mode 100644 index 0000000..0531aa2 --- /dev/null +++ b/unit-tests/test-cases/dlopen-dyld-locking/bar.c @@ -0,0 +1,31 @@ +/* + * 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@ + */ + + +void __attribute__((constructor)) myInit() +{ + // printf("in bar\n"); +} + + + diff --git a/unit-tests/test-cases/dlopen-dyld-locking/base.c b/unit-tests/test-cases/dlopen-dyld-locking/base.c new file mode 100644 index 0000000..4f842dc --- /dev/null +++ b/unit-tests/test-cases/dlopen-dyld-locking/base.c @@ -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@ + */ +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static pthread_mutex_t sBarrierMutex; +static pthread_cond_t sBarrierFree; + +static volatile int sValue = 0; + +static void __attribute__((constructor)) myinit() +{ + pthread_mutex_init(&sBarrierMutex, NULL); + pthread_cond_init(&sBarrierFree, NULL); +} + +void waitForState(int value) +{ + pthread_mutex_lock(&sBarrierMutex); + while ( sValue < value ) { + struct timeval tvNow; + struct timespec tsTimeout; + gettimeofday(&tvNow, NULL); + 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"); + exit(0); + } + } + pthread_mutex_unlock(&sBarrierMutex); +} + + +void setState(int value) +{ + pthread_mutex_lock(&sBarrierMutex); + sValue = value; + pthread_cond_broadcast(&sBarrierFree); + pthread_mutex_unlock(&sBarrierMutex); +} + + diff --git a/unit-tests/test-cases/dlopen-dyld-locking/base.h b/unit-tests/test-cases/dlopen-dyld-locking/base.h new file mode 100644 index 0000000..f1aef79 --- /dev/null +++ b/unit-tests/test-cases/dlopen-dyld-locking/base.h @@ -0,0 +1,3 @@ + +extern void waitForState(int); +extern void setState(int); diff --git a/unit-tests/test-cases/dlopen-dyld-locking/foo.c b/unit-tests/test-cases/dlopen-dyld-locking/foo.c new file mode 100644 index 0000000..5753fa5 --- /dev/null +++ b/unit-tests/test-cases/dlopen-dyld-locking/foo.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 "base.h" + +void __attribute__((constructor)) myInit() +{ + setState(1); + waitForState(2); +} + + + diff --git a/unit-tests/test-cases/dlopen-dyld-locking/main.c b/unit-tests/test-cases/dlopen-dyld-locking/main.c new file mode 100644 index 0000000..7952613 --- /dev/null +++ b/unit-tests/test-cases/dlopen-dyld-locking/main.c @@ -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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "base.h" + + +/// +/// This test verifies that dlopen() releases the dyld lock +/// while initializers are run. +/// +/// + + +static void* work(void* arg) +{ + waitForState(1); + dlopen("libbar.dylib", RTLD_LAZY); // dladdr() will hang until dyld lock is released + setState(2); // the initializer in libfoo.dylib is block waiting for state 2 + return NULL; +} + + +int main() +{ + pthread_t otherThread; + if ( pthread_create(&otherThread, NULL, work, NULL) != 0 ) { + FAIL("dlopen-dyld-locking: pthread_create failed"); + exit(0); + } + + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-dyld-locking: dlopen(\"libfoo.dylib\") failed: %s", dlerror()); + exit(0); + } + + PASS("dlsym-dyld-locking"); + 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 new file mode 100644 index 0000000..cc201a6 --- /dev/null +++ b/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main foo.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c b/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c new file mode 100644 index 0000000..fea884b --- /dev/null +++ b/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c @@ -0,0 +1,28 @@ +/* + * 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@ + */ + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-from-anonymous-code/main.c b/unit-tests/test-cases/dlopen-from-anonymous-code/main.c new file mode 100644 index 0000000..8dfe72f --- /dev/null +++ b/unit-tests/test-cases/dlopen-from-anonymous-code/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // memcpy +#include // exit(), EXIT_SUCCESS +#include +#include // sys_icache_invalidate +#include // for mprotext + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +void* calldlopen(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) +{ + return (*dlopen_proc)(path, mode); +} + +// +// try calling dlopen() from code not owned by dyld +// +int main() +{ + void* codeBlock = malloc(4096); + memcpy(codeBlock, &calldlopen, 4096); + sys_icache_invalidate(codeBlock, 4096); + mprotect(codeBlock, 4096, PROT_READ | PROT_EXEC); + //fprintf(stderr, "codeBlock=%p\n", codeBlock); + + void* (*caller)(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) = codeBlock; + + void* handle = (*caller)("foo.bundle", RTLD_LAZY, &dlopen); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "foo.bundle", 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 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + + PASS("dlopen-from-anonymous-code"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-in-initializer/Makefile b/unit-tests/test-cases/dlopen-in-initializer/Makefile new file mode 100644 index 0000000..d3dd9c2 --- /dev/null +++ b/unit-tests/test-cases/dlopen-in-initializer/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main || echo "FAIL dlopen-in-initializer" + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dlopen-in-initializer/foo.c b/unit-tests/test-cases/dlopen-in-initializer/foo.c new file mode 100644 index 0000000..3bef65e --- /dev/null +++ b/unit-tests/test-cases/dlopen-in-initializer/foo.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 on a non-existent file to verify error handled cleanly + dlopen("/no_such/path", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-in-initializer/main.c b/unit-tests/test-cases/dlopen-in-initializer/main.c new file mode 100644 index 0000000..bdc6dcf --- /dev/null +++ b/unit-tests/test-cases/dlopen-in-initializer/main.c @@ -0,0 +1,67 @@ +/* + * 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() + + +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); + } + } + +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlopen-in-initializer"); + 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 new file mode 100644 index 0000000..723ff97 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main || echo "FAIL dlopen-init-dlopen-up" + +all: main + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c b/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c new file mode 100644 index 0000000..15b1327 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c @@ -0,0 +1,5 @@ + + +extern int foo(); + +int bar() { return foo(); } diff --git a/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c b/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c new file mode 100644 index 0000000..a816ea8 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c @@ -0,0 +1,38 @@ +/* + * 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 + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // this is an upward dependency since libbar depends on libfoo + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-init-dlopen-up/main.c b/unit-tests/test-cases/dlopen-init-dlopen-up/main.c new file mode 100644 index 0000000..d9a2c70 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen-up/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "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 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + +} + + + +int main() +{ + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen-up"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-init-dlopen/Makefile b/unit-tests/test-cases/dlopen-init-dlopen/Makefile new file mode 100644 index 0000000..4a35562 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main || echo "FAIL dlopen-init-dlopen" + +all: main + +main : main.c libbar.dylib libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-init-dlopen/bar.c b/unit-tests/test-cases/dlopen-init-dlopen/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/unit-tests/test-cases/dlopen-init-dlopen/foo.c b/unit-tests/test-cases/dlopen-init-dlopen/foo.c new file mode 100644 index 0000000..78ab60d --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen/foo.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 foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-init-dlopen/main.c b/unit-tests/test-cases/dlopen-init-dlopen/main.c new file mode 100644 index 0000000..9f1c5fb --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-dlopen/main.c @@ -0,0 +1,66 @@ +/* + * 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() + + +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); + } + } + +} + + + +int main() +{ + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-init-up/Makefile b/unit-tests/test-cases/dlopen-init-up/Makefile new file mode 100644 index 0000000..3991ab4 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-up/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main || echo "FAIL dlopen-init-dlopen-up" + +all: main + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/unit-tests/test-cases/dlopen-init-up/bar.c b/unit-tests/test-cases/dlopen-init-up/bar.c new file mode 100644 index 0000000..15b1327 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-up/bar.c @@ -0,0 +1,5 @@ + + +extern int foo(); + +int bar() { return foo(); } diff --git a/unit-tests/test-cases/dlopen-init-up/foo.c b/unit-tests/test-cases/dlopen-init-up/foo.c new file mode 100644 index 0000000..77dd28f --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-up/foo.c @@ -0,0 +1,39 @@ +/* + * 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 + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // this is an upward dependency since libbar depends on libfoo + // rdar://problem/5213017 + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlopen-init-up/main.c b/unit-tests/test-cases/dlopen-init-up/main.c new file mode 100644 index 0000000..abdd5f1 --- /dev/null +++ b/unit-tests/test-cases/dlopen-init-up/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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int main() +{ + PASS("dlopen-init-up"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen_preflight-basic/Makefile b/unit-tests/test-cases/dlopen_preflight-basic/Makefile new file mode 100644 index 0000000..4b1d3e7 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-basic/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + ./main batch + +all: main foo.bundle bar.bundle bogus.bundle libbogus.dylib + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +bogus.bundle : + echo "bogus" > bogus.bundle + +libbogus.dylib : + echo "bogus" > libbogus.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle bogus.bundle libbogus.dylib + diff --git a/unit-tests/test-cases/dlopen_preflight-basic/bar.c b/unit-tests/test-cases/dlopen_preflight-basic/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-basic/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/unit-tests/test-cases/dlopen_preflight-basic/foo.c b/unit-tests/test-cases/dlopen_preflight-basic/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-basic/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/dlopen_preflight-basic/main.c b/unit-tests/test-cases/dlopen_preflight-basic/main.c new file mode 100644 index 0000000..4fdec0e --- /dev/null +++ b/unit-tests/test-cases/dlopen_preflight-basic/main.c @@ -0,0 +1,95 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //for (uint32_t i=0; i < infoCount; ++i) { + // fprintf(stderr, "batch mapped: %d/%d %s\n", i, infoCount, info[i].imageFilePath); + //} + for (uint32_t i=0; i < infoCount; ++i) { + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "cannot load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + if ( dlopen_preflight("foo.bundle") ) { + FAIL("dlopen_preflight-basic foo.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "foo.bundle, dlerror: %s\n", dlerror()); + + if ( dlopen_preflight("bar.bundle") ) { + FAIL("dlopen_preflight-basic bar.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "bar.bundle, dlerror: %s\n", dlerror()); + + if ( dlopen_preflight("bogus.bundle") ) { + FAIL("dlopen_preflight-basic bogus.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "bogus.bundle, dlerror: %s\n", dlerror()); + + if ( ! dlopen_preflight("/usr/lib/libSystem.B.dylib") ) { + FAIL("dlopen_preflight-basic libSystem should be loadable, but got: %s", dlerror()); + exit(0); + } + + + PASS("dlopen_preflight-basic"); + + 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 491d9f4..980ce61 100644 --- a/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile +++ b/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile @@ -23,10 +23,21 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +PROG = ./main + +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + PROG = /usr/bin/true + endif +endif + + + run: all - ${TESTROOT}/bin/exit-zero-pass.pl "dlsym-RTLD_NEXT-missing with circular libraries" "dlsym-RTLD_NEXT-missing with circular libraries" ./main + ${TESTROOT}/bin/exit-zero-pass.pl "dlsym-RTLD_NEXT-missing with circular libraries" "dlsym-RTLD_NEXT-missing with circular libraries" ${PROG} -all: main +all: main main : main.c foo1.dylib ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo1.dylib diff --git a/unit-tests/test-cases/dlsym-RTLD_SELF/main.c b/unit-tests/test-cases/dlsym-RTLD_SELF/main.c index ce7c3a8..8c137c6 100644 --- a/unit-tests/test-cases/dlsym-RTLD_SELF/main.c +++ b/unit-tests/test-cases/dlsym-RTLD_SELF/main.c @@ -37,7 +37,6 @@ /// We also check that this works in the main executable. /// -#ifdef RTLD_SELF int foo() { @@ -63,12 +62,10 @@ static void trySO(const char* pathToLoad) (*sym)(); } -#endif int main() { -#ifdef RTLD_SELF trySO("test.bundle"); trySO("test.dylib"); @@ -77,8 +74,5 @@ int main() } PASS("dlsym-RTLD_SELF bundle and dylib"); -#else - XFAIL("dlsym-RTLD_SELF not implemented"); -#endif return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dyld-func-lookup/Makefile b/unit-tests/test-cases/dyld-func-lookup/Makefile new file mode 100644 index 0000000..56ebee5 --- /dev/null +++ b/unit-tests/test-cases/dyld-func-lookup/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main test.bundle test.dylib + +main : main.c foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/unit-tests/test-cases/dyld-func-lookup/foo.c b/unit-tests/test-cases/dyld-func-lookup/foo.c new file mode 100644 index 0000000..00b5af3 --- /dev/null +++ b/unit-tests/test-cases/dyld-func-lookup/foo.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +extern bool _dyld_func_lookup(const char* dyld_func_name, void** address); + + + +bool check_dyld_func_lookup() +{ + void* address; + + // dlopen does not exist, something is wrong + if ( ! _dyld_func_lookup("__dyld_dlopen", &address) ) + return false; + + // if a garbage string returns true, something is wrong + if ( _dyld_func_lookup("blablah", &address) ) + return false; + + // looks good + return true; +} diff --git a/unit-tests/test-cases/dyld-func-lookup/main.c b/unit-tests/test-cases/dyld-func-lookup/main.c new file mode 100644 index 0000000..270e343 --- /dev/null +++ b/unit-tests/test-cases/dyld-func-lookup/main.c @@ -0,0 +1,69 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool check_dyld_func_lookup(); + +typedef bool (*proc)(void); + +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); + } + + proc sym = (proc)dlsym(handle, "check_dyld_func_lookup"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + if ( sym == &check_dyld_func_lookup ) + FAIL("found same check_dyld_func_lookup as in main executable"); + + if ( ! (*sym)() ) + FAIL("check_dyld_func_lookup failed for %s", path); + + dlclose(handle); +} + + + +int main() +{ + if ( ! check_dyld_func_lookup() ) + FAIL("check_dyld_func_lookup failed for main executable"); + + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dyld-func-lookup"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dyld-launched-prebound/Makefile b/unit-tests/test-cases/dyld-launched-prebound/Makefile index 4eb418a..7233096 100644 --- a/unit-tests/test-cases/dyld-launched-prebound/Makefile +++ b/unit-tests/test-cases/dyld-launched-prebound/Makefile @@ -27,7 +27,7 @@ run: all ${TESTROOT}/bin/exit-zero-pass.pl "_dyld_launched_prebound() was implemented" "_dyld_launched_prebound() was not implemented" ./main all: - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c clean: ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/dyld-launched-prebound/main.c b/unit-tests/test-cases/dyld-launched-prebound/main.c index b4bf269..a64d5a2 100644 --- a/unit-tests/test-cases/dyld-launched-prebound/main.c +++ b/unit-tests/test-cases/dyld-launched-prebound/main.c @@ -27,7 +27,7 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() int -main(int argc, char **argv, char **envp, char**appl) +main(int argc, const char* argv[]) { _dyld_launched_prebound(); return EXIT_SUCCESS; diff --git a/unit-tests/test-cases/dyld-slide/Makefile b/unit-tests/test-cases/dyld-slide/Makefile index 35f422d..98fd14d 100644 --- a/unit-tests/test-cases/dyld-slide/Makefile +++ b/unit-tests/test-cases/dyld-slide/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@ # @@ -27,7 +27,7 @@ run: all ${TESTROOT}/bin/exit-zero-pass.pl "dyld did slide" "dyld did not slide" ./main all: - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c -pagezero_size 0x40000000 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 ad5b149..957dfef 100644 --- a/unit-tests/test-cases/dyld-slide/main.c +++ b/unit-tests/test-cases/dyld-slide/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -29,10 +29,9 @@ // This builds an executable that is just big enough to force dyld to slide a bit // -#define ARRAY_SIZE 301800000 +#define ARRAY_SIZE 335400000 int bigarray1[ARRAY_SIZE]; -int bigarray2[ARRAY_SIZE]; int main() diff --git a/unit-tests/test-cases/executable-image-index/Makefile b/unit-tests/test-cases/executable-image-index/Makefile new file mode 100644 index 0000000..ae7321e --- /dev/null +++ b/unit-tests/test-cases/executable-image-index/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + #export DYLD_INSERT_LIBRARIES=libfoo.dylib && ./main + ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/executable-image-index/foo.c b/unit-tests/test-cases/executable-image-index/foo.c new file mode 100644 index 0000000..83c0b65 --- /dev/null +++ b/unit-tests/test-cases/executable-image-index/foo.c @@ -0,0 +1,2 @@ +void foo() { } + diff --git a/unit-tests/test-cases/executable-image-index/main.c b/unit-tests/test-cases/executable-image-index/main.c new file mode 100644 index 0000000..315ea8e --- /dev/null +++ b/unit-tests/test-cases/executable-image-index/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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// many apps rely on _dyld_get_image_header(0) being the main executable +// DYLD_INSERT_LIBRARIES can change the order + +extern const struct mach_header __dso_handle; + +int main() +{ + if ( _dyld_get_image_header(0) == &__dso_handle ) + PASS("executable-image-index"); + else + FAIL("executable-image-index: index(0) is %s", _dyld_get_image_name(0)); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/fallback-with-suid/Makefile b/unit-tests/test-cases/fallback-with-suid/Makefile index f2a630d..6fd0158 100644 --- a/unit-tests/test-cases/fallback-with-suid/Makefile +++ b/unit-tests/test-cases/fallback-with-suid/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -24,23 +24,21 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile run: all - export HOME="`pwd`/hide" && ./main user - export HOME="`pwd`/hide" && ./main-suid root - -all: main main-suid + ${TESTROOT}/bin/exit-non-zero-pass.pl "fallback-with-suid" "fallback-with-suid" ./main-suid + +all: main-suid -main: main.c hide/lib/libfoo.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib main-suid: main cp main main-suid sudo chown root main-suid sudo chmod 4755 main-suid -hide/lib/libfoo.dylib : foo.c - mkdir -p hide/lib - ${CC} ${CCFLAGS} foo.c -dynamiclib -o hide/lib/libfoo.dylib +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /bogus/libz.dylib clean: - ${RM} ${RMFLAGS} *~ main main-suid hide + ${RM} ${RMFLAGS} *~ main main-suid libfoo.dylib diff --git a/unit-tests/test-cases/fallback-with-suid/foo.c b/unit-tests/test-cases/fallback-with-suid/foo.c index fa1d6fe..4ecb931 100644 --- a/unit-tests/test-cases/fallback-with-suid/foo.c +++ b/unit-tests/test-cases/fallback-with-suid/foo.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,3 +21,5 @@ * @APPLE_LICENSE_HEADER_END@ */ +void compress() {} + diff --git a/unit-tests/test-cases/fallback-with-suid/main.c b/unit-tests/test-cases/fallback-with-suid/main.c index a0ce919..bb79393 100644 --- a/unit-tests/test-cases/fallback-with-suid/main.c +++ b/unit-tests/test-cases/fallback-with-suid/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -31,22 +31,12 @@ // binaries set to run as some other user id never use $HOME part of fallback-path // +extern void compress(); + int main(int argc, const char *argv[]) { - const struct mach_header* mh = NSAddImage("/foo/bar/libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR); - - if ( strcmp(argv[1], "root") == 0 ) { - if ( mh == NULL ) - PASS("fallback-with-suid root"); - else - FAIL("fallback-with-suid root"); - } - else { - if ( mh != NULL ) - PASS("fallback-with-suid user"); - else - FAIL("fallback-with-suid user"); - } + if ( &compress != NULL ) + FAIL("fallback-with-suid root"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/flat-insert/Makefile b/unit-tests/test-cases/flat-insert/Makefile new file mode 100644 index 0000000..ff17213 --- /dev/null +++ b/unit-tests/test-cases/flat-insert/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### verify inserted libraries override with flat namespace + +run: all + export DYLD_INSERT_LIBRARIES="libfoo.dylib" && ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -flat_namespace -Wl,-interposable + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/flat-insert/foo.c b/unit-tests/test-cases/flat-insert/foo.c new file mode 100644 index 0000000..2d68edf --- /dev/null +++ b/unit-tests/test-cases/flat-insert/foo.c @@ -0,0 +1,30 @@ +/* + * 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 + + +int foo() +{ + return 42; +} + diff --git a/unit-tests/test-cases/flat-insert/main.c b/unit-tests/test-cases/flat-insert/main.c new file mode 100644 index 0000000..160955c --- /dev/null +++ b/unit-tests/test-cases/flat-insert/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + if ( foo() == 42 ) + PASS("flat-insert"); + else + FAIL("flat-insert"); + return EXIT_SUCCESS; +} + +// inserted library has another copy of foo() that should +// overridde this one and return 42 +int foo() +{ + return 0; +} diff --git a/unit-tests/test-cases/framework-fallback/Makefile b/unit-tests/test-cases/framework-fallback/Makefile index adf063d..44cfad1 100644 --- a/unit-tests/test-cases/framework-fallback/Makefile +++ b/unit-tests/test-cases/framework-fallback/Makefile @@ -29,7 +29,7 @@ run: all all : main main: main.c - ${CC} main.c -o main -I${TESTROOT}/include + ${CC} ${CCFLAGS} -Wno-deprecated-declarations main.c -o main -I${TESTROOT}/include clean: ${RM} ${RMFLAGS} main diff --git a/unit-tests/test-cases/image-state-change/Makefile b/unit-tests/test-cases/image-state-change/Makefile new file mode 100644 index 0000000..b287606 --- /dev/null +++ b/unit-tests/test-cases/image-state-change/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main foo.bundle + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib + diff --git a/unit-tests/test-cases/image-state-change/bar.c b/unit-tests/test-cases/image-state-change/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/unit-tests/test-cases/image-state-change/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/unit-tests/test-cases/image-state-change/foo.c b/unit-tests/test-cases/image-state-change/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/image-state-change/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/image-state-change/main.c b/unit-tests/test-cases/image-state-change/main.c new file mode 100644 index 0000000..0a6673d --- /dev/null +++ b/unit-tests/test-cases/image-state-change/main.c @@ -0,0 +1,101 @@ +/* + * 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 // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +static int singleMappedCount = 0; +static int batchMappedCount = 0; + + + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) + printf("batchMappedHandler(): %u/%u -> %s\n", i, infoCount, info[i].imageFilePath); + if ( state != dyld_image_state_dependents_mapped ) { + FAIL("image-state-change: batchMappedHandler passed state %d", state); + exit(0); + } + batchMappedCount += infoCount; + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleMappedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_mapped ) { + FAIL("image-state-change: singleMappedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleMappedHandler given %d images", infoCount); + exit(0); + } + ++singleMappedCount; + return NULL; +} + +static void loadAndUnLoad() +{ + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-change: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-state-change: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } +} + + +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); + // with batch mode we get notified of existing images, but not with single mode, so re-sync counts + batchMappedCount=0; + + loadAndUnLoad(); + + loadAndUnLoad(); + + if ( singleMappedCount == batchMappedCount ) + PASS("image-state-change"); + else + FAIL("image-state-change: batch count=%d, single count=%d", batchMappedCount, singleMappedCount); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/image-state-deny-OFI/Makefile b/unit-tests/test-cases/image-state-deny-OFI/Makefile new file mode 100644 index 0000000..5ad9359 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-OFI/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + ./main batch + +all: main foo.bundle bar.bundle foo2.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +foo2.bundle: foo.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo2.bundle foo.c + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle foo2.bundle + diff --git a/unit-tests/test-cases/image-state-deny-OFI/bar.c b/unit-tests/test-cases/image-state-deny-OFI/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-OFI/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/unit-tests/test-cases/image-state-deny-OFI/foo.c b/unit-tests/test-cases/image-state-deny-OFI/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-OFI/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/image-state-deny-OFI/main.c b/unit-tests/test-cases/image-state-deny-OFI/main.c new file mode 100644 index 0000000..3e1f341 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-OFI/main.c @@ -0,0 +1,132 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool doneRegistering = false; + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) { + //fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "can't load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + + +static const char* singleInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "singleInitializedHandler(%s)\n", info[0].imageFilePath); + if ( doneRegistering ) { + FAIL("image-state-deny something loaded"); + exit(0); + } + return NULL; +} + +//static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +//{ +// //for (uint32_t i=0; i < infoCount; ++i) +// // fprintf(stderr, "bound: %u %s\n", i, info[i].imageFilePath); +// return NULL; +//} + +static void load(const char* name) +{ + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(name, &ofi) == NSObjectFileImageSuccess ) { + NSModule mod = NSLinkModule(ofi, name, NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod != NULL ) { + FAIL("neither NSCreateObjectFileImageFromFile nor NSLinkModule failed but should have"); + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + } + exit(0); + } + else { + NSDestroyObjectFileImage(ofi); + } + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, singleInitializedHandler); + doneRegistering = true; + + // tell dyld we want to know when images successfully loaded + //dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("foo2.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed for foo2.bundle"); + exit(0); + } + + load("foo.bundle"); + + load("bar.bundle"); + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed for foo2.bundle"); + exit(0); + } + +// dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY); + + PASS("image-state-deny"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/image-state-deny-dlclose/Makefile b/unit-tests/test-cases/image-state-deny-dlclose/Makefile new file mode 100644 index 0000000..ed88495 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-dlclose/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + +main: main.c libfoo.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbase.dylib base.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbase.dylib + diff --git a/unit-tests/test-cases/image-state-deny-dlclose/base.c b/unit-tests/test-cases/image-state-deny-dlclose/base.c new file mode 100644 index 0000000..842e2e2 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-dlclose/base.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //for (uint32_t i=0; i < infoCount; ++i) { + // fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + //} + return NULL; +} + +static void myregister() __attribute__((constructor)); + +static void myregister() +{ + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); +} diff --git a/unit-tests/test-cases/image-state-deny-dlclose/foo.c b/unit-tests/test-cases/image-state-deny-dlclose/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-dlclose/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/image-state-deny-dlclose/main.c b/unit-tests/test-cases/image-state-deny-dlclose/main.c new file mode 100644 index 0000000..e4756bb --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-dlclose/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#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[]) +{ + // open and close base + void* h1 = dlopen("libbase.dylib", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libbase.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h1); + + // if base was unloaded, then this dlopen will jump to neverland + void* h2 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( h2 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libfoo.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + + void* h3 = dlopen("libbase.dylib", RTLD_LAZY); + if ( h3 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libbase.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h3); + + dlclose(h2); + + + PASS("image-state-deny-dlclose"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/image-state-deny/Makefile b/unit-tests/test-cases/image-state-deny/Makefile new file mode 100644 index 0000000..4bff299 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + ./main batch + +all: main foo.bundle bar.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle + diff --git a/unit-tests/test-cases/image-state-deny/bar.c b/unit-tests/test-cases/image-state-deny/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/unit-tests/test-cases/image-state-deny/foo.c b/unit-tests/test-cases/image-state-deny/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/image-state-deny/main.c b/unit-tests/test-cases/image-state-deny/main.c new file mode 100644 index 0000000..df8f1d9 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny/main.c @@ -0,0 +1,113 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool doneRegistering = false; + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) { + //fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "can't load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + + +static const char* singleInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "singleInitializedHandler(%s)\n", info[0].imageFilePath); + if ( doneRegistering ) { + FAIL("image-state-deny something loaded"); + exit(0); + } + return NULL; +} + +//static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +//{ +// //for (uint32_t i=0; i < infoCount; ++i) +// // fprintf(stderr, "bound: %u %s\n", i, info[i].imageFilePath); +// return NULL; +//} + +static void load(const char* name) +{ + void* handle = dlopen(name, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny: dlopen(%s) should have failed", name); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, singleInitializedHandler); + doneRegistering = true; + + // tell dyld we want to know when images successfully loaded + //dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + load("foo.bundle"); + + load("bar.bundle"); + + //dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY); + + PASS("image-state-deny"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/image-state-dependents-initialized/Makefile b/unit-tests/test-cases/image-state-dependents-initialized/Makefile new file mode 100644 index 0000000..91b2ef1 --- /dev/null +++ b/unit-tests/test-cases/image-state-dependents-initialized/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/image-state-dependents-initialized/bar.c b/unit-tests/test-cases/image-state-dependents-initialized/bar.c new file mode 100644 index 0000000..3b6c323 --- /dev/null +++ b/unit-tests/test-cases/image-state-dependents-initialized/bar.c @@ -0,0 +1,119 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +static enum { start, barDeps, bar, fooDeps, foo, mainDeps, main } step = start; + + +static const char* depInitHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( infoCount != 1 ) { + FAIL("image-state-dependents-initialized: depInitHandler infoCount != 1\n"); + exit(0); + } + + //fprintf(stderr, "depInitHandler(%s), state=%d\n", info[0].imageFilePath, step); + + if ( (step == start) && (strstr(info[0].imageFilePath, "libbar.dylib") != NULL) ) + step = barDeps; + else if ( (step == bar) && (strstr(info[0].imageFilePath, "libfoo.dylib") != NULL) ) + step = fooDeps; + else if ( (step == foo) && (strstr(info[0].imageFilePath, "/main") != NULL) ) + step = mainDeps; + else { + fprintf(stderr, "out of order depInitHandler\n"); + exit(0); + } + + + return NULL; +} + +static const char* initHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( infoCount != 1 ) { + FAIL("image-state-dependents-initialized: initHandler infoCount != 1\n"); + exit(0); + } + + //fprintf(stderr, "initHandler(%s), state=%d\n", info[0].imageFilePath, step); + bool isBar = (strstr(info[0].imageFilePath, "libbar.dylib") != NULL); + bool isFoo = (strstr(info[0].imageFilePath, "libfoo.dylib") != NULL); + bool isMain= (strstr(info[0].imageFilePath, "/main") != NULL); + + if ( isBar ) { + if ( step == barDeps ) { + step = bar; + } + else { + fprintf(stderr, "out of order initHandler bar\n"); + exit(0); + } + } + else if ( isFoo ) { + if ( step == fooDeps ) { + step = foo; + } + else { + fprintf(stderr, "out of order initHandler foo\n"); + exit(0); + } + } + else if ( isMain ) { + if ( step == mainDeps ) { + step = main; + } + else { + fprintf(stderr, "out of order initHandler main\n"); + exit(0); + } + } + + + return NULL; +} + + + + +void __attribute__((constructor)) setup_bar() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, false, depInitHandler); + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, initHandler); +} + + + diff --git a/unit-tests/test-cases/image-state-dependents-initialized/foo.c b/unit-tests/test-cases/image-state-dependents-initialized/foo.c new file mode 100644 index 0000000..9584c6e --- /dev/null +++ b/unit-tests/test-cases/image-state-dependents-initialized/foo.c @@ -0,0 +1,11 @@ + + +int foo_value = 1; + +void __attribute__((constructor)) setup_foo() +{ + foo_value = 2; +} + + + diff --git a/unit-tests/test-cases/image-state-dependents-initialized/main.c b/unit-tests/test-cases/image-state-dependents-initialized/main.c new file mode 100644 index 0000000..d4933f8 --- /dev/null +++ b/unit-tests/test-cases/image-state-dependents-initialized/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + PASS("image-state-dependents-initialized"); + + return EXIT_SUCCESS; +} + + +int main_value = 1; + +void __attribute__((constructor)) setup_main() +{ + main_value = 2; +} + diff --git a/unit-tests/test-cases/image-suffix/Makefile b/unit-tests/test-cases/image-suffix/Makefile index 3c39e98..4317204 100644 --- a/unit-tests/test-cases/image-suffix/Makefile +++ b/unit-tests/test-cases/image-suffix/Makefile @@ -28,7 +28,7 @@ run : check1 check2 check3 check4 check5 check6 check7 check8 all : main1 main2 main3 main4 main5 main6 main7 main8 main : main.c libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main 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 @@ -68,7 +68,7 @@ clean: # (fails on 10.3) # main1 : main.c libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main1 libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main1 libfoo.dylib check1: main1 DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 libfoo.dylib @@ -79,7 +79,7 @@ check1: main1 # check2: main links with libfoo_debug.dylib and dynamically loads libfoo.dylib # main2 : main.c libfoo_debug.dylib - ${CC} -I${TESTROOT}/include main.c -o main2 libfoo_debug.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main2 libfoo_debug.dylib check2: main2 echo "pwd-1 is ${PWD}" @@ -92,7 +92,7 @@ check2: main2 # check3: main links with libfoo.dylib sets DYLD_LIBRARY_PATH=hide and dynamically loads libfoo.dylib # main3 : main.c libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main3 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 @@ -103,7 +103,7 @@ check3: main3 # (fails on 10.3) # main4 : main.c libfoo.dylib - ${CC} -I${TESTROOT}/include main.c -o main4 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 @@ -114,7 +114,7 @@ check4: main4 # (fails on 10.3) # main5 : main.c libbar.dylib libbar_debug.dylib - ${CC} -I${TESTROOT}/include main.c -o main5 libbar.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 @@ -125,7 +125,7 @@ check5: main5 # (fails on 10.3) # main6 : main.c libbar_debug.dylib - ${CC} -I${TESTROOT}/include main.c -o main6 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 @@ -135,7 +135,7 @@ check6: main6 # check7: main links with libbar.dylib sets DYLD_LIBRARY_PATH=hide and dynamically loads libbar.dylib # main7 : main.c libbar.dylib - ${CC} -I${TESTROOT}/include main.c -o main7 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 @@ -146,7 +146,7 @@ check7: main7 # (fails on 10.3) # main8 : main.c libbar.dylib - ${CC} -I${TESTROOT}/include main.c -o main8 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 diff --git a/unit-tests/test-cases/initializer-args/Makefile b/unit-tests/test-cases/initializer-args/Makefile index 3b5e6be..1afdb74 100644 --- a/unit-tests/test-cases/initializer-args/Makefile +++ b/unit-tests/test-cases/initializer-args/Makefile @@ -27,7 +27,7 @@ run: all ./main arg1 arg2 all: - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c clean: ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/Makefile b/unit-tests/test-cases/insert-libraries-with-initializer/Makefile index 352ec10..f4ae30a 100644 --- a/unit-tests/test-cases/insert-libraries-with-initializer/Makefile +++ b/unit-tests/test-cases/insert-libraries-with-initializer/Makefile @@ -23,13 +23,35 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile +# +# verifies that initializers for inserted libraries run before the +# main executables initializers +# + + run: all - export DYLD_INSERT_LIBRARIES="libfoo.dylib" && ./main + export DYLD_INSERT_LIBRARIES=libinsert.dylib && ./main -all: - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c +all: main libinsert.dylib + + +main: main.c libfoo1.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib -clean: - ${RM} ${RMFLAGS} *~ main libfoo.dylib +libfoo1.dylib: foo1.c libbase.dylib libfoo2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo1.dylib foo1.c libbase.dylib libfoo2.dylib + +libfoo2.dylib: foo2.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo2.dylib foo2.c libbase.dylib + +libinsert.dylib: insert.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libinsert.dylib insert.c libbase.dylib + +libbase.dylib: base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbase.dylib base.c + + +clean: + ${RM} ${RMFLAGS} *~ main lib*.dylib + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/base.c b/unit-tests/test-cases/insert-libraries-with-initializer/base.c new file mode 100644 index 0000000..fcd63c6 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-with-initializer/base.c @@ -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@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static int state = 1; +bool badOrder = false; + +void setState(int nextState) +{ + if ( nextState == state ) + ++state; + else + badOrder = true; + +} + + +void baseCheck() +{ + if ( badOrder ) + FAIL("insert-libraries-with-initializers bad state at %d", state); + else + PASS("insert-libraries-with-initializers"); +} + + + + + + +static __attribute__((constructor)) void base_init() +{ + //fprintf(stderr, "base_init()\n"); + setState(1); +} + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/base.h b/unit-tests/test-cases/insert-libraries-with-initializer/base.h new file mode 100644 index 0000000..1dbf697 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-with-initializer/base.h @@ -0,0 +1,27 @@ +/* + * 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@ + */ + + +extern void setState(int); +extern void baseCheck(); + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c b/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c new file mode 100644 index 0000000..ac55290 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c @@ -0,0 +1,35 @@ +/* + * 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 "base.h" + + + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void foo1_init() +{ + //fprintf(stderr, "foo1_init()\n"); + setState(4); +} + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c b/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c new file mode 100644 index 0000000..6574e02 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-with-initializer/foo2.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 "base.h" + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void foo2_init() +{ + //fprintf(stderr, "foo2_init()\n"); + setState(3); +} + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/insert.c b/unit-tests/test-cases/insert-libraries-with-initializer/insert.c new file mode 100644 index 0000000..79d78a2 --- /dev/null +++ b/unit-tests/test-cases/insert-libraries-with-initializer/insert.c @@ -0,0 +1,34 @@ +/* + * 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 "base.h" + + + + +static __attribute__((constructor)) void insert() +{ + //fprintf(stderr, "insert()\n"); + setState(2); +} + diff --git a/unit-tests/test-cases/insert-libraries-with-initializer/main.c b/unit-tests/test-cases/insert-libraries-with-initializer/main.c index bb193e7..64df397 100644 --- a/unit-tests/test-cases/insert-libraries-with-initializer/main.c +++ b/unit-tests/test-cases/insert-libraries-with-initializer/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2007 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,9 +25,19 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() -int -main(int argc, char **argv, char **envp, char**appl) + +#include "base.h" + +int main() { - FAIL("initializer/constructor was not called"); - return EXIT_SUCCESS; + baseCheck(); + return EXIT_SUCCESS; } + + +static __attribute__((constructor)) void main_init() +{ + //fprintf(stderr, "main_init()\n"); + setState(5); +} + diff --git a/unit-tests/test-cases/lazy-binding-reg-params/Makefile b/unit-tests/test-cases/lazy-binding-reg-params/Makefile index f3037cd..558fc57 100644 --- a/unit-tests/test-cases/lazy-binding-reg-params/Makefile +++ b/unit-tests/test-cases/lazy-binding-reg-params/Makefile @@ -25,13 +25,13 @@ include ${TESTROOT}/include/common.makefile MACHINE = $(shell machine) -ifeq "-arch ppc" "$(ARCH)" +ifeq "ppc" "$(ARCH)" EXTRA_FLAG = -maltivec -force_cpusubtype_ALL else - ifeq "-arch ppc64" "$(ARCH)" + ifeq "ppc64" "$(ARCH)" EXTRA_FLAG = -maltivec else - ifeq "-arch i386" "$(ARCH)" + ifeq "i386" "$(ARCH)" EXTRA_FLAG = "" else ifeq "" "$(ARCH)" 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 0072780..007f11d 100644 --- a/unit-tests/test-cases/lazy-binding-reg-params/foo.h +++ b/unit-tests/test-cases/lazy-binding-reg-params/foo.h @@ -15,7 +15,7 @@ extern bool dofloattest(double,double,double,double,double,double,double,double, #if __i386__ || __x86_64__ typedef float vFloat __attribute__ ((__vector_size__ (16))); #elif __ppc__ || __ppc64__ - typedef vector float vFloat; + typedef __vector float vFloat; #endif extern bool dovectortest(vFloat, vFloat, vFloat, vFloat, vFloat); diff --git a/unit-tests/test-cases/loader_path/Makefile b/unit-tests/test-cases/loader_path/Makefile index 6b34207..694cd9d 100644 --- a/unit-tests/test-cases/loader_path/Makefile +++ b/unit-tests/test-cases/loader_path/Makefile @@ -44,7 +44,7 @@ libfoo2.dylib : foo.c main : main.c libfoo2.dylib hide/libbar.dylib hide/libfoo3.dylib - ${CC} -I${TESTROOT}/include main.c -o main hide/libbar.dylib libfoo2.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main hide/libbar.dylib libfoo2.dylib install_name_tool -change libfoo2.dylib '@loader_path/libfoo2.dylib' main install_name_tool -change libbar.dylib '@loader_path/hide/libbar.dylib' main diff --git a/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c b/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c index 46698ad..a2233c8 100644 --- a/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c +++ b/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c @@ -26,7 +26,7 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() int -main(int argc, char **argv, char **envp, char**appl) +main(int argc, const char* argv[]) { return EXIT_SUCCESS; diff --git a/unit-tests/test-cases/operator-new/Makefile b/unit-tests/test-cases/operator-new/Makefile new file mode 100644 index 0000000..fb7fe84 --- /dev/null +++ b/unit-tests/test-cases/operator-new/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +run: all + ./main + +all: main + + +main: main.cxx + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/operator-new/main.cxx b/unit-tests/test-cases/operator-new/main.cxx new file mode 100644 index 0000000..ead5c1e --- /dev/null +++ b/unit-tests/test-cases/operator-new/main.cxx @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + +// +// This test case verifies that calling operator new[] in libstdc++.dylib +// will turn around and call operator new in this main exectuable +// + +static void* ptr; + +void* operator new(size_t s) throw (std::bad_alloc) +{ + ptr = malloc(s); + return ptr; +} + + +int main() +{ + char* stuff = new char[24]; + if ( (void*)stuff == ptr ) + PASS("operator-new"); + else + FAIL("operator-new"); + 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 2cff519..37a84a4 100644 --- a/unit-tests/test-cases/partial-library-load/Makefile +++ b/unit-tests/test-cases/partial-library-load/Makefile @@ -38,7 +38,7 @@ run: all all: main test.bundle main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c test.bundle : bundle.c libfoo.dylib ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c libfoo.dylib diff --git a/unit-tests/test-cases/pie-basic/Makefile b/unit-tests/test-cases/pie-basic/Makefile new file mode 100644 index 0000000..2a2f680 --- /dev/null +++ b/unit-tests/test-cases/pie-basic/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + + +run: main + ./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; \ + + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/unit-tests/test-cases/pie-basic/main.c b/unit-tests/test-cases/pie-basic/main.c new file mode 100644 index 0000000..3a6571d --- /dev/null +++ b/unit-tests/test-cases/pie-basic/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-big/Makefile b/unit-tests/test-cases/pie-big/Makefile new file mode 100644 index 0000000..6d4d95b --- /dev/null +++ b/unit-tests/test-cases/pie-big/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + + +run: main + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-basic"; \ + else \ + echo "XFAIL pie-basic"; \ + fi; \ + + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/unit-tests/test-cases/pie-big/main.c b/unit-tests/test-cases/pie-big/main.c new file mode 100644 index 0000000..f7e3b88 --- /dev/null +++ b/unit-tests/test-cases/pie-big/main.c @@ -0,0 +1,36 @@ +/* + * 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 + +char bigarray[1500000000]; + + +int main() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/unit-tests/test-cases/pie-custom-stack/Makefile b/unit-tests/test-cases/pie-custom-stack/Makefile new file mode 100644 index 0000000..48ff01e --- /dev/null +++ b/unit-tests/test-cases/pie-custom-stack/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + + +run: main + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-custom-stack"; \ + else \ + echo "FAIL pie-custom-stack"; \ + fi; \ + + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 -Wl,-stack_size,0x10000000 + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/unit-tests/test-cases/pie-custom-stack/main.c b/unit-tests/test-cases/pie-custom-stack/main.c new file mode 100644 index 0000000..3a6571d --- /dev/null +++ b/unit-tests/test-cases/pie-custom-stack/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/prebased-performance/Makefile b/unit-tests/test-cases/prebased-performance/Makefile index f895d90..b469f9f 100644 --- a/unit-tests/test-cases/prebased-performance/Makefile +++ b/unit-tests/test-cases/prebased-performance/Makefile @@ -25,10 +25,10 @@ include ${TESTROOT}/include/common.makefile DYLIB_BASES_ADDRESS = 0x10000000 -ifeq "-arch ppc64" "$(ARCH)" +ifeq "ppc64" "$(ARCH)" DYLIB_BASES_ADDRESS = 0x300000000 else - ifeq "-arch x86_64" "$(ARCH)" + ifeq "x86_64" "$(ARCH)" DYLIB_BASES_ADDRESS = 0x300000000 endif endif diff --git a/unit-tests/test-cases/prebased-performance/foo.c b/unit-tests/test-cases/prebased-performance/foo.c index 0da036f..1836973 100644 --- a/unit-tests/test-cases/prebased-performance/foo.c +++ b/unit-tests/test-cases/prebased-performance/foo.c @@ -66,6 +66,8 @@ bool checkRebasing() ++dirtyPageCount; } + //fprintf(stderr, "dirtyPageCount=%d\n", dirtyPageCount); + // if there are too many dirty pages, then dyld is inefficient return ( dirtyPageCount < 2 ); } diff --git a/unit-tests/test-cases/flat-lookup-everywhere/Makefile b/unit-tests/test-cases/progname/Makefile similarity index 86% rename from unit-tests/test-cases/flat-lookup-everywhere/Makefile rename to unit-tests/test-cases/progname/Makefile index d1b99b5..b0ee016 100644 --- a/unit-tests/test-cases/flat-lookup-everywhere/Makefile +++ b/unit-tests/test-cases/progname/Makefile @@ -24,10 +24,10 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile run: all - DYLD_INSERT_LIBRARIES="/usr/lib/libMallocDebug.A.dylib" DYLD_FORCE_FLAT_NAMESPACE="" ./main + ./main all: - ${CC} ${CCFLAGS} -I${TESTROOT}/include -framework CoreFoundation -o main main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c clean: ${RM} ${RMFLAGS} *~ main diff --git a/unit-tests/test-cases/progname/main.c b/unit-tests/test-cases/progname/main.c new file mode 100644 index 0000000..1f05111 --- /dev/null +++ b/unit-tests/test-cases/progname/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static const char* name = NULL; + + +static void checker() __attribute__((constructor)); +static void checker() +{ + name = strdup(getprogname()); +} + + + +int +main(int argc, const char* argv[]) +{ + // verify that getprogname() works during initializers + if ( strcmp(name, getprogname()) == 0 ) + PASS("progname"); + else + FAIL("progname"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/re-export-dylib/Makefile b/unit-tests/test-cases/re-export-dylib/Makefile new file mode 100644 index 0000000..cb28183 --- /dev/null +++ b/unit-tests/test-cases/re-export-dylib/Makefile @@ -0,0 +1,62 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + + +# +# Test that the 10.4 and 10.5 ways to re-export a dylib work +# + +run : all + ./main4 + ./main5 + +all : main4 main5 + +libbar4.dylib : bar.c + gcc 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 + +main4 : main.c libfoo4.dylib + gcc 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 + +libfoo5.dylib : foo.c libbar5.dylib + gcc foo.c -dynamiclib libbar5.dylib -sub_library libbar5 -o $(PWD)/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 + + +clean: + rm -rf main4 main5 libfoo4.dylib libfoo5.dylib libbar4.dylib libbar5.dylib + \ No newline at end of file diff --git a/unit-tests/test-cases/re-export-dylib/bar.c b/unit-tests/test-cases/re-export-dylib/bar.c new file mode 100644 index 0000000..b0faf2f --- /dev/null +++ b/unit-tests/test-cases/re-export-dylib/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/unit-tests/test-cases/re-export-dylib/foo.c b/unit-tests/test-cases/re-export-dylib/foo.c new file mode 100644 index 0000000..2486c7a --- /dev/null +++ b/unit-tests/test-cases/re-export-dylib/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/unit-tests/test-cases/re-export-dylib/main.c b/unit-tests/test-cases/re-export-dylib/main.c new file mode 100644 index 0000000..2d54c74 --- /dev/null +++ b/unit-tests/test-cases/re-export-dylib/main.c @@ -0,0 +1,39 @@ +/* + * 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 "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-dylib"); + return 0; +} diff --git a/unit-tests/test-cases/re-export-framework/Makefile b/unit-tests/test-cases/re-export-framework/Makefile new file mode 100644 index 0000000..b474053 --- /dev/null +++ b/unit-tests/test-cases/re-export-framework/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 + +PWD = `pwd` + + +# +# Test that the 10.4 and 10.5 ways to re-export a framework work +# + +run : 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 + +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 + +main4 : main.c Foo4.framework/Foo4 + gcc 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 + +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 + +main5 : main.c Foo5.framework/Foo5 + gcc main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 + + + + +clean: + rm -rf main4 Foo4.framework Bar4.framework main5 Foo5.framework Bar5.framework + \ No newline at end of file diff --git a/unit-tests/test-cases/re-export-framework/bar.c b/unit-tests/test-cases/re-export-framework/bar.c new file mode 100644 index 0000000..b0faf2f --- /dev/null +++ b/unit-tests/test-cases/re-export-framework/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/unit-tests/test-cases/re-export-framework/foo.c b/unit-tests/test-cases/re-export-framework/foo.c new file mode 100644 index 0000000..2486c7a --- /dev/null +++ b/unit-tests/test-cases/re-export-framework/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/unit-tests/test-cases/re-export-framework/main.c b/unit-tests/test-cases/re-export-framework/main.c new file mode 100644 index 0000000..7ad3fed --- /dev/null +++ b/unit-tests/test-cases/re-export-framework/main.c @@ -0,0 +1,39 @@ +/* + * 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 "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-framework"); + return 0; +} diff --git a/unit-tests/test-cases/re-export-sub-framework/Makefile b/unit-tests/test-cases/re-export-sub-framework/Makefile new file mode 100644 index 0000000..1b761f5 --- /dev/null +++ b/unit-tests/test-cases/re-export-sub-framework/Makefile @@ -0,0 +1,66 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + + +# +# Test that the 10.4 and 10.5 ways to re-export a sub framework work +# + +run : 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 + +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 + +main4 : main.c Foo4.framework/Foo4 + gcc 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 + +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 + +main5 : main.c Foo5.framework/Foo5 + gcc main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5 + + + +clean: + rm -rf main4 Foo4.framework Bar4.framework main5 Foo5.framework Bar5.framework + \ No newline at end of file diff --git a/unit-tests/test-cases/re-export-sub-framework/bar.c b/unit-tests/test-cases/re-export-sub-framework/bar.c new file mode 100644 index 0000000..b0faf2f --- /dev/null +++ b/unit-tests/test-cases/re-export-sub-framework/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/unit-tests/test-cases/re-export-sub-framework/foo.c b/unit-tests/test-cases/re-export-sub-framework/foo.c new file mode 100644 index 0000000..2486c7a --- /dev/null +++ b/unit-tests/test-cases/re-export-sub-framework/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/unit-tests/test-cases/re-export-sub-framework/main.c b/unit-tests/test-cases/re-export-sub-framework/main.c new file mode 100644 index 0000000..fe2ae5e --- /dev/null +++ b/unit-tests/test-cases/re-export-sub-framework/main.c @@ -0,0 +1,39 @@ +/* + * 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 "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-sub-framework"); + return 0; +} diff --git a/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile new file mode 100644 index 0000000..efd6319 --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +run: all + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -o main main.c + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.cxx + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + + diff --git a/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx new file mode 100644 index 0000000..9bf26a8 --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + +// +// This test case verifies that calling operator new[] in libstdc++.dylib +// will turn around and call operator new in this main exectuable +// + +static void* ptr; + +void* operator new(size_t s) throw (std::bad_alloc) +{ + ptr = malloc(s); + return ptr; +} + + +int check() +{ + char* stuff = new char[24]; + if ( (void*)stuff == ptr ) + PASS("operator-new"); + else + FAIL("operator-new"); + return 0; +} + +int x = check(); // runs when library is loaded + +// add this so WEAK_DEFINES is set, so dyld searchs this image +int __attribute__((weak)) junk = 2; diff --git a/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c new file mode 100644 index 0000000..4579fe4 --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c @@ -0,0 +1,12 @@ + +#include + +int main() +{ + // dynamically load libfoo.dylib which depends on libstdc++.dylib + // being re-bound to libfoo's operator new. + dlopen("libfoo.dylib", RTLD_LAZY); + return 0; +} + + 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 new file mode 100644 index 0000000..c8b9346 --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-override/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +run: all + export DYLD_LIBRARY_PATH=`pwd` && ./main + +all: main libSystem.B.dylib + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c -framework Cocoa + +libSystem.B.dylib: /usr/lib/libSystem.B.dylib + cp /usr/lib/libSystem.B.dylib ./libSystem.B.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libSystem.B.dylib libfoo.dylib + + 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 new file mode 100644 index 0000000..322485e --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-override/foo.c @@ -0,0 +1,18 @@ + +#include +#include + +#include "test.h" + +void __attribute__((constructor)) init() +{ + uintptr_t libSysFuncAddr = (uintptr_t)&strcmp; + uintptr_t cfFuncAddr = (uintptr_t)&CFStringGetTypeID; + if ( cfFuncAddr - libSysFuncAddr < 256*1024*1024 ) + PASS("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 new file mode 100644 index 0000000..cc404ed --- /dev/null +++ b/unit-tests/test-cases/read-only-import-shared-cache-override/main.c @@ -0,0 +1,10 @@ +#include + +int main() +{ + // dynamically load libfoo.dylib which depends on libstdc++.dylib + // being re-bound to libfoo's operator new. + dlopen("libfoo.dylib", RTLD_LAZY); + return 0; +} + diff --git a/unit-tests/test-cases/read-only-stubs/Makefile b/unit-tests/test-cases/read-only-stubs/Makefile new file mode 100644 index 0000000..d3a63ab --- /dev/null +++ b/unit-tests/test-cases/read-only-stubs/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include -dynamiclib foo.c libbar.dylib -o libfoo.dylib + +libbar.dylib: bar.c + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/read-only-stubs/bar.c b/unit-tests/test-cases/read-only-stubs/bar.c new file mode 100644 index 0000000..d9a1c20 --- /dev/null +++ b/unit-tests/test-cases/read-only-stubs/bar.c @@ -0,0 +1,35 @@ +/* + * 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() + + +int barData = 1; + +void bar() +{ +} + + diff --git a/unit-tests/test-cases/read-only-stubs/foo.c b/unit-tests/test-cases/read-only-stubs/foo.c new file mode 100644 index 0000000..01fc9a8 --- /dev/null +++ b/unit-tests/test-cases/read-only-stubs/foo.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void bar(); +extern int barData; + + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ +#if __LP64__ + uint64_t size; +#else + uint32_t size; +#endif + uintptr_t slide = (uintptr_t)&_mh_dylib_header; // assume dylib is zero-base so slide == load address +#if __i386__ + return getsectdatafromheader(&_mh_dylib_header, "__IMPORT", "__jump_table", &size) + slide; +#elif __ppc__ + return getsectdatafromheader(&_mh_dylib_header, "TEXT", "__picsymbolstub1", &size) + slide; +#elif __ppc64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__picsymbolstub1", &size) + slide; +#elif __x86_64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide; +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) { + FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr); + exit(0); + } +} + +int fooData = 1; + +void foo() +{ + void* stubAddr = getStubAddr(); + checkStubs(stubAddr); + barData = 0; + bar(); + checkStubs(stubAddr); +} + + diff --git a/unit-tests/test-cases/read-only-stubs/main.c b/unit-tests/test-cases/read-only-stubs/main.c new file mode 100644 index 0000000..3c3dca9 --- /dev/null +++ b/unit-tests/test-cases/read-only-stubs/main.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); +extern int fooData; + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + //fprintf(stderr, "result=%X, info.protection=%X\n", result, info.protection); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ + unsigned long size; +#if __i386__ + return getsectdata("__IMPORT", "__jump_table", &size); +#elif __ppc__ + void* p = getsectdata("__TEXT", "__picsymbolstub1", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __ppc64__ + return getsectdata("__TEXT", "__picsymbolstub1", &size); +#elif __x86_64__ + return getsectdata("__TEXT", "__symbol_stub1", &size); +#else + #error unknown arch +#endif +} + + +static void checkStubs(void* addr) +{ + vm_prot_t perm = getPermission(addr); + if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) { + FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr); + exit(0); + } +} + + +int main() +{ + void* stubAddr = getStubAddr(); + checkStubs(stubAddr); + fooData = 1; + foo(); + checkStubs(stubAddr); + PASS("read-only-stubs"); + 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 new file mode 100644 index 0000000..a011834 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# 1) a main executable built with -rpath run with DYLD_FALLBACK_LIBRARY_PATH and linked rpath wins +# 2) a main executable built without -rpath run with DYLD_FALLBACK_LIBRARY_PATH and dylib found +# + + +run: all + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/bad && ./main + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/good && ./main2 + +all: main main2 bad/libfoo.dylib + + +good/libfoo.dylib : foo.c + mkdir -p good + ${CC} foo.c -dynamiclib -o good/libfoo.dylib -install_name @rpath/libfoo.dylib + +bad/libfoo.dylib : foo.c + mkdir -p bad + ${CC} foo.c -DBAD -dynamiclib -o bad/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c good/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main good/libfoo.dylib -Wl,-rpath -Wl,@loader_path/good + +main2 : main.c good/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main2 good/libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main2 good bad diff --git a/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..a8fe5b0 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c @@ -0,0 +1,30 @@ +/* + * 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@ + */ +int foo() +{ +#if BAD + return 0; +#else + return 1; +#endif +} diff --git a/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 0000000..72afbf7 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.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 +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-DYLD_FALLBACK_LIBRARY_PATH"); + else + FAIL("rpath-DYLD_FALLBACK_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..153f718 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable run with DYLD_LIBRARY_PATH will override its rpath +# + + +run: all + export DYLD_LIBRARY_PATH=`pwd`/hide1 && ./main + +all: main hide2/libfoo.dylib hide1/libfoo.dylib + + +hide1/libfoo.dylib : foo.c + mkdir -p hide1 + ${CC} foo.c -dynamiclib -o hide1/libfoo.dylib -install_name @rpath/libfoo.dylib + +hide2/libfoo.dylib : foo.c + mkdir -p hide2 + ${CC} foo.c -DBAD -dynamiclib -o hide2/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c hide2/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide2/libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide2 + +clean: + ${RM} ${RMFLAGS} *~ main hide1 hide2 diff --git a/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..a8fe5b0 --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,30 @@ +/* + * 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@ + */ +int foo() +{ +#if BAD + return 0; +#else + return 1; +#endif +} diff --git a/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..01a144b --- /dev/null +++ b/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.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 +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-DYLD_LIBRARY_PATH"); + else + FAIL("rpath-DYLD_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..ba5400d --- /dev/null +++ b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable run with LD_LIBRARY_PATH can locate a dylib it links against +# + + +run: all + export LD_LIBRARY_PATH=`pwd`/hide/hole && ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..784dee3 --- /dev/null +++ b/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-LD_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-basic/Makefile b/unit-tests/test-cases/rpath-basic/Makefile new file mode 100644 index 0000000..536d651 --- /dev/null +++ b/unit-tests/test-cases/rpath-basic/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath used to locate a dylib it links against +# + + +run: all + ./main + +all: main + + +hide/hole/libbar.dylib : bar.c + mkdir -p hide/hole + ${CC} bar.c -dynamiclib -o hide/hole/libbar.dylib -install_name @rpath/libbar.dylib + +hide/hole/libfoo.dylib : foo.c hide/hole/libbar.dylib + ${CC} foo.c hide/hole/libbar.dylib -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,`pwd`/hide/hole + +clean: + ${RM} -rf *~ main hide diff --git a/unit-tests/test-cases/rpath-basic/bar.c b/unit-tests/test-cases/rpath-basic/bar.c new file mode 100644 index 0000000..b72a1a5 --- /dev/null +++ b/unit-tests/test-cases/rpath-basic/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/unit-tests/test-cases/rpath-basic/foo.c b/unit-tests/test-cases/rpath-basic/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/rpath-basic/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-basic/main.c b/unit-tests/test-cases/rpath-basic/main.c new file mode 100644 index 0000000..bf6c7fa --- /dev/null +++ b/unit-tests/test-cases/rpath-basic/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-basic"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile b/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile new file mode 100644 index 0000000..d94f47d --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath calls into a dylib which calls +# dlopen(). The -rpath from the dylib should be used to +# find the dlopen path. +# + + +run: all + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include -Wl,-rpath -Wl,`pwd`/hide/hole + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide libbar.dylib diff --git a/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c b/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c new file mode 100644 index 0000000..17ab4f6 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.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 "test.h" + +void bar() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } +} + + diff --git a/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c b/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c b/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c new file mode 100644 index 0000000..1eb7429 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c @@ -0,0 +1,37 @@ +/* + * 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 "test.h" + +extern void bar(); + +int main() +{ + bar(); + + PASS("rpath-dlopen-in-dylib"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-dlopen-indirect/Makefile b/unit-tests/test-cases/rpath-dlopen-indirect/Makefile new file mode 100644 index 0000000..b679f77 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-indirect/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath calls into a dylib which calls +# dlopen(). The -rpath from the main executable should be used to +# find the dlopen path. +# + + +run: all + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,`pwd`/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide libbar.dylib diff --git a/unit-tests/test-cases/rpath-dlopen-indirect/bar.c b/unit-tests/test-cases/rpath-dlopen-indirect/bar.c new file mode 100644 index 0000000..160109b --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-indirect/bar.c @@ -0,0 +1,23 @@ + +#include +#include +#include + +#include "test.h" + +void bar() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } +} + + diff --git a/unit-tests/test-cases/rpath-dlopen-indirect/foo.c b/unit-tests/test-cases/rpath-dlopen-indirect/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-indirect/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-dlopen-indirect/main.c b/unit-tests/test-cases/rpath-dlopen-indirect/main.c new file mode 100644 index 0000000..ba7efb6 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-indirect/main.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 +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + + PASS("rpath-dlopen-indirect"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-dlopen-leak/Makefile b/unit-tests/test-cases/rpath-dlopen-leak/Makefile new file mode 100644 index 0000000..2261028 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-leak/Makefile @@ -0,0 +1,64 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with using @loader_path based rpaths +# + +# leaks does not work on rosetta processes +EMULATED = 0 +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + EMULATED = 1 + endif +endif + + +run: all + 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"; \ + ${TESTROOT}/bin/exit-zero-pass.pl "rpath-dlopen-leak" "rpath-dlopen-leak" "./main.bad | grep '0 leaks for 0 total leaked bytes' > /dev/null"; \ + else \ + echo "XFAIL rpath-dlopen-leak"; \ + fi; + +all: main main.bad + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@loader_path/hide/hole + +main.bad : main.c hide/hole/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main.bad -Wl,-rpath -Wl,@loader_path/bad + +clean: + ${RM} ${RMFLAGS} *~ main main.bad hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-dlopen-leak/foo.c b/unit-tests/test-cases/rpath-dlopen-leak/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-leak/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-dlopen-leak/main.c b/unit-tests/test-cases/rpath-dlopen-leak/main.c new file mode 100644 index 0000000..b33ceca --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen-leak/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "leaks %u\n", getpid()); + system(cmd); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-dlopen/Makefile b/unit-tests/test-cases/rpath-dlopen/Makefile new file mode 100644 index 0000000..ce34746 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath and uses dlopen can find a dylib +# + + +run: all + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,`pwd`/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-dlopen/foo.c b/unit-tests/test-cases/rpath-dlopen/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-dlopen/main.c b/unit-tests/test-cases/rpath-dlopen/main.c new file mode 100644 index 0000000..d413906 --- /dev/null +++ b/unit-tests/test-cases/rpath-dlopen/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen: %s", dlerror()); + return EXIT_SUCCESS; + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen: %s", dlerror()); + return EXIT_SUCCESS; + } + + PASS("rpath-dlopen"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-executable_path/Makefile b/unit-tests/test-cases/rpath-executable_path/Makefile new file mode 100644 index 0000000..76dde9f --- /dev/null +++ b/unit-tests/test-cases/rpath-executable_path/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @executable_path. +# The @executable_path in the rpath should expand at runtime to the directory +# of the main executable. +# + + +run: all + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib hide/hole/libfoo.dylib + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,@executable_path/hide/hole + +clean: + ${RM} -rf *~ main hide libbar.dylib diff --git a/unit-tests/test-cases/rpath-executable_path/bar.c b/unit-tests/test-cases/rpath-executable_path/bar.c new file mode 100644 index 0000000..31a758c --- /dev/null +++ b/unit-tests/test-cases/rpath-executable_path/bar.c @@ -0,0 +1,8 @@ +extern void foo(); + +void bar() +{ + foo(); +} + + diff --git a/unit-tests/test-cases/rpath-executable_path/foo.c b/unit-tests/test-cases/rpath-executable_path/foo.c new file mode 100644 index 0000000..1624757 --- /dev/null +++ b/unit-tests/test-cases/rpath-executable_path/foo.c @@ -0,0 +1,5 @@ + + +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-executable_path/main.c b/unit-tests/test-cases/rpath-executable_path/main.c new file mode 100644 index 0000000..4612793 --- /dev/null +++ b/unit-tests/test-cases/rpath-executable_path/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + PASS("rpath-executable_path"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-indirect-suid/Makefile b/unit-tests/test-cases/rpath-indirect-suid/Makefile new file mode 100644 index 0000000..a1ec99e --- /dev/null +++ b/unit-tests/test-cases/rpath-indirect-suid/Makefile @@ -0,0 +1,71 @@ +## +# 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 + + +# +# a setuid main executable linked with -rpath links against a dylib +# that uses rpath to find another dylib. It is an error if +# LC_RPATH uses @loader_path or a relative path, but ok if it is an absolute path +# + + +run: all + ./main || echo "FAIL rpath-indirect-suid absolute path" + ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid @loader_path path" "rpath-indirect-suid @loader_path path" ./main_bad1 + ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid relative path" "rpath-indirect-suid relative path" ./main_bad2 + ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid @rpath spoof" "rpath-indirect-suid @rpath spoof" ./main_bad3 + +all: main main_bad1 main_bad2 main_bad3 + +hide/hole/libbar.dylib : bar.c + mkdir -p hide/hole + ${CC} bar.c -dynamiclib -o hide/hole/libbar.dylib -install_name @rpath/libbar.dylib + +libfoo.dylib : foo.c hide/hole/libbar.dylib + ${CC} foo.c -dynamiclib -o "`pwd`/libfoo.dylib" hide/hole/libbar.dylib + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,`pwd`/hide/hole + sudo chown root main + sudo chmod 4755 main + +main_bad1 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad1 libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide/hole + sudo chown root main_bad1 + sudo chmod 4755 main_bad1 + +main_bad2 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad2 libfoo.dylib -Wl,-rpath -Wl,hide/hole + sudo chown root main_bad2 + sudo chmod 4755 main_bad2 + +main_bad3 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad3 libfoo.dylib + ln -s hide/hole @rpath + sudo chown root main_bad3 + sudo chmod 4755 main_bad3 + +clean: + ${RM} ${RMFLAGS} *~ main main_bad1 main_bad2 main_bad3 hide libfoo.dylib @rpath diff --git a/unit-tests/test-cases/rpath-indirect-suid/bar.c b/unit-tests/test-cases/rpath-indirect-suid/bar.c new file mode 100644 index 0000000..b1b8fd7 --- /dev/null +++ b/unit-tests/test-cases/rpath-indirect-suid/bar.c @@ -0,0 +1,6 @@ + +void bar() +{ +} + + diff --git a/unit-tests/test-cases/rpath-indirect-suid/foo.c b/unit-tests/test-cases/rpath-indirect-suid/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/rpath-indirect-suid/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-indirect-suid/main.c b/unit-tests/test-cases/rpath-indirect-suid/main.c new file mode 100644 index 0000000..bdea9f9 --- /dev/null +++ b/unit-tests/test-cases/rpath-indirect-suid/main.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); +#if DEFAULT_FAIL + FAIL("rpath-indirect-setuid"); +#else + PASS("rpath-indirect-setuid"); +#endif + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile b/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile new file mode 100644 index 0000000..d83c5e8 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @loader_path. +# The @loader_path in the rpath should expand at runtime to the directory +# of the main executable during calls to dlopen() +# + + +run: all + ./main + +all: main + +hide/hole/libbaz.dylib : baz.c + mkdir -p hide/hole + ${CC} baz.c -dynamiclib -o hide/hole/libbaz.dylib -install_name @rpath/libbaz.dylib + +libbar.dylib : bar.c hide/hole/libbaz.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib hide/hole/libbaz.dylib + +libfoo.dylib : foo.c libbar.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@loader_path/hide/hole libfoo.dylib + +clean: + ${RM} -rf *~ main hide libbar.dylib libfoo.dylib \ No newline at end of file diff --git a/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c b/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c new file mode 100644 index 0000000..f9c0742 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c @@ -0,0 +1,8 @@ +extern void baz(); + +void bar() +{ + baz(); +} + + diff --git a/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c b/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c new file mode 100644 index 0000000..0793d97 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c @@ -0,0 +1,6 @@ + +void baz() +{ +} + + diff --git a/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c b/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c new file mode 100644 index 0000000..92d2257 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c @@ -0,0 +1,8 @@ +#include +#include +#include + +bool foo() +{ + return (dlopen("./libbar.dylib", RTLD_NOW) != NULL); +} diff --git a/unit-tests/test-cases/rpath-loader_path-dlopen/main.c b/unit-tests/test-cases/rpath-loader_path-dlopen/main.c new file mode 100644 index 0000000..2b1cc25 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path-dlopen/main.c @@ -0,0 +1,39 @@ +/* + * 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 "test.h" + +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-loader_path-dlopen"); + else + FAIL("rpath-loader_path-dlopen: %s", dlerror()); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-loader_path/Makefile b/unit-tests/test-cases/rpath-loader_path/Makefile new file mode 100644 index 0000000..6bd18a9 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @loader_path. +# The @loader_path in the rpath should expand at runtime to the directory +# of the binary. +# + + +run: all + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/unit-tests/test-cases/rpath-loader_path/foo.c b/unit-tests/test-cases/rpath-loader_path/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-loader_path/main.c b/unit-tests/test-cases/rpath-loader_path/main.c new file mode 100644 index 0000000..bf6c7fa --- /dev/null +++ b/unit-tests/test-cases/rpath-loader_path/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-basic"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-nesting/Makefile b/unit-tests/test-cases/rpath-nesting/Makefile new file mode 100644 index 0000000..0364549 --- /dev/null +++ b/unit-tests/test-cases/rpath-nesting/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# The main executable supplies an rpath. libfoo.dylib supplies an +# rpath. libfoo.dylib needs libbar.dylib and libbaz.dylib. One +# is found from the main executable's rpath and one from libfoo.dylib's +# rpath. +# + + +run: all + ./main + +all: main + +hide1/libbar.dylib : bar.c + mkdir -p hide1 + ${CC} bar.c -dynamiclib -o hide1/libbar.dylib -install_name @rpath/libbar.dylib + +hide2/libbaz.dylib : baz.c + mkdir -p hide2 + ${CC} baz.c -dynamiclib -o hide2/libbaz.dylib -install_name @rpath/libbaz.dylib + +libfoo.dylib : foo.c hide1/libbar.dylib hide2/libbaz.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib hide1/libbar.dylib hide2/libbaz.dylib -Wl,-rpath -Wl,`pwd`/hide2 + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,`pwd`/hide1 + +clean: + ${RM} ${RMFLAGS} *~ main hide1 hide2 libfoo.dylib diff --git a/unit-tests/test-cases/rpath-nesting/bar.c b/unit-tests/test-cases/rpath-nesting/bar.c new file mode 100644 index 0000000..d47ba49 --- /dev/null +++ b/unit-tests/test-cases/rpath-nesting/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +void bar() +{ +} + + diff --git a/unit-tests/test-cases/rpath-nesting/baz.c b/unit-tests/test-cases/rpath-nesting/baz.c new file mode 100644 index 0000000..dc61b62 --- /dev/null +++ b/unit-tests/test-cases/rpath-nesting/baz.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +void baz() +{ +} + + diff --git a/unit-tests/test-cases/rpath-nesting/foo.c b/unit-tests/test-cases/rpath-nesting/foo.c new file mode 100644 index 0000000..7b46c28 --- /dev/null +++ b/unit-tests/test-cases/rpath-nesting/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@ + */ + +extern void bar(); +extern void baz(); + +void foo() +{ + bar(); + baz(); +} diff --git a/unit-tests/test-cases/rpath-nesting/main.c b/unit-tests/test-cases/rpath-nesting/main.c new file mode 100644 index 0000000..47d3ae2 --- /dev/null +++ b/unit-tests/test-cases/rpath-nesting/main.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 +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + + PASS("rpath-nesting"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/shared-cache-symlink/Makefile b/unit-tests/test-cases/shared-cache-symlink/Makefile new file mode 100644 index 0000000..92ea1b0 --- /dev/null +++ b/unit-tests/test-cases/shared-cache-symlink/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +run: all + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/shared-cache-symlink/main.c b/unit-tests/test-cases/shared-cache-symlink/main.c new file mode 100644 index 0000000..ab1c6b6 --- /dev/null +++ b/unit-tests/test-cases/shared-cache-symlink/main.c @@ -0,0 +1,76 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libz.1.dylib should be in the shared cache +// libz.dylib is a symlink to libz.dylib +// we want to verify that dlopening the symlink name will use the one in the shared cache + + +int main() +{ + void* libzHandle = dlopen("/usr/lib/libz.dylib", RTLD_LAZY); + if ( libzHandle == NULL ) { + FAIL("shared-cache-symlink: dlopen(/usr/lib/libz.dylib, RTLD_LAZY) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get address of strcmp() + void* sym = dlsym(libzHandle, "inflate"); + if ( sym == NULL ) { + FAIL("shared-cache-symlink: dlsym(handle, \"inflate\") failed"); + return EXIT_SUCCESS; + } + + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("shared-cache-symlink: dladdr(sym, xx) failed"); + return EXIT_SUCCESS; + } + + // walk images to get slide + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + if ( _dyld_get_image_header(i) == info.dli_fbase ) { + if ( _dyld_get_image_vmaddr_slide(i) == 0 ) { + // images in shared cache have a slide of zero + PASS("shared-cache-symlink"); + return EXIT_SUCCESS; + } + else { + FAIL("shared-cache-symlink: libz.dylib not loaded from shared cache"); + return EXIT_SUCCESS; + } + } + } + + FAIL("shared-cache-symlink libz.dylib not found"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/suid-environ/Makefile b/unit-tests/test-cases/suid-environ/Makefile index 07dcdef..8ef3c6d 100644 --- a/unit-tests/test-cases/suid-environ/Makefile +++ b/unit-tests/test-cases/suid-environ/Makefile @@ -29,7 +29,7 @@ run: all all: main main: main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c sudo chown root main sudo chmod 4755 main diff --git a/unit-tests/test-cases/suid-executable_path/Makefile b/unit-tests/test-cases/suid-executable_path/Makefile new file mode 100644 index 0000000..1dd7f1e --- /dev/null +++ b/unit-tests/test-cases/suid-executable_path/Makefile @@ -0,0 +1,82 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# Use of @exectuable_path in setuid binaries is not allowed +# Use of @loader_path in setuid binaries is not allowed +# Use of relative paths in setuid binaries is not allowed +# + +run: all + ./main_exe "setuid-executable_path" || echo "FAIL setuid-executable_path @executable_path not setuid" + ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path @executable_path" "setuid-executable_path @executable_path" ./main_exe-suid "setuid-executable_path" + ./main_loader "setuid-executable_path" || echo "FAIL setuid-executable_path @loader_path not setuid" + ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path @loader_path" "setuid-executable_path @loader_path" ./main_loader-suid "setuid-executable_path" + ./main_rel "setuid-executable_path" || echo "FAIL setuid-executable_path relative path not setuid" + ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path relative path" "setuid-executable_path relative path" ./main_rel-suid "setuid-executable_path" + + + +all: main_exe main_exe-suid main_loader main_loader-suid main_rel main_rel-suid + +dir1/libfoo.dylib : foo.c + mkdir -p dir1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir1/libfoo.dylib -install_name @executable_path/dir1/libfoo.dylib + +dir2/libbar.dylib : foo.c + mkdir -p dir2 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir2/libbar.dylib -install_name @loader_path/dir2/libbar.dylib + +dir3/libbaz.dylib : foo.c + mkdir -p dir3 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o ./dir3/libbaz.dylib + +main_exe: main.c dir1/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_exe main.c dir1/libfoo.dylib + cp main_exe main_exe-suid + sudo chown root main_exe-suid + sudo chmod 4755 main_exe-suid + +main_loader: main.c dir2/libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_loader main.c dir2/libbar.dylib + cp main_loader main_loader-suid + sudo chown root main_loader-suid + sudo chmod 4755 main_loader-suid + +main_rel: main.c dir3/libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_rel main.c dir3/libbaz.dylib + cp main_rel main_rel-suid + sudo chown root main_rel-suid + sudo chmod 4755 main_rel-suid + + + + +clean: + ${RM} ${RMFLAGS} *~ main_exe main_exe-suid main_loader main_loader-suid main_rel main_rel-suid dir1 dir2 dir3 + diff --git a/unit-tests/test-cases/suid-executable_path/foo.c b/unit-tests/test-cases/suid-executable_path/foo.c new file mode 100644 index 0000000..d2b44c4 --- /dev/null +++ b/unit-tests/test-cases/suid-executable_path/foo.c @@ -0,0 +1,5 @@ + + +int foo() { return 1; } + + diff --git a/unit-tests/test-cases/suid-executable_path/main.c b/unit-tests/test-cases/suid-executable_path/main.c new file mode 100644 index 0000000..aa63e85 --- /dev/null +++ b/unit-tests/test-cases/suid-executable_path/main.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 // 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(); + if ( issetugid() ) + FAIL(argv[1]); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/template/Makefile b/unit-tests/test-cases/template/Makefile new file mode 100644 index 0000000..b0ee016 --- /dev/null +++ b/unit-tests/test-cases/template/Makefile @@ -0,0 +1,34 @@ +## +# 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 + +run: all + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/flat-lookup-everywhere/main.c b/unit-tests/test-cases/template/main.c similarity index 88% rename from unit-tests/test-cases/flat-lookup-everywhere/main.c rename to unit-tests/test-cases/template/main.c index fb2c2a1..083736b 100644 --- a/unit-tests/test-cases/flat-lookup-everywhere/main.c +++ b/unit-tests/test-cases/template/main.c @@ -22,14 +22,12 @@ */ #include // fprintf(), NULL #include // exit(), EXIT_SUCCESS -#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() int -main() +main(int argc, const char* argv[]) { - CFStringRef string = CFStringCreateWithFormat(NULL, NULL, CFSTR("")); - PASS("flat-lookup-everywhere"); + PASS("template"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/unloadable-library-residue/Makefile b/unit-tests/test-cases/unloadable-library-residue/Makefile index 77e16cc..fd4ad9b 100644 --- a/unit-tests/test-cases/unloadable-library-residue/Makefile +++ b/unit-tests/test-cases/unloadable-library-residue/Makefile @@ -38,7 +38,7 @@ run: all all: main libfoo.dylib main : main.c - ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c libfoo.dylib : foo.c libbar_missing.dylib libbar.dylib ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib libbar.dylib diff --git a/unit-tests/test-cases/unloadable-library-residue/main.c b/unit-tests/test-cases/unloadable-library-residue/main.c index 2a87dad..fc39c25 100644 --- a/unit-tests/test-cases/unloadable-library-residue/main.c +++ b/unit-tests/test-cases/unloadable-library-residue/main.c @@ -39,6 +39,7 @@ int main() uint32_t count = _dyld_image_count(); for(uint32_t i=0; i < count; ++i) { const char* name = _dyld_get_image_name(i); + //fprintf(stderr, "%s\n", name); if (strcmp(name, "libfoo.dylib") == 0 ) { FAIL("library-cant-be-bound: libfoo.dylib shows up in list of images"); return 0; diff --git a/unit-tests/test-cases/weak-override/main.c b/unit-tests/test-cases/weak-override/main.c index d958a78..372c29a 100644 --- a/unit-tests/test-cases/weak-override/main.c +++ b/unit-tests/test-cases/weak-override/main.c @@ -29,6 +29,9 @@ // it calls myfunc() extern int foo(); +// add this so WEAK_DEFINES is set, so dyld searchs this image +int __attribute__((weak)) junk = 2; + int main() { if ( foo() == 10 ) diff --git a/unit-tests/test-cases/weak-symbol-flat/Makefile b/unit-tests/test-cases/weak-symbol-flat/Makefile index f49610d..ba1d7aa 100644 --- a/unit-tests/test-cases/weak-symbol-flat/Makefile +++ b/unit-tests/test-cases/weak-symbol-flat/Makefile @@ -36,7 +36,7 @@ libfoo_missing.dylib : foo.c ${CC} -dynamiclib -o libfoo_missing.dylib foo.c -install_name libfoo.dylib main: main.c libfoo.dylib - export MACOSX_DEPLOYMENT_TARGET=10.2 && ${CC} -I${TESTROOT}/include -L. -lfoo -o main main.c -flat_namespace + ${CC} -I${TESTROOT}/include -mmacosx-version-min=10.2 -L. -lfoo -o main main.c -flat_namespace clean: ${RM} ${RMFLAGS} *~ libfoo.dylib libfoo_missing.dylib main diff --git a/unit-tests/test-cases/zero-fill-segment/Makefile b/unit-tests/test-cases/zero-fill-segment/Makefile new file mode 100644 index 0000000..513cd75 --- /dev/null +++ b/unit-tests/test-cases/zero-fill-segment/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +run: all + ./main + +all: main + + +main: main.c foo.bundle + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c zero.s + ${CC} ${CCFLAGS} -bundle foo.c zero.s -o foo.bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/unit-tests/test-cases/zero-fill-segment/foo.c b/unit-tests/test-cases/zero-fill-segment/foo.c new file mode 100644 index 0000000..4202b55 --- /dev/null +++ b/unit-tests/test-cases/zero-fill-segment/foo.c @@ -0,0 +1,8 @@ + +extern int foo[]; + +int getfoo(int x) +{ + return foo[x]; +} + diff --git a/unit-tests/test-cases/zero-fill-segment/main.c b/unit-tests/test-cases/zero-fill-segment/main.c new file mode 100644 index 0000000..0676756 --- /dev/null +++ b/unit-tests/test-cases/zero-fill-segment/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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // dlopen() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("zero-fill-segment: dlopen(\"%s\") failed with: %s", "foo.bundle", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "getfoo"); + if ( sym == NULL ) { + FAIL("zero-fill-segment: dlsym(handle, \"getfoo\") failed"); + exit(0); + } + + dlclose(handle); + + PASS("zero-fill-segment"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/zero-fill-segment/zero.s b/unit-tests/test-cases/zero-fill-segment/zero.s new file mode 100644 index 0000000..cfb52ea --- /dev/null +++ b/unit-tests/test-cases/zero-fill-segment/zero.s @@ -0,0 +1,5 @@ + +.globl _foo +.zerofill __MYZERO, __zero, _foo, 8192 + + diff --git a/unit-tests/test-cases/zero-length-segment/Makefile b/unit-tests/test-cases/zero-length-segment/Makefile new file mode 100644 index 0000000..9d8b5cb --- /dev/null +++ b/unit-tests/test-cases/zero-length-segment/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +run: all + export DYLD_PRINT_SEGMENTS=1 && ./main 2> segments.log + grep __FOOBAR segments.log > /dev/null || echo "PASS zero-length-segment" + (grep __FOOBAR segments.log > /dev/null && echo "FAIL zero-length-segment") || /usr/bin/true + + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-sectcreate -Wl,__FOOBAR -Wl,__empty -Wl,/dev/null + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib segments.log + diff --git a/unit-tests/test-cases/zero-length-segment/foo.c b/unit-tests/test-cases/zero-length-segment/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/unit-tests/test-cases/zero-length-segment/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/unit-tests/test-cases/zero-length-segment/main.c b/unit-tests/test-cases/zero-length-segment/main.c new file mode 100644 index 0000000..f5a0e5d --- /dev/null +++ b/unit-tests/test-cases/zero-length-segment/main.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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + return EXIT_SUCCESS; +} -- 2.45.2