From 2fd3f4e8fd2c2f2c7d149fbea471d8b3fb56f15a Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 29 Oct 2013 00:03:35 +0000 Subject: [PATCH] dyld-239.3.tar.gz --- doc/ReleaseNotes.txt | 2 + doc/man/man3/dlopen.3 | 56 +- doc/man/man3/dlsym.3 | 1 - dyld.xcodeproj/project.pbxproj | 193 +++- include/dlfcn.h | 4 +- include/mach-o/dyld.h | 2 +- include/mach-o/dyld_debug.h | 247 ----- include/mach-o/dyld_images.h | 16 +- include/mach-o/dyld_priv.h | 42 + include/objc-shared-cache.h | 44 +- launch-cache/CacheFileAbstraction.hpp | 54 ++ launch-cache/MachOBinder.hpp | 195 ++-- launch-cache/MachOLayout.hpp | 25 +- launch-cache/MachORebaser.hpp | 2 +- launch-cache/dsc_extractor.cpp | 126 ++- launch-cache/dsc_iterator.cpp | 130 ++- launch-cache/dsc_iterator.h | 48 +- launch-cache/dyld_cache_format.h | 21 + launch-cache/dyld_shared_cache_util.cpp | 575 ++++++------ launch-cache/update_dyld_shared_cache.cpp | 681 +++++++++----- src/ImageLoader.cpp | 120 ++- src/ImageLoader.h | 130 ++- src/ImageLoaderMachO.cpp | 188 +++- src/ImageLoaderMachO.h | 10 +- src/ImageLoaderMachOClassic.cpp | 20 +- src/ImageLoaderMachOClassic.h | 2 +- src/ImageLoaderMachOCompressed.cpp | 98 +- src/ImageLoaderMachOCompressed.h | 4 +- src/dyld.cpp | 888 +++++++++++++----- src/dyld.h | 16 +- src/dyldAPIs.cpp | 51 +- src/dyldAPIsInLibSystem.cpp | 203 +++- src/dyldExceptions.c | 8 +- src/dyldInitialization.cpp | 108 ++- src/dyldLibSystemGlue.c | 7 + src/dyldLibSystemInterface.h | 9 +- src/dyldLock.cpp | 21 +- src/dyldLock.h | 3 +- src/dyldNew.cpp | 26 +- src/dyldStartup.s | 42 +- src/dyldSyscallInterface.h | 79 ++ src/dyld_gdb.cpp | 120 ++- src/dyld_sim.exp | 31 + src/dyld_stub_binder.s | 103 +- src/glue.c | 196 +++- src/libdyld_sim.exp | 5 + src/start_glue.h | 41 + src/start_glue.s | 18 +- src/threadLocalHelpers.s | 102 +- src/threadLocalVariables.c | 46 +- unit-tests/include/common.makefile | 18 +- unit-tests/run-all-unit-tests | 2 +- .../Makefile | 7 +- .../main.c | 16 + .../test-cases/absolute-symbol/Makefile | 21 + unit-tests/test-cases/absolute-symbol/abs.s | 12 + unit-tests/test-cases/absolute-symbol/foo.c | 5 + unit-tests/test-cases/absolute-symbol/main.c | 31 + unit-tests/test-cases/big-stack/main.c | 24 +- .../bundle-memory-load-fat/Makefile | 2 +- .../test-cases/bundle-terminator/Makefile | 37 + .../test-cases/bundle-terminator/bundle.cxx | 38 + .../test-cases/bundle-terminator/main.c | 63 ++ unit-tests/test-cases/crt-apple/Makefile | 2 +- unit-tests/test-cases/crt-libSystem/Makefile | 2 +- .../test-cases/dladdr-stripped/Makefile | 2 +- .../dlclose-dylib-dynamic-ref/Makefile | 40 + .../dlclose-dylib-dynamic-ref/bar.c | 8 + .../dlclose-dylib-dynamic-ref/baz.c | 8 + .../dlclose-dylib-dynamic-ref/foo.c | 8 + .../dlclose-dylib-dynamic-ref/main.c | 134 +++ .../dlclose-dylib-ref-count/Makefile | 40 + .../test-cases/dlclose-dylib-ref-count/bar.c | 6 + .../test-cases/dlclose-dylib-ref-count/base.c | 1 + .../test-cases/dlclose-dylib-ref-count/foo.c | 5 + .../test-cases/dlclose-dylib-ref-count/main.c | 110 +++ .../dlclose-dylib-terminators/Makefile | 43 + .../dlclose-dylib-terminators/bar.cpp | 17 + .../dlclose-dylib-terminators/baz.c | 7 + .../dlclose-dylib-terminators/foo.cpp | 23 + .../dlclose-dylib-terminators/main.c | 49 + .../test-cases/dlclose-unload-c++/main.c | 2 +- .../dlopen-codesign-dynamic/Makefile | 45 + .../test-cases/dlopen-codesign-dynamic/foo.c | 3 + .../test-cases/dlopen-codesign-dynamic/main.c | 45 + .../test-cases/dlopen-codesign/Makefile | 43 + unit-tests/test-cases/dlopen-codesign/foo.c | 3 + unit-tests/test-cases/dlopen-codesign/main.c | 27 + .../test-cases/dlopen-leak-threaded/Makefile | 4 +- .../test-cases/dlopen-leak-threaded/main.c | 8 +- unit-tests/test-cases/dlopen-leak/Makefile | 4 +- unit-tests/test-cases/dlopen-leak/main.c | 13 +- .../test-cases/dlopen-search-leak/Makefile | 2 +- .../test-cases/dlopen-search-leak/main.c | 8 +- .../Makefile | 2 +- .../main.c | 8 +- .../test-cases/dlopen_preflight-leak/Makefile | 4 +- .../test-cases/dlopen_preflight-leak/main.c | 8 +- .../framework-DYLD_LIBRARY_PATH/Makefile | 43 + .../framework-DYLD_LIBRARY_PATH/foo.c | 5 + .../framework-DYLD_LIBRARY_PATH/main.c | 47 + .../test-cases/interpose-multiple/Makefile | 54 ++ .../test-cases/interpose-multiple/base.c | 6 + .../test-cases/interpose-multiple/base.h | 3 + .../test-cases/interpose-multiple/foo1.c | 26 + .../test-cases/interpose-multiple/foo2.c | 25 + .../test-cases/interpose-multiple/main.c | 33 + .../test-cases/loader_path-dup/Makefile | 4 +- unit-tests/test-cases/loader_path/Makefile | 4 +- .../test-cases/restrict-environ/Makefile | 5 +- unit-tests/test-cases/restrict-environ/main.c | 8 + .../test-cases/rpath-dlopen-leak/Makefile | 4 +- .../test-cases/rpath-dlopen-leak/main.c | 7 +- .../symbol-resolver-interposed/myfoo.c | 1 + .../symbol-resolver-lazy-prebound/Makefile | 56 ++ .../symbol-resolver-lazy-prebound/bar.c | 58 ++ .../symbol-resolver-lazy-prebound/foo.c | 33 + .../symbol-resolver-lazy-prebound/main.c | 43 + .../weak-coalesce-inserted/Makefile | 42 + .../test-cases/weak-coalesce-inserted/base.c | 83 ++ .../test-cases/weak-coalesce-inserted/base.h | 31 + .../test-cases/weak-coalesce-inserted/foo1.c | 39 + .../test-cases/weak-coalesce-inserted/foo2.c | 35 + .../test-cases/weak-coalesce-inserted/main.c | 42 + .../test-cases/weak-coalesce-stubs/Makefile | 2 +- 125 files changed, 5208 insertions(+), 1700 deletions(-) delete mode 100644 include/mach-o/dyld_debug.h create mode 100644 src/dyldSyscallInterface.h create mode 100644 src/dyld_sim.exp create mode 100644 src/libdyld_sim.exp create mode 100644 src/start_glue.h create mode 100644 unit-tests/test-cases/absolute-symbol/Makefile create mode 100644 unit-tests/test-cases/absolute-symbol/abs.s create mode 100644 unit-tests/test-cases/absolute-symbol/foo.c create mode 100644 unit-tests/test-cases/absolute-symbol/main.c create mode 100644 unit-tests/test-cases/bundle-terminator/Makefile create mode 100644 unit-tests/test-cases/bundle-terminator/bundle.cxx create mode 100644 unit-tests/test-cases/bundle-terminator/main.c create mode 100644 unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile create mode 100644 unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c create mode 100644 unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c create mode 100644 unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c create mode 100644 unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c create mode 100644 unit-tests/test-cases/dlclose-dylib-ref-count/Makefile create mode 100644 unit-tests/test-cases/dlclose-dylib-ref-count/bar.c create mode 100644 unit-tests/test-cases/dlclose-dylib-ref-count/base.c create mode 100644 unit-tests/test-cases/dlclose-dylib-ref-count/foo.c create mode 100644 unit-tests/test-cases/dlclose-dylib-ref-count/main.c create mode 100644 unit-tests/test-cases/dlclose-dylib-terminators/Makefile create mode 100644 unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp create mode 100644 unit-tests/test-cases/dlclose-dylib-terminators/baz.c create mode 100644 unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp create mode 100644 unit-tests/test-cases/dlclose-dylib-terminators/main.c create mode 100644 unit-tests/test-cases/dlopen-codesign-dynamic/Makefile create mode 100644 unit-tests/test-cases/dlopen-codesign-dynamic/foo.c create mode 100644 unit-tests/test-cases/dlopen-codesign-dynamic/main.c create mode 100644 unit-tests/test-cases/dlopen-codesign/Makefile create mode 100644 unit-tests/test-cases/dlopen-codesign/foo.c create mode 100644 unit-tests/test-cases/dlopen-codesign/main.c create mode 100644 unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile create mode 100644 unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c create mode 100644 unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c create mode 100644 unit-tests/test-cases/interpose-multiple/Makefile create mode 100644 unit-tests/test-cases/interpose-multiple/base.c create mode 100644 unit-tests/test-cases/interpose-multiple/base.h create mode 100644 unit-tests/test-cases/interpose-multiple/foo1.c create mode 100644 unit-tests/test-cases/interpose-multiple/foo2.c create mode 100644 unit-tests/test-cases/interpose-multiple/main.c create mode 100644 unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile create mode 100644 unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c create mode 100644 unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c create mode 100644 unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c create mode 100644 unit-tests/test-cases/weak-coalesce-inserted/Makefile create mode 100644 unit-tests/test-cases/weak-coalesce-inserted/base.c create mode 100644 unit-tests/test-cases/weak-coalesce-inserted/base.h create mode 100644 unit-tests/test-cases/weak-coalesce-inserted/foo1.c create mode 100644 unit-tests/test-cases/weak-coalesce-inserted/foo2.c create mode 100644 unit-tests/test-cases/weak-coalesce-inserted/main.c diff --git a/doc/ReleaseNotes.txt b/doc/ReleaseNotes.txt index 33ce9ad..18d5f81 100644 --- a/doc/ReleaseNotes.txt +++ b/doc/ReleaseNotes.txt @@ -44,9 +44,11 @@ dyld-200.3 genCaches fails: "header size miscalculation 0x00006000" dyld-200.2 + ARCH_NAME for armv7k is defined as "armv7s" dsc_iterator.cpp needs cases for v7 variants dyld-200 + dyld fails to build for armv7s update_dyld_shared_cache should accept an 'overlay' along with a 'root' directory option Remove PowerPC support diff --git a/doc/man/man3/dlopen.3 b/doc/man/man3/dlopen.3 index 679d267..1da4927 100644 --- a/doc/man/man3/dlopen.3 +++ b/doc/man/man3/dlopen.3 @@ -1,4 +1,4 @@ -.Dd Nov 7, 2011 +.Dd Aug 7, 2012 .Os .Dt DLOPEN 3 .Sh NAME @@ -121,34 +121,49 @@ returned will only search the main executable. .Fn dlopen 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, but if the path specified -is formatted like a framework, then DYLD_FRAMEWORK_PATH and DYLD_FALLBACK_FRAMEWORK_PATH are -used instead. -The non-fallback variables have no default value. The default value of DYLD_FALLBACK_LIBRARY_PATH -is $HOME/lib:/usr/local/lib:/usr/lib, and DYLD_FALLBACK_FRAMEWORK_PATH is -$HOME/Library/Frameworks:/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks. -.Fn dlopen -searches the directories specified in the environment variables in the order they are listed. +When set, the environment variables contain a colon-separated list of directory paths, +which can be absolute or relative to the current working directory. .Pp When .Fa path -doesn't contain a slash character (i.e. it is just a leaf name and therefore not a framework), +does not contain a slash character (i.e. it is just a leaf name), .Fn dlopen -searches the following the following until it finds a compatible Mach-O file: $LD_LIBRARY_PATH, +searches the following until it finds a compatible Mach-O file: $LD_LIBRARY_PATH, $DYLD_LIBRARY_PATH, current working directory, $DYLD_FALLBACK_LIBRARY_PATH. .Pp When .Fa path -contains a slash (i.e. a full path or a partial path) +looks like a framework path (e.g. /stuff/foo.framework/foo), .Fn dlopen -searches the following the following until it finds a compatible Mach-O file: -$DYLD_LIBRARY_PATH (with leaf name from +searches the following until it finds a compatible Mach-O file: +$DYLD_FRAMEWORK_PATH (with framework partial path from +.Fa path +), then the supplied +.Fa path +(using current working directory for relative paths), then +$DYLD_FALLBACK_FRAMEWORK_PATH (with framework partial path from .Fa path +). +.Pp +When +.Fa path +contains a slash but is not a framework path (i.e. a full path or a partial path to a dylib), +.Fn dlopen +searches the following until it finds a compatible Mach-O file: +$DYLD_LIBRARY_PATH (with leaf name from +.Fa path ), then the supplied .Fa path -(using current working directory for partial paths). +(using current working directory for relative paths), then +$DYLD_FALLBACK_LIBRARY_PATH (with leaf name from +.Fa path +). +.Pp +Note: If DYLD_FALLBACK_LIBRARY_PATH is not set, dlopen operates as if +DYLD_FALLBACK_LIBRARY_PATH was set to $HOME/lib:/usr/local/lib:/usr/lib. +.Pp +Note: If DYLD_FALLBACK_FRAMEWORK_PATH is not set, dlopen operates as if +DYLD_FALLBACK_FRAMEWORK_PATH was set to $HOME/Library/Frameworks:/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks. .Pp Note: There are no configuration files to control dlopen searching. .Pp @@ -162,16 +177,11 @@ If .Fn dlopen fails, it returns a null pointer, and sets an error condition which may be interrogated with .Fn dlerror . -.Sh AUTHORS -Mac OS X 10.3 incorporated the dlcompat package written by Jorge Acereda -and Peter O'Gorman . -.Pp -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 -.Xr dyld 3 +.Xr dyld 1 .Xr ld 1 diff --git a/doc/man/man3/dlsym.3 b/doc/man/man3/dlsym.3 index 2cd3f44..5f7c4ed 100644 --- a/doc/man/man3/dlsym.3 +++ b/doc/man/man3/dlsym.3 @@ -88,7 +88,6 @@ If you looking up a C++ symbol, you need to use the mangled C++ symbol name. .Sh SEE ALSO .Xr dlopen 3 -.Xr dlsym 3 .Xr dlerror 3 .Xr dyld 3 .Xr ld 1 diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index 504873c..d8e3000 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ F908137111D3FB5000626CC1 /* usr|local|include|mach-o */, F908137211D3FB5000626CC1 /* usr|share|man|man1 */, F908137311D3FB5000626CC1 /* usr|share|man|man3 */, + F9F479FE152A63F2008F75C2 /* simulator clean up */, ); dependencies = ( F9B4D78012AD9736000605A6 /* PBXTargetDependency */, @@ -58,6 +59,8 @@ F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; }; F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; }; F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; }; + F93666E0163B4C42002ECADA /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F93666DF163B4C42002ECADA /* CoreFoundation.framework */; }; + F93666E2163B4C58002ECADA /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F93666E1163B4C58002ECADA /* Security.framework */; }; F93937470A94FC4700070A07 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */; }; F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */; }; F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */; settings = {COMPILER_FLAGS = "-O3"; }; }; @@ -203,7 +206,7 @@ F908135111D3ED9000626CC1 /* usr|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "/usr/include/mach-o"; + dstPath = "$(INSTALL_PATH_PREFIX)//usr/include/mach-o"; dstSubfolderSpec = 0; files = ( F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */, @@ -215,7 +218,7 @@ F908137011D3FB5000626CC1 /* usr|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/include; + dstPath = "$(INSTALL_PATH_PREFIX)//usr/include"; dstSubfolderSpec = 0; files = ( F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */, @@ -226,7 +229,7 @@ F908137111D3FB5000626CC1 /* usr|local|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "/usr/local/include/mach-o"; + dstPath = "$(INSTALL_PATH_PREFIX)//usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */, @@ -280,7 +283,7 @@ F9C69EFC14EC8AB8009CAE2E /* usr|local|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/local/include; + dstPath = "$(INSTALL_PATH_PREFIX)//usr/local/include"; dstSubfolderSpec = 0; files = ( F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */, @@ -314,6 +317,8 @@ F906E2230639E96400B13DB2 /* dyld_debug.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyld_debug.c; path = src/dyld_debug.c; sourceTree = ""; }; F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = ""; }; F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = ""; }; + F93666DF163B4C42002ECADA /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + F93666E1163B4C58002ECADA /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; 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 = ""; }; @@ -321,14 +326,14 @@ 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 = ""; }; + F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = update_dyld_shared_cache.cpp; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = ""; }; F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = ""; }; F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = ""; }; F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = ""; }; F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = ""; }; F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = ""; }; + F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.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 = ""; }; @@ -341,6 +346,7 @@ F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = ""; }; F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = ""; }; F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AFEA3216F15CE300CB5161 /* start_glue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = start_glue.h; path = src/start_glue.h; sourceTree = ""; }; F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; }; F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-shared-cache.h"; path = "include/objc-shared-cache.h"; sourceTree = ""; }; F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = ""; }; @@ -380,6 +386,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F93666E2163B4C58002ECADA /* Security.framework in Frameworks */, + F93666E0163B4C42002ECADA /* CoreFoundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -445,6 +453,8 @@ F939373D0A94FC4700070A07 /* launch-cache */ = { isa = PBXGroup; children = ( + F93666E1163B4C58002ECADA /* Security.framework */, + F93666DF163B4C42002ECADA /* CoreFoundation.framework */, F939373E0A94FC4700070A07 /* Architectures.hpp */, F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */, F93937400A94FC4700070A07 /* dyld_cache_format.h */, @@ -475,7 +485,10 @@ F9ED4C990630A76000DF4E74 /* Products */, F939373D0A94FC4700070A07 /* launch-cache */, ); + indentWidth = 4; sourceTree = ""; + tabWidth = 4; + usesTabs = 1; }; F9ED4C990630A76000DF4E74 /* Products */ = { isa = PBXGroup; @@ -506,8 +519,10 @@ F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */, F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */, F9D49CCB1458B95200F86ADD /* start_glue.s */, + F9AFEA3216F15CE300CB5161 /* start_glue.h */, F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */, F9ED4CD00630A7F100DF4E74 /* glue.c */, + F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */, F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */, F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */, F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */, @@ -535,7 +550,6 @@ F98D274C0AA79D7400416316 /* dyld_images.h */, F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */, F9ED4CE90630A80600DF4E74 /* dyld_priv.h */, - F939F219078F1A2100AC144F /* dyld_debug.h */, F9ED4CEA0630A80600DF4E74 /* dyld.h */, F99EE6AE06B48D4200BF1992 /* dlfcn.h */, F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */, @@ -568,6 +582,7 @@ isa = PBXNativeTarget; buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache" */; buildPhases = ( + F91083C91702592700831889 /* create dyld_cache_config.h */, F939372F0A94FAF700070A07 /* Sources */, F93937300A94FAF700070A07 /* Frameworks */, F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */, @@ -725,6 +740,22 @@ shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphoneos\" ] \nthen\n\tmkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n\techo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n"; showEnvVarsInLog = 0; }; + F91083C91702592700831889 /* create dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "create dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -n \"${ARM_SDK}\" ]; then\n\techo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\techo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n\techo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${ARM_SDK}/usr/include/mach/shared_region.h\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\techo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n\tif [ -z ${RC_PURPLE} ]; then \n\t\techo \"#define ARM_SHARED_REGION_START 0x30000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM_SHARED_REGION_SIZE 0x10000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tfi\nfi\n\n"; + showEnvVarsInLog = 0; + }; F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; @@ -737,7 +768,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\nfi\n\nif [ -n \"${RC_PURPLE}\" ]\nthen\n mkdir -p \"${DSTROOT}/Developer/Platforms/iPhoneOS.platform/usr/lib\"\n mv \"${DSTROOT}/Developer/Platforms/iPhoneOS.platform/usr/local/lib/dsc_extractor.bundle\" \"${DSTROOT}/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle\"\nfi"; + shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\nfi\n\nif [ -n \"${RC_PURPLE}\" ]\nthen\n mkdir -p \"${DSTROOT}/${DEVELOPER_DIR}/Platforms/iPhoneOS.platform/usr/lib\"\n mv \"${DSTROOT}/${DEVELOPER_DIR}/Platforms/iPhoneOS.platform/usr/local/lib/dsc_extractor.bundle\" \"${DSTROOT}/${DEVELOPER_DIR}/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle\"\nfi"; showEnvVarsInLog = 0; }; F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = { @@ -768,7 +799,22 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n"; + shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n\n"; + showEnvVarsInLog = 0; + }; + F9F479FE152A63F2008F75C2 /* simulator clean up */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "simulator clean up"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# simulator needs just headers and dylib\nif [ \"${PLATFORM_NAME}\" = \"iphonesimulator\" ] \nthen\n\tmkdir -p ${DSTROOT}/${SDKROOT}/usr/lib/system/\n\tmv ${DSTROOT}/usr/lib/system/libdyld_sim.dylib ${DSTROOT}/${SDKROOT}/usr/lib/system/\n\tmkdir -p ${DSTROOT}/${SDKROOT}/usr/local/include/mach-o\n\tmv ${DSTROOT}/usr/local/include/mach-o/dsc_iterator.h ${DSTROOT}/${SDKROOT}/usr/local/include/mach-o/dsc_iterator.h\n\trm -rf ${DSTROOT}/usr/\n\tmkdir ${DSTROOT}/${SDKROOT}/usr/lib/system/host\n\tln -s /usr/lib/system/libdyld.dylib ${DSTROOT}/${SDKROOT}/usr/lib/system/host/libdyld.dylib\n\tinstall_name_tool -change /usr/lib/system/libdyld.dylib /usr/lib/system/host/libdyld.dylib ${DSTROOT}/${SDKROOT}/usr/lib/system/libdyld_sim.dylib\nfi\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -902,6 +948,8 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH_PREFIX = "$(INSTALL_PATH_PREFIX_$(PLATFORM_NAME))"; + INSTALL_PATH_PREFIX_iphonesimulator = "$(SDKROOT)"; PRODUCT_NAME = libdyld; }; name = Debug; @@ -912,6 +960,8 @@ COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH_PREFIX = "$(INSTALL_PATH_PREFIX_$(PLATFORM_NAME))"; + INSTALL_PATH_PREFIX_iphonesimulator = "$(SDKROOT)"; PRODUCT_NAME = libdyld; ZERO_LINK = NO; }; @@ -927,6 +977,7 @@ GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; GCC_WARN_MISSING_PARENTHESES = YES; @@ -935,6 +986,11 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PRODUCT_NAME = update_dyld_shared_cache; VALID_ARCHS = "x86_64 i386"; }; @@ -951,11 +1007,17 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = s; GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/$(LOCAL)/bin"; LOCAL = "$(LOCAL_$(RC_TARGET_CONFIG))"; LOCAL_iPhone = local; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = "-stdlib=libc++"; PRODUCT_NAME = update_dyld_shared_cache; STRIP_INSTALLED_PRODUCT = YES; STRIP_STYLE = debugging; @@ -984,6 +1046,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = NO; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; PRODUCT_NAME = dyld_shared_cache_util; @@ -1004,7 +1067,14 @@ GCC_OPTIMIZATION_LEVEL = 0; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; MACH_O_TYPE = mh_bundle; - OTHER_LDFLAGS = "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress", + ); PRODUCT_NAME = dsc_extractor; }; name = Debug; @@ -1021,7 +1091,14 @@ GCC_INLINES_ARE_PRIVATE_EXTERN = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; MACH_O_TYPE = mh_bundle; - OTHER_LDFLAGS = "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress", + ); PRODUCT_NAME = dsc_extractor; ZERO_LINK = NO; }; @@ -1038,6 +1115,7 @@ BASE_ADDRESS_armv7 = 0x2fe00000; BASE_ADDRESS_armv7f = 0x2fe00000; BASE_ADDRESS_armv7k = 0x2fe00000; + BASE_ADDRESS_armv7s = 0x2fe00000; BASE_ADDRESS_i386 = 0x8fe00000; BASE_ADDRESS_ppc = 0x8fe00000; BASE_ADDRESS_x86_64 = 0x7fff5fc00000; @@ -1045,7 +1123,13 @@ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; - EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/dyld.exp"; + ENTRY_iphoneos = "-Wl,-e,__dyld_start"; + ENTRY_iphonesimulator = "-Wl,-e,_start_sim"; + ENTRY_macosx = "-Wl,-e,__dyld_start"; + EXPORTED_SYMBOLS_FILE = "$(EXPORTS_$(PLATFORM_NAME))"; + EXPORTS_iphoneos = "$(SRCROOT)/src/dyld.exp"; + EXPORTS_iphonesimulator = "$(SRCROOT)/src/dyld_sim.exp"; + EXPORTS_macosx = "$(SRCROOT)/src/dyld.exp"; GCC_C_LANGUAGE_STANDARD = c99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_BUILTIN_FUNCTIONS = NO; @@ -1062,17 +1146,27 @@ "./launch-cache", ); INSTALL_PATH = /usr/lib; + LD_GENERATE_MAP_FILE = YES; OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); OTHER_LDFLAGS = ( "-seg1addr", "$(BASE_ADDRESS_$(CURRENT_ARCH))", "@$(DERIVED_SOURCES_DIR)/archives.txt", "-nostdlib", - "-Wl,-e,__dyld_start", "-Wl,-dylinker", "-Wl,-dylinker_install_name,/usr/lib/dyld", + "-stdlib=libc++", + "$(ALIGNMENT_$(CURRENT_ARCH))", + "$(ENTRY_$(PLATFORM_NAME))", ); - PRODUCT_NAME = dyld; + PRODUCT_NAME = "$(PRODUCT_NAME_$(PLATFORM_NAME))"; + PRODUCT_NAME_iphoneos = dyld; + PRODUCT_NAME_iphonesimulator = dyld_sim; + PRODUCT_NAME_macosx = dyld; STRIPFLAGS = "-S"; UNSTRIPPED_PRODUCT = NO; VERSIONING_SYSTEM = "apple-generic"; @@ -1093,6 +1187,7 @@ BASE_ADDRESS_armv7 = 0x2fe00000; BASE_ADDRESS_armv7f = 0x2fe00000; BASE_ADDRESS_armv7k = 0x2fe00000; + BASE_ADDRESS_armv7s = 0x2fe00000; BASE_ADDRESS_i386 = 0x8fe00000; BASE_ADDRESS_ppc = 0x8fe00000; BASE_ADDRESS_x86_64 = 0x7fff5fc00000; @@ -1100,7 +1195,13 @@ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/dyld.exp"; + ENTRY_iphoneos = "-Wl,-e,__dyld_start"; + ENTRY_iphonesimulator = "-Wl,-e,_start_sim"; + ENTRY_macosx = "-Wl,-e,__dyld_start"; + EXPORTED_SYMBOLS_FILE = "$(EXPORTS_$(PLATFORM_NAME))"; + EXPORTS_iphoneos = "$(SRCROOT)/src/dyld.exp"; + EXPORTS_iphonesimulator = "$(SRCROOT)/src/dyld_sim.exp"; + EXPORTS_macosx = "$(SRCROOT)/src/dyld.exp"; GCC_C_LANGUAGE_STANDARD = c99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_CPP_RTTI = NO; @@ -1114,18 +1215,28 @@ "./launch-cache", ); INSTALL_PATH = /usr/lib; + LD_GENERATE_MAP_FILE = YES; ORDER_FILE = "$(SRCROOT)/src/dyld.order"; "OTHER_CFLAGS[arch=armv6]" = "-mthumb"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); OTHER_LDFLAGS = ( "-seg1addr", "$(BASE_ADDRESS_$(CURRENT_ARCH))", "@$(DERIVED_SOURCES_DIR)/archives.txt", "-nostdlib", - "-Wl,-e,__dyld_start", "-Wl,-dylinker", "-Wl,-dylinker_install_name,/usr/lib/dyld", + "-stdlib=libc++", + "$(ALIGNMENT_$(CURRENT_ARCH))", + "$(ENTRY_$(PLATFORM_NAME))", ); - PRODUCT_NAME = dyld; + PRODUCT_NAME = "$(PRODUCT_NAME_$(PLATFORM_NAME))"; + PRODUCT_NAME_iphoneos = dyld; + PRODUCT_NAME_iphonesimulator = dyld_sim; + PRODUCT_NAME_macosx = dyld; STRIPFLAGS = "-S"; UNSTRIPPED_PRODUCT = NO; VERSIONING_SYSTEM = "apple-generic"; @@ -1141,7 +1252,9 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + DEAD_CODE_STRIPPING = YES; EXECUTABLE_PREFIX = lib; + EXPORT_OPTIONS_iphonesimulator = "-exported_symbols_list $(SRCROOT)/src/libdyld_sim.exp"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; @@ -1149,7 +1262,22 @@ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; INSTALL_PATH = /usr/lib/system; - PRODUCT_NAME = dyld; + LIBSYSTEM_LIBS = "$(LIBSYSTEM_LIBS_$(PLATFORM_NAME))"; + LIBSYSTEM_LIBS_iphoneos = "-Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel"; + LIBSYSTEM_LIBS_iphonesimulator = "-Wl,-upward-lsystem_sim_c -Wl,-upward-lSystem -Wl,-reexport_library,/usr/lib/system/libdyld.dylib -Wl,-allow_simulator_linking_to_macosx_dylibs"; + LIBSYSTEM_LIBS_macosx = "-Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel"; + OTHER_LDFLAGS = ( + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "$(EXPORT_OPTIONS_$(PLATFORM_NAME))", + "-L$(SDKROOT)/usr/lib/system", + ); + PRODUCT_NAME = "$(PRODUCT_NAME_$(PLATFORM_NAME))"; + PRODUCT_NAME_iphoneos = dyld; + PRODUCT_NAME_iphonesimulator = dyld_sim; + PRODUCT_NAME_macosx = dyld; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -1167,9 +1295,11 @@ ); COPY_PHASE_STRIP = YES; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; EXECUTABLE_PREFIX = lib; + EXPORT_OPTIONS_iphonesimulator = "-exported_symbols_list $(SRCROOT)/src/libdyld_sim.exp"; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_ENABLE_CPP_EXCEPTIONS = NO; GCC_ENABLE_CPP_RTTI = NO; @@ -1178,13 +1308,26 @@ HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; INSTALLHDRS_COPY_PHASE = YES; INSTALL_PATH = /usr/lib/system; + LIBSYSTEM_LIBS = "$(LIBSYSTEM_LIBS_$(PLATFORM_NAME))"; + LIBSYSTEM_LIBS_iphoneos = "-Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel"; + LIBSYSTEM_LIBS_iphonesimulator = "-Wl,-upward-lsystem_sim_c -Wl,-upward-lSystem -Wl,-reexport_library,/usr/lib/system/libdyld.dylib -Wl,-allow_simulator_linking_to_macosx_dylibs"; + LIBSYSTEM_LIBS_macosx = "-Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-llaunch -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel"; + OTHER_CPLUSPLUSFLAGS = ( + "-fno-exceptions", + "$(OTHER_CFLAGS)", + ); OTHER_LDFLAGS = ( - "-nodefaultlibs", - "-Wl,-upward-lSystem", + "-nostdlib", + "$(LIBSYSTEM_LIBS)", "-umbrella", System, + "$(EXPORT_OPTIONS_$(PLATFORM_NAME))", + "-L$(SDKROOT)/usr/lib/system", ); - PRODUCT_NAME = dyld; + PRODUCT_NAME = "$(PRODUCT_NAME_$(PLATFORM_NAME))"; + PRODUCT_NAME_iphoneos = dyld; + PRODUCT_NAME_iphonesimulator = dyld_sim; + PRODUCT_NAME_macosx = dyld; SEPARATE_STRIP = YES; STRIP_INSTALLED_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -1235,6 +1378,10 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_SYMBOLS_PRIVATE_EXTERN = YES; INSTALL_PATH = /usr/local/lib; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PRODUCT_NAME = dsc; }; name = Debug; @@ -1258,6 +1405,10 @@ GCC_WARN_UNUSED_VARIABLE = YES; INSTALLHDRS_COPY_PHASE = YES; INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); PRODUCT_NAME = dsc; ZERO_LINK = NO; }; diff --git a/include/dlfcn.h b/include/dlfcn.h index 7e4ae35..bcb0c09 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -38,7 +38,7 @@ extern "C" { #if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) #include -#include +#include /* * Structure filled in by dladdr(). */ @@ -58,7 +58,7 @@ 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; +extern bool dlopen_preflight(const char* __path) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); #endif /* not POSIX */ diff --git a/include/mach-o/dyld.h b/include/mach-o/dyld.h index 3f579ef..642ca42 100644 --- a/include/mach-o/dyld.h +++ b/include/mach-o/dyld.h @@ -71,7 +71,7 @@ extern int32_t NSVersionOfRunTimeLibrary(const char* libraryName) __O /* - * NSVersionOfRunTimeLibrary() returns the current_version number that the main executable was linked + * 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. It returns -1 if the main executable did not link * against the specified library. diff --git a/include/mach-o/dyld_debug.h b/include/mach-o/dyld_debug.h deleted file mode 100644 index a0590f5..0000000 --- a/include/mach-o/dyld_debug.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (c) 1999 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_DEBUG_ -#define _DYLD_DEBUG_ - -#include -#ifndef DYLD_BUILD /* do not include this when building dyld itself */ -#include -#endif /* !defined(DYLD_BUILD) */ - -#include - -/* - * The dyld debugging API is deprecated as of Mac OS X 10.4 - */ -enum dyld_debug_return { - DYLD_SUCCESS, - DYLD_INCONSISTENT_DATA, - DYLD_INVALID_ARGUMENTS, - DYLD_FAILURE -}; - -struct dyld_debug_module { - struct mach_header *header; - unsigned long vmaddr_slide; - unsigned long module_index; -}; - -enum dyld_event_type { - DYLD_IMAGE_ADDED, - DYLD_MODULE_BOUND, - DYLD_MODULE_REMOVED, - DYLD_MODULE_REPLACED, - DYLD_PAST_EVENTS_END, - DYLD_IMAGE_REMOVED -}; - -struct dyld_event { - enum dyld_event_type type; - struct dyld_debug_module arg[2]; -}; - -extern enum dyld_debug_return _dyld_debug_defining_module( - mach_port_t target_task, - unsigned long send_timeout, - unsigned long rcv_timeout, - boolean_t inconsistent_data_ok, - char *name, - struct dyld_debug_module *module) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -extern enum dyld_debug_return _dyld_debug_is_module_bound( - mach_port_t target_task, - unsigned long send_timeout, - unsigned long rcv_timeout, - boolean_t inconsistent_data_ok, - struct dyld_debug_module module, - boolean_t *bound) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -extern enum dyld_debug_return _dyld_debug_bind_module( - mach_port_t target_task, - unsigned long send_timeout, - unsigned long rcv_timeout, - boolean_t inconsistent_data_ok, - struct dyld_debug_module module) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -extern enum dyld_debug_return _dyld_debug_module_name( - mach_port_t target_task, - unsigned long send_timeout, - unsigned long rcv_timeout, - boolean_t inconsistent_data_ok, - struct dyld_debug_module module, - char **image_name, - unsigned long *image_nameCnt, - char **module_name, - unsigned long *module_nameCnt) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -extern enum dyld_debug_return _dyld_debug_subscribe_to_events( - mach_port_t target_task, - unsigned long send_timeout, - unsigned long rcv_timeout, - boolean_t inconsistent_data_ok, - void (*dyld_event_routine)(struct dyld_event event)) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -/* - * _dyld_debug_add_event_subscriber() uses the mig interface functions below - * to dispatch the dyld event messages from the subscriber port specified. - */ -extern enum dyld_debug_return _dyld_debug_add_event_subscriber( - mach_port_t target_task, - unsigned long send_timeout, - unsigned long rcv_timeout, - boolean_t inconsistent_data_ok, - mach_port_t subscriber) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -/* - * These structures should be produced by mig(1) from the mig generated files - * but they are not. These are really only needed so the correct size of the - * request and reply messages can be allocated. - */ -struct _dyld_event_message_request { -#ifdef __MACH30__ - mach_msg_header_t head; - NDR_record_t NDR; - struct dyld_event event; - mach_msg_trailer_t trailer; -#else - msg_header_t head; - msg_type_t eventType; - struct dyld_event event; -#endif -}; -struct _dyld_event_message_reply { -#ifdef __MACH30__ - mach_msg_header_t head; - NDR_record_t NDR; - struct dyld_event event; -#else - msg_header_t head; - msg_type_t RetCodeType; - kern_return_t RetCode; -#endif -}; -#ifndef mig_internal -/* - * _dyld_event_server() is the mig generated routine to dispatch dyld event - * messages. - */ -extern boolean_t _dyld_event_server( -#ifdef __MACH30__ - mach_msg_header_t *request, - mach_msg_header_t *reply); -#else - struct _dyld_event_message_request *request, - struct _dyld_event_message_reply *reply); -#endif -#endif /* mig_internal */ - -#ifndef SHLIB -/* - * _dyld_event_server_callback() is the routine called by _dyld_event_server() - * that must be written by users of _dyld_event_server(). - */ -extern -#ifdef __MACH30__ -kern_return_t -#else -void -#endif -_dyld_event_server_callback( -#ifdef __MACH30__ - mach_port_t subscriber, -#else - port_t subscriber, -#endif - struct dyld_event event); -#endif /* SHLIB */ - -/* - * This is the state of the target task while we are sending a message to it. - */ -struct _dyld_debug_task_state { - mach_port_t debug_port; - mach_port_t debug_thread; - unsigned int debug_thread_resume_count; - unsigned int task_resume_count; - mach_port_t *threads; - unsigned int thread_count; -}; - -/* - * _dyld_debug_make_runnable() is called before sending messages to the - * dynamic link editor. Basically it assures that the debugging - * thread is the only runnable thread in the task to receive the - * message. It also assures that the debugging thread is indeed - * runnable if it was suspended. The function will make sure each - * thread in the remote task is suspended and resumed the same number - * of times, so in the end the suspend count of each individual thread - * is the same. - */ -extern enum dyld_debug_return _dyld_debug_make_runnable( - mach_port_t target_task, - struct _dyld_debug_task_state *state) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -/* - * _dyld_debug_restore_runnable() is called after sending messages to the - * dynamic link editor. It undoes what _dyld_debug_make_runnable() did to the - * task and put it back the way it was. - */ -extern enum dyld_debug_return _dyld_debug_restore_runnable( - mach_port_t target_task, - struct _dyld_debug_task_state *state) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -/* - * To provide more detailed information when the APIs of the dyld debug - * interfaces fail (return DYLD_FAILURE) the following structure is filled in. - * After it is filled in the function registered with - * set_dyld_debug_error_func() is called with a pointer to that struct. - * - * The local_error field is a unique number for each possible error condition - * in the source code in that makes up the dyld debug APIs. The source file - * and line number in the cctools libdyld directory where the dyld debug APIs - * are implemented are set into the file_name and line_number fields. The - * field dyld_debug_return is filled in with that would be returned by the - * API (usually DYLD_FAILURE). The other fields will be zero or filled in by - * the error code from the mach system call, or UNIX system call that failed. - */ -struct dyld_debug_error_data { - enum dyld_debug_return dyld_debug_return; - kern_return_t mach_error; - int dyld_debug_errno; - unsigned long local_error; - char *file_name; - unsigned long line_number; -}; - -extern void _dyld_debug_set_error_func( - void (*func)(struct dyld_debug_error_data *e)) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -#ifndef DYLD_BUILD /* do not include this when building dyld itself */ - -extern enum dyld_debug_return _dyld_debug_task_from_core( - NSObjectFileImage coreFileImage, - mach_port_t *core_task) AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4; - -#endif /* !defined(DYLD_BUILD) */ - -#endif /* _DYLD_DEBUG_ */ diff --git a/include/mach-o/dyld_images.h b/include/mach-o/dyld_images.h index ed710ef..ba287f9 100644 --- a/include/mach-o/dyld_images.h +++ b/include/mach-o/dyld_images.h @@ -63,7 +63,7 @@ extern "C" { * */ -enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1 }; +enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1, dyld_image_info_change=2 }; struct dyld_image_info { const struct mach_header* imageLoadAddress; /* base address image is mapped into */ @@ -122,17 +122,11 @@ struct dyld_all_image_infos { const char* errorSymbol; /* the following field is only in version 12 (Mac OS X 10.7, iOS 4.3) and later */ uintptr_t sharedCacheSlide; + /* the following field is only in version 13 (Mac OS X 10.9, iOS 7.0) and later */ + uint8_t sharedCacheUUID[16]; + /* the following field is only in version 14 (Mac OS X 10.9, iOS 7.0) and later */ + uintptr_t reserved[16]; }; -extern struct dyld_all_image_infos dyld_all_image_infos; - -/* - * Beginning in Mac OS X 10.6, rather than looking up the symbol "_dyld_all_image_infos" - * in dyld's symbol table, you can add DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET to the mach_header - * for dyld and read the 32-bit unsigned int at that location. Adding that value to dyld's - * mach_header address gets you the address of dyld_all_image_infos in dyld. - */ -#define DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET 0x1010 - /* diff --git a/include/mach-o/dyld_priv.h b/include/mach-o/dyld_priv.h index 9464fb8..49d5775 100644 --- a/include/mach-o/dyld_priv.h +++ b/include/mach-o/dyld_priv.h @@ -178,12 +178,36 @@ extern const char* dyld_image_path_containing_address(const void* addr); +// Convienence constants for return values from dyld_get_sdk_version() and friends. +#define DYLD_MACOSX_VERSION_10_4 0x000A0400 +#define DYLD_MACOSX_VERSION_10_5 0x000A0500 +#define DYLD_MACOSX_VERSION_10_6 0x000A0600 +#define DYLD_MACOSX_VERSION_10_7 0x000A0700 +#define DYLD_MACOSX_VERSION_10_8 0x000A0800 +#define DYLD_MACOSX_VERSION_10_9 0x000A0900 + +#define DYLD_IOS_VERSION_2_0 0x00020000 +#define DYLD_IOS_VERSION_2_1 0x00020100 +#define DYLD_IOS_VERSION_2_2 0x00020200 +#define DYLD_IOS_VERSION_3_0 0x00030000 +#define DYLD_IOS_VERSION_3_1 0x00030100 +#define DYLD_IOS_VERSION_3_2 0x00030200 +#define DYLD_IOS_VERSION_4_0 0x00040000 +#define DYLD_IOS_VERSION_4_1 0x00040100 +#define DYLD_IOS_VERSION_4_2 0x00040200 +#define DYLD_IOS_VERSION_4_3 0x00040300 +#define DYLD_IOS_VERSION_5_0 0x00050000 +#define DYLD_IOS_VERSION_5_1 0x00050100 +#define DYLD_IOS_VERSION_6_0 0x00060000 +#define DYLD_IOS_VERSION_6_1 0x00060100 +#define DYLD_IOS_VERSION_7_0 0x00070000 // // This is finds the SDK version a binary was built against. // Returns zero on error, or if SDK version could not be determined. // // Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later extern uint32_t dyld_get_sdk_version(const struct mach_header* mh); @@ -192,6 +216,7 @@ extern uint32_t dyld_get_sdk_version(const struct mach_header* mh); // Returns zero on error, or if SDK version could not be determined. // // Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later extern uint32_t dyld_get_program_sdk_version(); @@ -200,6 +225,7 @@ extern uint32_t dyld_get_program_sdk_version(); // Returns zero on error, or if no min OS recorded in binary. // // Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later extern uint32_t dyld_get_min_os_version(const struct mach_header* mh); @@ -208,6 +234,7 @@ extern uint32_t dyld_get_min_os_version(const struct mach_header* mh); // Returns zero on error, or if no min OS recorded in binary. // // Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later extern uint32_t dyld_get_program_min_os_version(); @@ -221,6 +248,21 @@ extern uint32_t dyld_get_program_min_os_version(); extern bool dyld_shared_cache_some_image_overridden(); #endif + + +// +// Returns if the process is setuid or is code signed with entitlements. +// +// Exists in Mac OS X 10.9 and later +extern bool dyld_process_is_restricted(); + + +// +// for OpenGL to tell dyld it is ok to deallocate a memory based image when done. +// +// Exists in Mac OS X 10.9 and later +#define NSLINKMODULE_OPTION_CAN_UNLOAD 0x20 + #if __cplusplus diff --git a/include/objc-shared-cache.h b/include/objc-shared-cache.h index b7db57d..8d59bfb 100644 --- a/include/objc-shared-cache.h +++ b/include/objc-shared-cache.h @@ -82,7 +82,7 @@ Source is http://burtleburtle.net/bob/c/perfect.c #include #include #ifdef SELOPT_WRITE -#include +#include #endif /* DO NOT INCLUDE ANY objc HEADERS HERE @@ -108,6 +108,8 @@ namespace objc_opt { typedef int32_t objc_stringhash_offset_t; typedef uint8_t objc_stringhash_check_t; +static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level); + #ifdef SELOPT_WRITE // Perfect hash code is at the end of this file. @@ -133,18 +135,23 @@ struct eqstr { } }; +struct hashstr { + size_t operator()(const char *s) const { + return (size_t)lookup8((uint8_t *)s, strlen(s), 0); + } +}; + // cstring => cstring's vmaddress // (used for selector names and class names) -typedef __gnu_cxx::hash_map, eqstr> string_map; +typedef std::unordered_map string_map; // class name => (class vmaddress, header_info vmaddress) -typedef __gnu_cxx::hash_multimap, __gnu_cxx::hash, eqstr> class_map; +typedef std::unordered_multimap, hashstr, eqstr> class_map; static perfect_hash make_perfect(const string_map& strings); #endif -static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level); // Precomputed perfect hash table of strings. // Base class for precomputed selector table and class table. @@ -169,40 +176,55 @@ struct objc_stringhash_t { objc_stringhash_offset_t *offsets() { return (objc_stringhash_offset_t *)&checkbytes()[capacity]; } const objc_stringhash_offset_t *offsets() const { return (const objc_stringhash_offset_t *)&checkbytes()[capacity]; } - uint32_t hash(const char *key) const + uint32_t hash(const char *key, size_t keylen) const { - uint64_t val = lookup8((uint8_t*)key, strlen(key), salt); + uint64_t val = lookup8((uint8_t*)key, keylen, salt); uint32_t index = (uint32_t)(val>>shift) ^ scramble[tab[val&mask]]; return index; } + uint32_t hash(const char *key) const + { + return hash(key, strlen(key)); + } + // The check bytes areused to reject strings that aren't in the table // without paging in the table's cstring data. This checkbyte calculation // catches 4785/4815 rejects when launching Safari; a perfect checkbyte // would catch 4796/4815. - objc_stringhash_check_t checkbyte(const char *key) const + objc_stringhash_check_t checkbyte(const char *key, size_t keylen) const { return ((key[0] & 0x7) << 5) | - (strlen(key) & 0x1f); + ((uint8_t)keylen & 0x1f); } + objc_stringhash_check_t checkbyte(const char *key) const + { + return checkbyte(key, strlen(key)); + } + + #define INDEX_NOT_FOUND (~(uint32_t)0) uint32_t getIndex(const char *key) const { - uint32_t h = hash(key); + size_t keylen = strlen(key); + uint32_t h = hash(key, keylen); // Use check byte to reject without paging in the table's cstrings objc_stringhash_check_t h_check = checkbytes()[h]; - objc_stringhash_check_t key_check = checkbyte(key); + objc_stringhash_check_t key_check = checkbyte(key, keylen); bool check_fail = (h_check != key_check); #if ! SELOPT_DEBUG if (check_fail) return INDEX_NOT_FOUND; #endif - const char *result = (const char *)this + offsets()[h]; + // fixme change &zero to 0 in the next version-breaking update + objc_stringhash_offset_t offset = offsets()[h]; + if (offset == offsetof(objc_stringhash_t,zero)) return INDEX_NOT_FOUND; + const char *result = (const char *)this + offset; if (0 != strcmp(key, result)) return INDEX_NOT_FOUND; #if SELOPT_DEBUG diff --git a/launch-cache/CacheFileAbstraction.hpp b/launch-cache/CacheFileAbstraction.hpp index c2e536c..cd2524e 100644 --- a/launch-cache/CacheFileAbstraction.hpp +++ b/launch-cache/CacheFileAbstraction.hpp @@ -62,6 +62,15 @@ public: uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); } void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); } + uint64_t localSymbolsOffset() const INLINE { return E::get64(fields.localSymbolsOffset); } + void set_localSymbolsOffset(uint64_t value) INLINE { E::set64(fields.localSymbolsOffset, value); } + + uint64_t localSymbolsSize() const INLINE { return E::get64(fields.localSymbolsSize); } + void set_localSymbolsSize(uint64_t value) INLINE { E::set64(fields.localSymbolsSize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + private: dyld_cache_header fields; }; @@ -144,6 +153,51 @@ struct dyldCacheSlideInfoEntry { +template +class dyldCacheLocalSymbolsInfo { +public: + uint32_t nlistOffset() const INLINE { return E::get32(fields.nlistOffset); } + void set_nlistOffset(uint32_t value) INLINE { E::set32(fields.nlistOffset, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + + uint32_t stringsOffset() const INLINE { return E::get32(fields.stringsOffset); } + void set_stringsOffset(uint32_t value) INLINE { E::set32(fields.stringsOffset, value); } + + uint32_t stringsSize() const INLINE { return E::get32(fields.stringsSize); } + void set_stringsSize(uint32_t value) INLINE { E::set32(fields.stringsSize, value); } + + uint32_t entriesOffset() const INLINE { return E::get32(fields.entriesOffset); } + void set_entriesOffset(uint32_t value) INLINE { E::set32(fields.entriesOffset, value); } + + uint32_t entriesCount() const INLINE { return E::get32(fields.entriesCount); } + void set_entriesCount(uint32_t value) INLINE { E::set32(fields.entriesCount, value); } + +private: + dyld_cache_local_symbols_info fields; +}; + + +template +class dyldCacheLocalSymbolEntry { +public: + uint32_t dylibOffset() const INLINE { return E::get32(fields.dylibOffset); } + void set_dylibOffset(uint32_t value) INLINE { E::set32(fields.dylibOffset, value); } + + uint32_t nlistStartIndex() const INLINE { return E::get32(fields.nlistStartIndex); } + void set_nlistStartIndex(uint32_t value) INLINE { E::set32(fields.nlistStartIndex, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + +private: + dyld_cache_local_symbols_entry fields; +}; + + + + #endif // __DYLD_CACHE_ABSTRACTION__ diff --git a/launch-cache/MachOBinder.hpp b/launch-cache/MachOBinder.hpp index 332478f..6f37727 100644 --- a/launch-cache/MachOBinder.hpp +++ b/launch-cache/MachOBinder.hpp @@ -40,8 +40,8 @@ #include #include -#include -#include +#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -53,16 +53,28 @@ #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 #endif +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif template class Binder : public Rebaser { public: + class CStringHash { + public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; + }; 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; + typedef std::unordered_map*, CStringHash, CStringEquals> Map; Binder(const MachOLayoutAbstraction&, uint64_t dyldBaseAddress); @@ -73,16 +85,14 @@ public: void bind(std::vector&); void optimize(); void addResolverClient(Binder* clientDylib, const char* symbolName); - void addResolverLazyPointerMappedAddress(const char* symbolName, - typename A::P::uint_t lpVMAddr); private: typedef typename A::P P; typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; struct BinderAndReExportFlag { Binder* binder; bool reExport; }; struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; }; - typedef __gnu_cxx::hash_map, CStringEquals> NameToAddrMap; - typedef __gnu_cxx::hash_set, CStringEquals> NameSet; + typedef std::unordered_map NameToAddrMap; + typedef std::unordered_set NameSet; struct ClientAndSymbol { Binder* client; const char* symbolName; }; struct SymbolAndLazyPointer { const char* symbolName; pint_t lpVMAddr; }; @@ -100,7 +110,8 @@ private: const char* symbolName, bool lazyPointer, bool weakImport, std::vector& pointersInData); pint_t resolveUndefined(const macho_nlist

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

* sym); @@ -115,6 +126,7 @@ private: std::vector fDependentDylibs; NameToAddrMap fHashTable; NameSet fSymbolResolvers; + NameSet fAbsoluteSymbols; std::vector fReExportedSymbols; uint64_t fDyldBaseAddress; const macho_nlist

* fSymbolTable; @@ -127,7 +139,7 @@ private: bool fOriginallyPrebound; bool fReExportedSymbolsResolved; std::vector fClientAndSymbols; - std::vector fSymbolAndLazyPointers; + NameToAddrMap fResolverLazyPointers; }; template <> @@ -211,27 +223,36 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); pint_t baseAddress = layout.getSegments()[0].newAddress(); for(std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { - if ( (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) { - if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { - fSymbolResolvers.insert(it->name); - } - if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { - //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID()); - SymbolReExport sym; - sym.exportName = it->name; - sym.dylibOrdinal = it->other; - sym.importName = it->importName; - if ( (sym.importName == NULL) || (sym.importName[0] == '\0') ) - sym.importName = sym.exportName; - fReExportedSymbols.push_back(sym); - // fHashTable entry will be added in first call to findExportedSymbolAddress() - } - else { + switch ( it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + fSymbolResolvers.insert(it->name); + } + if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID()); + SymbolReExport sym; + sym.exportName = it->name; + sym.dylibOrdinal = it->other; + sym.importName = it->importName; + if ( (sym.importName == NULL) || (sym.importName[0] == '\0') ) + sym.importName = sym.exportName; + fReExportedSymbols.push_back(sym); + // fHashTable entry will be added in first call to findExportedSymbolAddress() + } + else { + fHashTable[it->name] = it->address + baseAddress; + } + break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: fHashTable[it->name] = it->address + baseAddress; - } - } - else { - throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath()); + break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + fHashTable[it->name] = it->address; + fAbsoluteSymbols.insert(it->name); + break; + default: + throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath()); + break; } //fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name); } @@ -240,7 +261,7 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress 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 + fHashTable.reserve(fDynamicInfo->nextdefsym()); // set initial bucket count for (const macho_nlist

* sym=start; sym < end; ++sym) { const char* name = &fStrings[sym->n_strx()]; fHashTable[name] = runtimeAddressFromNList(sym); @@ -249,7 +270,7 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress } else { int32_t count = fDynamicInfo->ntoc(); - fHashTable.resize(count); // set initial bucket count + fHashTable.reserve(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); @@ -422,10 +443,13 @@ void Binder::setDependentBinders(const Map& map) ++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; + Binder* dep = it->binder; + if ( dep != NULL ) { + const char* parentUmbrellaName = dep->parentUmbrella(); + if ( parentUmbrellaName != NULL ) { + if ( strcmp(parentUmbrellaName, thisLeafName) == 0 ) + it->reExport = true; + } } } } @@ -567,28 +591,22 @@ void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uin else binder = fDependentDylibs[libraryOrdinal-1].binder; pint_t targetSymbolAddress; - bool isResolverSymbol; + bool isResolverSymbol = false; + bool isAbsolute = false; Binder* foundIn; if ( weakImport && (binder == NULL) ) { targetSymbolAddress = 0; foundIn = NULL; - isResolverSymbol = false; } else { - if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol) ) + if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol, &isAbsolute) ) throwf("could not bind symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID()); } // don't bind lazy pointers to resolver stubs in shared cache if ( lazyPointer && isResolverSymbol ) { - if ( foundIn == this ) { - // record location of lazy pointer for other dylibs to re-use - pint_t lpVMAddr = segments[segmentIndex].newAddress() + segmentOffset; - foundIn->addResolverLazyPointerMappedAddress(symbolName, lpVMAddr); - //fprintf(stderr, "resolver %s in %s has lazy pointer with segmentOffset=0x%08llX\n", symbolName, this->getDylibID(), segmentOffset); - } - else { - // record that this dylib has a lazy pointer to a resolver function + if ( foundIn != this ) { + // record that this dylib has a lazy pointer to a resolver function foundIn->addResolverClient(this, symbolName); // fprintf(stderr, "have lazy pointer to resolver %s in %s\n", symbolName, this->getDylibID()); } @@ -618,7 +636,8 @@ void Binder::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uin default: throw "bad bind type"; } - pointersInData.push_back(mappedAddr); + if ( !isAbsolute ) + pointersInData.push_back(mappedAddr); } @@ -993,15 +1012,16 @@ typename A::P::uint_t Binder::resolveUndefined(const macho_nlist

* undefine } pint_t addr; bool isResolver; + bool isAbsolute; Binder* foundIn; - if ( ! binder->findExportedSymbolAddress(symbolName, &addr, &foundIn, &isResolver) ) + if ( ! binder->findExportedSymbolAddress(symbolName, &addr, &foundIn, &isResolver, &isAbsolute) ) throwf("could not resolve undefined symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID()); return addr; } } template -bool Binder::findExportedSymbolAddress(const char* name, pint_t* result, Binder** foundIn, bool* isResolverSymbol) +bool Binder::findExportedSymbolAddress(const char* name, pint_t* result, Binder** foundIn, bool* isResolverSymbol, bool* isAbsolute) { *foundIn = NULL; // since re-export chains can be any length, re-exports cannot be resolved in setDependencies() @@ -1012,17 +1032,18 @@ bool Binder::findExportedSymbolAddress(const char* name, pint_t* result, Bind for (typename std::vector::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) { pint_t targetSymbolAddress; bool isResolver; + bool isAb; if ( it->dylibOrdinal <= 0 ) throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache"; Binder* binder = fDependentDylibs[it->dylibOrdinal-1].binder; - if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver) ) + if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver, &isAb) ) throwf("could not bind symbol %s in %s expected in %s", it->importName, this->getDylibID(), binder->getDylibID()); - if ( isResolver ) - throw "bad mach-o binary, re-export of resolvers symbols not supported in dyld shared cache"; + if ( isResolver ) + fSymbolResolvers.insert(name); fHashTable[it->exportName] = targetSymbolAddress; } @@ -1036,18 +1057,20 @@ bool Binder::findExportedSymbolAddress(const char* name, pint_t* result, Bind *isResolverSymbol = true; } + // search this dylib typename NameToAddrMap::iterator pos = fHashTable.find(name); if ( pos != fHashTable.end() ) { *result = pos->second; //fprintf(stderr, "findExportedSymbolAddress(%s) => 0x%08llX in %s\n", name, (uint64_t)*result, this->getDylibID()); *foundIn = this; + *isAbsolute = (fAbsoluteSymbols.count(name) != 0); return true; } - // search re-exports + // search re-exported dylibs for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { if ( it->reExport ) { - if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol) ) + if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol, isAbsolute) ) return true; } } @@ -1065,24 +1088,60 @@ void Binder::addResolverClient(Binder* clientDylib, const char* symbolName fClientAndSymbols.push_back(x); } -// Record that this dylib has an lazy pointer that points within itself for use -// with a resolver function. -template -void Binder::addResolverLazyPointerMappedAddress(const char* symbolName, pint_t lpVMAddr) -{ - SymbolAndLazyPointer x; - x.symbolName = symbolName; - x.lpVMAddr = lpVMAddr; - fSymbolAndLazyPointers.push_back(x); -} template typename A::P::uint_t Binder::findLazyPointerFor(const char* symbolName) { - for (typename std::vector::iterator it = fSymbolAndLazyPointers.begin(); it != fSymbolAndLazyPointers.end(); ++it) { - if ( strcmp(it->symbolName, symbolName) == 0 ) - return it->lpVMAddr; - } + static const bool log = false; + + // first check cache + typename NameToAddrMap::iterator pos = fResolverLazyPointers.find(symbolName); + if ( pos != fResolverLazyPointers.end() ) { + if ( log ) fprintf(stderr, "found cached shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)(pos->second), symbolName, this->getDylibID()); + return pos->second; + } + + // do slow lookup in lazy pointer section + 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 sectionType = sect->flags() & SECTION_TYPE; + if ( sectionType == S_LAZY_SYMBOL_POINTERS) { + uint32_t elementCount = sect->size() / sizeof(pint_t); + const uint32_t indirectTableOffset = sect->reserved1(); + pint_t vmlocation = sect->addr(); + for (uint32_t j=0; j < elementCount; ++j, vmlocation += sizeof(pint_t)) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + const macho_nlist

* aSymbol = &fSymbolTable[symbolIndex]; + const char* aName = &fStrings[aSymbol->n_strx()]; + //fprintf(stderr, " sect=%s, index=%d, symbolIndex=%d, sym=%s\n", sect->sectname(), j, symbolIndex, &fStrings[undefinedSymbol->n_strx()]); + if ( strcmp(aName, symbolName) == 0 ) { + fResolverLazyPointers[symbolName] = vmlocation; + if ( log ) fprintf(stderr, "found slow-path shared lazy pointer at 0x%llX for %s in %s\n", (uint64_t)vmlocation, symbolName, this->getDylibID()); + return vmlocation; + } + break; + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + + if ( log ) fprintf(stderr, "NOT found shared lazy pointer for %s in %s\n", symbolName, this->getDylibID()); return 0; } diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp index 4d6583b..dbcbe76 100644 --- a/launch-cache/MachOLayout.hpp +++ b/launch-cache/MachOLayout.hpp @@ -41,7 +41,7 @@ #include #include -#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -151,6 +151,7 @@ public: // need getDyldInfoExports because export info uses ULEB encoding and size could grow virtual const uint8_t* getDyldInfoExports() const = 0; virtual void setDyldInfoExports(const uint8_t* newExports) const = 0; + virtual void uuid(uuid_t u) const = 0; }; @@ -195,6 +196,7 @@ public: virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; } virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; } virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; } + virtual void uuid(uuid_t u) const { memcpy(u, fUUID, 16); } private: typedef typename A::P P; @@ -230,6 +232,7 @@ private: bool fIsDylib; bool fHasDyldInfo; mutable const uint8_t* fDyldInfoExports; + uuid_t fUUID; }; @@ -245,10 +248,19 @@ public: const std::vector& allLayouts() const { return fLayouts; } private: + class CStringHash { + public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; + }; struct CStringEquals { bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; - typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; + typedef std::unordered_map PathToNode; static bool requestedSlice(const std::set* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType); @@ -422,7 +434,8 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* fDylibID.name = NULL; fDylibID.currentVersion = 0; fDylibID.compatibilityVersion = 0; - + bzero(fUUID, sizeof(fUUID)); + const macho_header

* mh = (const macho_header

*)machHeader; if ( mh->cputype() != arch() ) throw "Layout object is wrong architecture"; @@ -496,6 +509,12 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* fHasDyldInfo = true; dyldInfo = (struct macho_dyld_info_command

*)cmd; break; + case LC_UUID: + { + const macho_uuid_command

* uc = (macho_uuid_command

*)cmd; + memcpy(&fUUID, uc->uuid(), 16); + } + break; } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } diff --git a/launch-cache/MachORebaser.hpp b/launch-cache/MachORebaser.hpp index 88c4f83..4493cc3 100644 --- a/launch-cache/MachORebaser.hpp +++ b/launch-cache/MachORebaser.hpp @@ -120,7 +120,7 @@ private: template Rebaser::Rebaser(const MachOLayoutAbstraction& layout) - : fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL), + : fLayout(layout), fOrignalVMRelocBaseAddress(0), fLinkEditBase(0), fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false), fOrignalVMRelocBaseAddressValid(false), fSkipSplitSegInfoStart(0), fSkipSplitSegInfoEnd(0) { diff --git a/launch-cache/dsc_extractor.cpp b/launch-cache/dsc_extractor.cpp index c94cbe0..4f68041 100644 --- a/launch-cache/dsc_extractor.cpp +++ b/launch-cache/dsc_extractor.cpp @@ -41,13 +41,14 @@ #define NO_ULEB #include "Architectures.hpp" #include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" #include "dsc_iterator.h" #include "dsc_extractor.h" #include #include -#include +#include #include #include @@ -60,15 +61,24 @@ struct seg_info uint64_t sizem; }; +class CStringHash { +public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; class CStringEquals { public: bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } }; -typedef __gnu_cxx::hash_map, __gnu_cxx::hash, CStringEquals> NameToSegments; +typedef std::unordered_map, CStringHash, CStringEquals> NameToSegments; template -int optimize_linkedit(macho_header* mh, const void* mapped_cache, uint64_t* newSize) +int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize) { typedef typename A::P P; typedef typename A::P::E E; @@ -160,31 +170,91 @@ int optimize_linkedit(macho_header* mh, const void* mapped_cache, dataInCodeSize = dataInCode->datasize(); memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize); } + + // look for local symbol info in unmapped part of shared cache + dyldCacheHeader* header = (dyldCacheHeader*)mapped_cache; + macho_nlist

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

* allLocalNlists = (macho_nlist

*)(((uint8_t*)localInfo) + localInfo->nlistOffset()); + const uint32_t entriesCount = localInfo->entriesCount(); + for (uint32_t i=0; i < entriesCount; ++i) { + if ( entries[i].dylibOffset() == textOffsetInCache ) { + uint32_t localNlistStart = entries[i].nlistStartIndex(); + localNlistCount = entries[i].nlistCount(); + localNlists = &allLocalNlists[localNlistStart]; + localStrings = ((char*)localInfo) + localInfo->stringsOffset(); + break; + } + } + } + + // compute number of symbols in new symbol table + const macho_nlist

* const mergedSymTabStart = (macho_nlist

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

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

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // skip any locals in cache + if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT ) + continue; + ++newSymCount; + } + } + // copy symbol entries and strings from original cache file to new mapped dylib file const uint32_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align - const uint32_t newIndSymTabOffset = newSymTabOffset + symtab->nsyms()*sizeof(macho_nlist

); + const uint32_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist

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

* const newSymTabStart = (macho_nlist

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

* const mergedSymTabStart = (macho_nlist

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

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

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

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // if we have better local symbol info, skip any locals here + if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) ) + continue; *t = *s; t->set_n_strx(poolOffset); strcpy(&newStringPoolStart[poolOffset], &mergedStringPoolStart[s->n_strx()]); poolOffset += (strlen(&newStringPoolStart[poolOffset]) + 1); ++t; + ++symbolsCopied; + } + if ( localNlists != NULL ) { + // update load command to reflect new count of locals + dynamicSymTab->set_ilocalsym(symbolsCopied); + dynamicSymTab->set_nlocalsym(localNlistCount); + // copy local symbols + for (uint32_t i=0; i < localNlistCount; ++i) { + const char* localName = &localStrings[localNlists[i].n_strx()]; + *t = localNlists[i]; + t->set_n_strx(poolOffset); + strcpy(&newStringPoolStart[poolOffset], localName); + poolOffset += (strlen(localName) + 1); + ++t; + ++symbolsCopied; + } } + + if ( newSymCount != symbolsCopied ) { + fprintf(stderr, "symbol count miscalculation\n"); + return -1; + } + // pointer align string pool size while ( (poolOffset % sizeof(pint_t)) != 0 ) ++poolOffset; // copy indirect symbol table + uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset); memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t)); // update load commands @@ -196,6 +266,7 @@ int optimize_linkedit(macho_header* mh, const void* mapped_cache, dataInCode->set_dataoff(newDataInCodeOffset); dataInCode->set_datasize(dataInCodeSize); } + symtab->set_nsyms(symbolsCopied); symtab->set_symoff(newSymTabOffset); symtab->set_stroff(newStringPoolOffset); symtab->set_strsize(poolOffset); @@ -226,7 +297,7 @@ static void make_dirs(const char* file_path) lastSlash[1] = '\0'; struct stat stat_buf; if ( stat(dirs, &stat_buf) != 0 ) { - const char* afterSlash = &dirs[1]; + char* afterSlash = &dirs[1]; char* slash; while ( (slash = strchr(afterSlash, '/')) != NULL ) { *slash = '\0'; @@ -254,7 +325,7 @@ size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, c uint32_t nfat_archs = 0; uint32_t offsetInFatFile = 4096; uint8_t *base_ptr = &dylib_data.front(); - + #define FH reinterpret_cast(base_ptr) #define FA reinterpret_cast(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch))) @@ -278,11 +349,12 @@ size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, c // Write regular segments into the buffer uint32_t totalSize = 0; - + uint32_t textOffsetInCache = 0; for( std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { if(strcmp(it->segName, "__TEXT") == 0 ) { - const macho_header

*textMH = reinterpret_cast*>((uint8_t*)mapped_cache+it->offset); + textOffsetInCache = it->offset; + const macho_header

*textMH = reinterpret_cast*>((uint8_t*)mapped_cache+textOffsetInCache); FA->cputype = OSSwapHostToBigInt32(textMH->cputype()); FA->cpusubtype = OSSwapHostToBigInt32(textMH->cpusubtype()); @@ -309,7 +381,7 @@ size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, c // optimize linkedit uint64_t newSize = dylib_data.size(); - optimize_linkedit(((macho_header

*)(base_ptr+offsetInFatFile)), mapped_cache, &newSize); + optimize_linkedit(((macho_header

*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize); // update fat header with new file size dylib_data.resize(offsetInFatFile+newSize); @@ -343,7 +415,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path } close(cache_fd); - + // instantiate arch specific dylib maker size_t (*dylib_create_func)(const void*, std::vector&, const std::vector&) = NULL; if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 ) @@ -365,12 +437,16 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path } // iterate through all images in cache and build map of dylibs and segments - __block NameToSegments map; - dyld_shared_cache_iterate_segments_with_slide(mapped_cache, - ^(const char* dylib, const char* segName, uint64_t offset, uint64_t sizem, - uint64_t mappedddress, uint64_t slide) { - map[dylib].push_back(seg_info(segName, offset, sizem)); - }); + __block NameToSegments map; + __block int result = dyld_shared_cache_iterate(mapped_cache, statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + map[dylibInfo->path].push_back(seg_info(segInfo->name, segInfo->fileOffset, segInfo->fileSize)); + }); + + if(result != 0) { + fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n"); + munmap(mapped_cache, statbuf.st_size); + return result; + } // for each dylib instantiate a dylib file dispatch_group_t group = dispatch_group_create(); @@ -390,7 +466,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path strcat(dylib_path, "/"); strcat(dylib_path, it->first); - //printf("%s with %lu segments\n", dylib_path, segments.size()); + //printf("%s with %lu segments\n", dylib_path, it->second.size()); // make sure all directories in this path exist make_dirs(dylib_path); @@ -457,6 +533,11 @@ int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const c #if 0 +// test program +#include +#include +#include + typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, void (^progress)(unsigned current, unsigned total)); @@ -468,7 +549,8 @@ int main(int argc, const char* argv[]) return 1; } - void* handle = dlopen("/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY); + //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY); + void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY); if ( handle == NULL ) { fprintf(stderr, "dsc_extractor.bundle could not be loaded\n"); return 1; @@ -484,6 +566,8 @@ int main(int argc, const char* argv[]) fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result); return 0; } + + #endif diff --git a/launch-cache/dsc_iterator.cpp b/launch-cache/dsc_iterator.cpp index 50ff8c9..d57be70 100644 --- a/launch-cache/dsc_iterator.cpp +++ b/launch-cache/dsc_iterator.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -41,13 +41,18 @@ namespace dyld { // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped template - const uint8_t* mappedAddress(const uint8_t* cache, uint64_t addr) + const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) { const dyldCacheHeader* header = (dyldCacheHeader*)cache; const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; for (uint32_t i=0; i < header->mappingCount(); ++i) { if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { - return &cache[mappings[i].file_offset() + addr - mappings[i].address()]; + uint32_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); + const uint8_t* result = &cache[cacheOffset]; + if ( result < cacheEnd ) + return result; + else + return NULL; } } return NULL; @@ -55,14 +60,35 @@ namespace dyld { // call the callback block on each segment in this image template - void walkSegments(const uint8_t* cache, const char* dylibPath, const uint8_t* machHeader, uint64_t slide, dyld_shared_cache_iterator_slide_t callback) + int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, const uint8_t* machHeader, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { typedef typename A::P P; typedef typename A::P::E E; + dyld_shared_cache_dylib_info dylibInfo; + dyld_shared_cache_segment_info segInfo; + dylibInfo.version = 1; + dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment + dylibInfo.machHeader = machHeader; + dylibInfo.path = dylibPath; const macho_header

* mh = (const macho_header

*)machHeader; const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

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

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

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

::CMD ) { macho_segment_command

* segCmd = (macho_segment_command

*)cmd; @@ -74,39 +100,72 @@ namespace dyld { uint64_t sizem = segCmd->vmsize(); if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { // clip LINKEDIT size if bigger than cache file - const dyldCacheHeader* header = (dyldCacheHeader*)cache; - const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; - if ( mappings[2].file_offset() <= fileOffset ) { - if ( sizem > mappings[2].size() ) - sizem = mappings[2].file_offset() + mappings[2].size() - fileOffset; - } + if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) + sizem = (cacheEnd-cache)-fileOffset; } - callback(dylibPath, segCmd->segname(), fileOffset, sizem, segCmd->vmaddr()+slide, slide); + segInfo.version = 1; + segInfo.name = segCmd->segname(); + segInfo.fileOffset = fileOffset; + segInfo.fileSize = sizem; + segInfo.address = segCmd->vmaddr(); + callback(&dylibInfo, &segInfo); } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } + return 0; } // call walkSegments on each image in the cache template - int walkImages(const uint8_t* cache, dyld_shared_cache_iterator_slide_t callback) + int walkImages(const uint8_t* cache, uint32_t size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { + // Sanity check there is at least a header + if ( (size > 0) && (size < 0x7000) ) + return -1; typedef typename A::P::E E; typedef typename A::P P; - typedef typename A::P::uint_t pint_t; - const dyldCacheHeader* header = (dyldCacheHeader*)cache; - uint64_t slide = 0; - if ( header->mappingOffset() >= 0x48 ) { - const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; - uint64_t storedPointerToHeader = P::getP(*((pint_t*)&cache[mappings[1].file_offset()])); - slide = storedPointerToHeader - mappings[0].address(); - } - const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + uint64_t greatestMappingOffset = 0; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (size != 0) && (mappings[i].file_offset() > size) ) + return -1; + uint64_t endOffset = mappings[i].file_offset()+mappings[i].size(); + if ( (size != 0) && (endOffset > size) ) + return -1; + if ( endOffset > greatestMappingOffset ) + greatestMappingOffset = endOffset; + } + const uint8_t* cacheEnd = &cache[size]; + if ( size == 0 ) { + // Zero size means old API is being used, assume all mapped + cacheEnd = &cache[greatestMappingOffset]; + } + else { + // verifiy mappings are not bigger than size + if ( size < greatestMappingOffset ) + return -1; + } + // verify all image infos are mapped + if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd ) + return -1; + const uint8_t* firstSeg = NULL; for (uint32_t i=0; i < header->imagesCount(); ++i) { const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); - const uint8_t* machHeader = mappedAddress(cache, dylibs[i].address()); - walkSegments(cache, dylibPath, machHeader, slide, callback); + if ( (const uint8_t*)dylibPath > cacheEnd ) + return -1; + const uint8_t* machHeader = mappedAddress(cache, cacheEnd, dylibs[i].address()); + if ( machHeader == NULL ) + return -1; + if ( machHeader > cacheEnd ) + return -1; + if ( firstSeg == NULL ) + firstSeg = machHeader; + int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, machHeader, callback); + if ( result != 0 ) + return result; } return 0; } @@ -118,25 +177,34 @@ namespace dyld { // this routine will call the callback block once for each segment // in each dylib in the shared cache file. // Returns -1 if there was an error, otherwise 0. -int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) -{ +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { const uint8_t* cache = (uint8_t*)shared_cache_file; if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) - return dyld::walkImages(cache, callback); + return dyld::walkImages(cache, shared_cache_size, callback); else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) - return dyld::walkImages(cache, callback); + return dyld::walkImages(cache, shared_cache_size, callback); else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) - return dyld::walkImages(cache, callback); + return dyld::walkImages(cache, shared_cache_size, callback); else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) - return dyld::walkImages(cache, callback); + return dyld::walkImages(cache, shared_cache_size, callback); else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) - return dyld::walkImages(cache, callback); + return dyld::walkImages(cache, shared_cache_size, callback); else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) - return dyld::walkImages(cache, callback); + return dyld::walkImages(cache, shared_cache_size, callback); else return -1; } + +// implement old version by calling new version +int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) +{ + return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0); + }); +} + // implement non-block version by calling block version int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData) { diff --git a/launch-cache/dsc_iterator.h b/launch-cache/dsc_iterator.h index 981386f..b08eecc 100644 --- a/launch-cache/dsc_iterator.h +++ b/launch-cache/dsc_iterator.h @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -23,25 +23,55 @@ */ #include +#include + +struct dyld_shared_cache_dylib_info { + uint32_t version; // initial version 1 + uint32_t isAlias; // this is alternate path (symlink) + const void* machHeader; // of dylib in mapped cached file + const char* path; // of dylib + const uuid_t* uuid; // of dylib, or NULL is missing + // above fields all exist in version 1 +}; +typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info; + +struct dyld_shared_cache_segment_info { + uint64_t version; // initial version 1 + const char* name; // of segment + uint64_t fileOffset; // of segment in cache file + uint64_t fileSize; // of segment + uint64_t address; // of segment when cache mapped with ASLR (sliding) off + // above fields all exist in version 1 +}; +typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info; + #ifdef __cplusplus extern "C" { #endif +// Given a pointer and size of an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment in each dylib +// in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)); + + + +// +// The following iterator functions are deprecated: +// typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress); typedef void (^dyld_shared_cache_iterator_slide_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide); typedef void (*dyld_shared_cache_iterator_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, void* userData); typedef void (*dyld_shared_cache_iterator_slide_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, uint64_t slide, void* userData); -// Given a pointer to an in-memory copy of a dyld shared cache file, -// this routine will call the callback block once for each segment -// in each dylib in the shared cache file. -// Returns -1 if there was an error, otherwise 0. -extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback); -extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback); -extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData); -extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData); +extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData) __attribute__((deprecated)); #ifdef __cplusplus diff --git a/launch-cache/dyld_cache_format.h b/launch-cache/dyld_cache_format.h index 70d897c..9407ff6 100644 --- a/launch-cache/dyld_cache_format.h +++ b/launch-cache/dyld_cache_format.h @@ -41,6 +41,9 @@ struct dyld_cache_header uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file) uint64_t slideInfoOffset; // file offset of kernel slid info uint64_t slideInfoSize; // size of kernel slid info + uint64_t localSymbolsOffset; // file offset of where local symbols are stored + uint64_t localSymbolsSize; // size of local symbols information + uint8_t uuid[16]; // unique value for each shared cache file }; struct dyld_cache_mapping_info { @@ -73,6 +76,24 @@ struct dyld_cache_slide_info }; +struct dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; // offset into this chunk of nlist entries + uint32_t nlistCount; // count of nlist entries + uint32_t stringsOffset; // offset into this chunk of string pool + uint32_t stringsSize; // byte count of string pool + uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry + uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array +}; + +struct dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; // offset in cache file of start of dylib + uint32_t nlistStartIndex; // start index of locals for this dylib + uint32_t nlistCount; // number of local symbols for this dylib +}; + + #define MACOSX_DYLD_SHARED_CACHE_DIR "/var/db/dyld/" #define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" diff --git a/launch-cache/dyld_shared_cache_util.cpp b/launch-cache/dyld_shared_cache_util.cpp index b0b6918..9d4686a 100644 --- a/launch-cache/dyld_shared_cache_util.cpp +++ b/launch-cache/dyld_shared_cache_util.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2009-2010 Apple Inc. All rights reserved. + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -34,9 +34,7 @@ #include #include -#include #include -#include #include "dsc_iterator.h" #include "dyld_cache_format.h" @@ -45,29 +43,37 @@ #include "CacheFileAbstraction.hpp" -#define OP_NULL 0 -#define OP_LIST_DEPENDENCIES 1 -#define OP_LIST_DYLIBS 2 -#define OP_LIST_LINKEDIT 3 - -#define UUID_BYTES 16 +enum Mode { + modeNone, + modeList, + modeMap, + modeDependencies, + modeSlideInfo, + modeLinkEdit, + modeInfo +}; -// Define this here so we can work with or without block support -typedef void (*segment_callback_t)(const char* dylib, const char* segName, uint64_t offset, uint64_t sizem, - uint64_t mappedddress, uint64_t slide, void* userData); +struct Options { + Mode mode; + const char* dependentsOfPath; + const void* mappedCache; + bool printUUIDs; + bool printVMAddrs; + bool printDylibVersions; +}; -struct seg_callback_args { - char *target_path; - uint32_t target_found; - void *mapped_cache; - uint32_t op; - uint8_t print_uuids; - uint8_t print_vmaddrs; - uint8_t print_dylib_versions; +struct Results { + std::map pageToContent; + uint64_t linkeditBase; + bool dependentTargetFound; }; + + + + void usage() { - fprintf(stderr, "Usage: dscutil -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit [ shared-cache-file ]\n"); + fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit | -map [ shared-cache-file ] | -slide_info | -info\n"); } /* @@ -86,6 +92,8 @@ static const char* default_shared_cache_path() { return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7"; #elif __ARM_ARCH_7F__ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f"; +#elif __ARM_ARCH_7S__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s"; #elif __ARM_ARCH_7K__ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k"; #else @@ -93,132 +101,112 @@ static const char* default_shared_cache_path() { #endif } -/* - * Get a vector of all the load commands from the header pointed to by headerAddr - */ -template -std::vector* > get_load_cmds(void *headerAddr) { - typedef typename A::P P; - typedef typename A::P::E E; - - std::vector* > cmd_vector; - - const macho_header

* mh = (const macho_header

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

* const cmds = (macho_load_command

*)((long)mh + sizeof(macho_header

)); - const macho_load_command

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

*)cmd); - cmd = (const macho_load_command

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

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

* dylib_cmd = (macho_dylib_command

*)*it; - const char *name = dylib_cmd->name(); - uint32_t compat_vers = dylib_cmd->compatibility_version(); - uint32_t current_vers = dylib_cmd->current_version(); - - if (print_dylib_versions) { - printf("\t%s", name); - if ( compat_vers != 0xFFFFFFFF ) - printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n", - (compat_vers >> 16), - (compat_vers >> 8) & 0xff, - (compat_vers) & 0xff, - (current_vers >> 16), - (current_vers >> 8) & 0xff, - (current_vers) & 0xff); - else - printf("\n"); - } else { - printf("\t%s\n", name); - } + if ( strcmp(options.dependentsOfPath, dylibInfo->path) != 0 ) + return; + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + + const macho_dylib_command

* dylib_cmd; + const macho_header

* mh = (const macho_header

*)dylibInfo->machHeader; + const macho_load_command

* const cmds = (macho_load_command

*)((uintptr_t)dylibInfo->machHeader + sizeof(macho_header

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

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

*)cmd; + if ( options.printDylibVersions ) { + uint32_t compat_vers = dylib_cmd->compatibility_version(); + uint32_t current_vers = dylib_cmd->current_version(); + printf("\t%s", dylib_cmd->name()); + if ( compat_vers != 0xFFFFFFFF ) { + printf("(compatibility version %u.%u.%u, current version %u.%u.%u)\n", + (compat_vers >> 16), + (compat_vers >> 8) & 0xff, + (compat_vers) & 0xff, + (current_vers >> 16), + (current_vers >> 8) & 0xff, + (current_vers) & 0xff); + } + else { + printf("\n"); + } + } + else { + printf("\t%s\n", dylib_cmd->name()); + } + break; } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } + results.dependentTargetFound = true; } /* - * Print out a dylib from the shared cache, optionally including the UUID + * Print out a dylib from the shared cache, optionally including the UUID or unslid load address */ template -void print_dylib(const char *dylib, void *headerAddr, uint64_t slide, struct seg_callback_args *args) { - typedef typename A::P P; - typedef typename A::P::E E; - char uuid_str[UUID_BYTES*3]; - uint8_t got_uuid = 0; - uint64_t vmaddr = 0; - - std::vector< macho_load_command

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

* uuid_cmd = (macho_uuid_command

*)*it; - const uint8_t *uuid = uuid_cmd->uuid(); - sprintf(uuid_str, "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X>", - uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], - uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); - got_uuid = 1; - } - else if (cmdType == LC_SEGMENT) { - macho_segment_command

* seg_cmd = (macho_segment_command

*)*it; - if (strcmp(seg_cmd->segname(), "__TEXT") == 0) { - vmaddr = seg_cmd->vmaddr(); - } - } - } +void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) +{ + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; - if (args->print_vmaddrs) - printf("0x%08llX ", vmaddr+slide); - if (args->print_uuids) { - if (got_uuid) - printf("%s ", uuid_str); + if ( options.printVMAddrs ) + printf("0x%08llX ", segInfo->address); + if ( options.printUUIDs ) { + if ( dylibInfo->uuid != NULL ) { + const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;; + printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } else printf("< no uuid in dylib > "); } - printf("%s\n", dylib); + if ( dylibInfo->isAlias ) + printf("[alias] %s\n", dylibInfo->path); + else + printf("%s\n", dylibInfo->path); } -uint64_t sLinkeditBase = 0; -std::map sPageToContent; - - -static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, char* message) +static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results) { for (uint32_t p = pageStart; p <= pageEnd; p += 4096) { - std::map::iterator pos = sPageToContent.find(p); - if ( pos == sPageToContent.end() ) { - sPageToContent[p] = strdup(message); + std::map::iterator pos = results.pageToContent.find(p); + if ( pos == results.pageToContent.end() ) { + results.pageToContent[p] = strdup(message); } else { - char* oldMessage = pos->second; + const char* oldMessage = pos->second; char* newMesssage; asprintf(&newMesssage, "%s, %s", oldMessage, message); - sPageToContent[p] = newMesssage; - free(oldMessage); + results.pageToContent[p] = newMesssage; + ::free((void*)oldMessage); } } } @@ -228,15 +216,14 @@ static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, char* message) * get LINKEDIT info for dylib */ template -void process_linkedit(const char* dylib, void* headerAddr) { +void process_linkedit(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) { typedef typename A::P P; typedef typename A::P::E E; - // filter out symlinks by only handling first path found for each mach header - static std::set seenImages; - if ( seenImages.count(headerAddr) != 0 ) + // filter out symlinks + if ( dylibInfo->isAlias ) return; - seenImages.insert(headerAddr); - const macho_header

* mh = (const macho_header

*)headerAddr; + const macho_header

* mh = (const macho_header

*)dylibInfo->machHeader; uint32_t ncmds = mh->ncmds(); const macho_load_command

* const cmds = (macho_load_command

*)((long)mh + sizeof(macho_header

)); const macho_load_command

* cmd = cmds; @@ -244,35 +231,35 @@ void process_linkedit(const char* dylib, void* headerAddr) { if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) { macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); @@ -280,163 +267,139 @@ void process_linkedit(const char* dylib, void* headerAddr) { } - /* - * This callback is used with dsc_iterator, and called once for each segment in the target shared cache + * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built */ template -void segment_callback(const char *dylib, const char *segName, uint64_t offset, uint64_t sizem, - uint64_t mappedAddress, uint64_t slide, void *userData) { - typedef typename A::P P; - typedef typename A::P::E E; - struct seg_callback_args *args = (struct seg_callback_args *)userData; - if (strncmp(segName, "__TEXT", 6) == 0) { - int target_match = args->target_path ? (strcmp(args->target_path, dylib) == 0) : 0; - if (!args->target_path || target_match) { - if (target_match) { - args->target_found = 1; - } - void *headerAddr = (void*)((long)args->mapped_cache + (long)offset); - switch (args->op) { - case OP_LIST_DEPENDENCIES: - list_dependencies(dylib, headerAddr, args->print_dylib_versions); - break; - case OP_LIST_DYLIBS: - print_dylib(dylib, headerAddr, slide, args); - break; - case OP_LIST_LINKEDIT: - process_linkedit(dylib, headerAddr); - default: - break; - } - } - } - else if (strncmp(segName, "__LINKEDIT", 6) == 0) { - sLinkeditBase = mappedAddress - offset; - } +void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, const Options& options, Results& results) { + if ( !dylibInfo->isAlias ) + printf("0x%08llX - 0x%08llX %s %s\n", segInfo->address, segInfo->address + segInfo->fileSize, segInfo->name, dylibInfo->path); } +static void checkMode(Mode mode) { + if ( mode != modeNone ) { + fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, or -map\n"); + usage(); + exit(1); + } +} + +int main (int argc, const char* argv[]) { + const char* sharedCachePath = default_shared_cache_path(); -int main (int argc, char **argv) { - struct seg_callback_args args; - const char *shared_cache_path = NULL; - void *mapped_cache; - struct stat statbuf; - int cache_fd; - char c; - bool print_slide_info = false; - - args.target_path = NULL; - args.op = OP_NULL; - args.print_uuids = 0; - args.print_vmaddrs = 0; - args.print_dylib_versions = 0; - args.target_found = 0; + Options options; + options.mode = modeNone; + options.printUUIDs = false; + options.printVMAddrs = false; + options.printDylibVersions = false; + options.dependentsOfPath = NULL; - for (uint32_t optind = 1; optind < argc; optind++) { - char *opt = argv[optind]; + for (uint32_t i = 1; i < argc; i++) { + const char* opt = argv[i]; if (opt[0] == '-') { if (strcmp(opt, "-list") == 0) { - if (args.op) { - fprintf(stderr, "Error: select one of -list or -dependents\n"); - usage(); - exit(1); - } - args.op = OP_LIST_DYLIBS; - } else if (strcmp(opt, "-dependents") == 0) { - if (args.op) { - fprintf(stderr, "Error: select one of -list or -dependents\n"); - usage(); - exit(1); - } - if (!(++optind < argc)) { + checkMode(options.mode); + options.mode = modeList; + } + else if (strcmp(opt, "-dependents") == 0) { + checkMode(options.mode); + options.mode = modeDependencies; + options.dependentsOfPath = argv[++i]; + if ( i >= argc ) { fprintf(stderr, "Error: option -depdendents requires an argument\n"); usage(); exit(1); } - args.op = OP_LIST_DEPENDENCIES; - args.target_path = argv[optind]; - } else if (strcmp(opt, "-uuid") == 0) { - args.print_uuids = 1; - } else if (strcmp(opt, "-versions") == 0) { - args.print_dylib_versions = 1; - } else if (strcmp(opt, "-vmaddr") == 0) { - args.print_vmaddrs = 1; - } else if (strcmp(opt, "-linkedit") == 0) { - args.op = OP_LIST_LINKEDIT; - } else if (strcmp(opt, "-slide_info") == 0) { - print_slide_info = true; - } else { + } + else if (strcmp(opt, "-linkedit") == 0) { + checkMode(options.mode); + options.mode = modeLinkEdit; + } + else if (strcmp(opt, "-info") == 0) { + checkMode(options.mode); + options.mode = modeInfo; + } + else if (strcmp(opt, "-slide_info") == 0) { + checkMode(options.mode); + options.mode = modeSlideInfo; + } + else if (strcmp(opt, "-map") == 0) { + checkMode(options.mode); + options.mode = modeMap; + } + else if (strcmp(opt, "-uuid") == 0) { + options.printUUIDs = true; + } + else if (strcmp(opt, "-versions") == 0) { + options.printDylibVersions = true; + } + else if (strcmp(opt, "-vmaddr") == 0) { + options.printVMAddrs = true; + } + else { fprintf(stderr, "Error: unrecognized option %s\n", opt); usage(); exit(1); } - } else { - shared_cache_path = opt; + } + else { + sharedCachePath = opt; } } - if ( !print_slide_info ) { - if (args.op == OP_NULL) { - fprintf(stderr, "Error: select one of -list or -dependents\n"); - usage(); - exit(1); - } + if ( options.mode == modeNone ) { + fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n"); + usage(); + exit(1); + } - if (args.print_uuids && args.op != OP_LIST_DYLIBS) + if ( options.mode != modeSlideInfo ) { + if ( options.printUUIDs && (options.mode != modeList) ) fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n"); - if (args.print_vmaddrs && args.op != OP_LIST_DYLIBS) + + if ( options.printVMAddrs && (options.mode != modeList) ) fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n"); - if (args.print_dylib_versions && args.op != OP_LIST_DEPENDENCIES) + + if ( options.printDylibVersions && (options.mode != modeDependencies) ) fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n"); - if (args.op == OP_LIST_DEPENDENCIES && !args.target_path) { + if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) { fprintf(stderr, "Error: -dependents given, but no dylib path specified\n"); usage(); exit(1); } } - - if (!shared_cache_path) - shared_cache_path = default_shared_cache_path(); - - if (stat(shared_cache_path, &statbuf)) { - fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_path); + + struct stat statbuf; + if ( ::stat(sharedCachePath, &statbuf) == -1 ) { + fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno); exit(1); } - cache_fd = open(shared_cache_path, O_RDONLY); - if (cache_fd < 0) { - fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_path); + int cache_fd = ::open(sharedCachePath, O_RDONLY); + if ( cache_fd < 0 ) { + fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno); exit(1); } - mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); - if (mapped_cache == MAP_FAILED) { - fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_path, errno); + options.mappedCache = ::mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (options.mappedCache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno); exit(1); } - if ( print_slide_info ) { - const dyldCacheHeader* header = (dyldCacheHeader*)mapped_cache; - if ( (strcmp(header->magic(), "dyld_v1 x86_64") != 0) - && (strcmp(header->magic(), "dyld_v1 armv6") != 0) - && (strcmp(header->magic(), "dyld_v1 armv7") != 0) - && (strcmp(header->magic(), "dyld_v1 armv7f") != 0) - && (strcmp(header->magic(), "dyld_v1 armv7k") != 0) ) { - fprintf(stderr, "Error: unrecognized dyld shared cache magic or arch does not support sliding\n"); - exit(1); - } + if ( options.mode == modeSlideInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; if ( header->slideInfoOffset() == 0 ) { fprintf(stderr, "Error: dyld shared cache does not contain slide info\n"); exit(1); } - const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)mapped_cache + header->mappingOffset()); + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); const dyldCacheFileMapping* dataMapping = &mappings[1]; uint64_t dataStartAddress = dataMapping->address(); uint64_t dataSize = dataMapping->size(); - const dyldCacheSlideInfo* slideInfoHeader = (dyldCacheSlideInfo*)((char*)mapped_cache+header->slideInfoOffset()); + const dyldCacheSlideInfo* slideInfoHeader = (dyldCacheSlideInfo*)((char*)options.mappedCache+header->slideInfoOffset()); printf("slide info version=%d\n", slideInfoHeader->version()); printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096); const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset()); @@ -447,53 +410,121 @@ int main (int argc, char **argv) { printf("%02X", entry->bits[j]); printf("\n"); } - - + } + else if ( options.mode == modeInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + printf("uuid: "); + if ( header->mappingOffset() >= 0x68 ) { + const uint8_t* uuid = header->uuid(); + printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + else { + printf("n/a\n"); + } + printf("image count: %u\n", header->imagesCount()); + printf("mappings:\n"); + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( mappings[i].init_prot() & VM_PROT_EXECUTE ) + printf(" __TEXT %lluMB\n", mappings[i].size()/(1024*1024)); + else if ( mappings[i]. init_prot() & VM_PROT_WRITE ) + printf(" __DATA %lluMB\n", mappings[i].size()/(1024*1024)); + else if ( mappings[i].init_prot() & VM_PROT_READ ) + printf(" __LINKEDIT %lluMB\n", mappings[i].size()/(1024*1024)); + } } else { segment_callback_t callback; - if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 ) - callback = segment_callback; - else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 ) - callback = segment_callback; - else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 ) - callback = segment_callback; - else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 ) - callback = segment_callback; - else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 ) - callback = segment_callback; - else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7f") == 0 ) - callback = segment_callback; - else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7k") == 0 ) - callback = segment_callback; - else { + if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + break; + } + } + else if ( strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0 ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + break; + } + } + else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0) + || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + break; + } + } + else { fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); exit(1); } - - args.mapped_cache = mapped_cache; - - #if __BLOCKS__ - // Shim to allow building for the host - void *argsPtr = &args; - dyld_shared_cache_iterate_segments_with_slide(mapped_cache, - ^(const char* dylib, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide ) { - (callback)(dylib, segName, offset, size, mappedddress, slide, argsPtr); + + __block Results results; + results.dependentTargetFound = false; + int iterateResult = dyld_shared_cache_iterate(options.mappedCache, statbuf.st_size, + ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) { + (callback)(dylibInfo, segInfo, options, results); }); - #else - dyld_shared_cache_iterate_segments_with_slide_nb(mapped_cache, callback, &args); - #endif + if ( iterateResult != 0 ) { + fprintf(stderr, "Error: malformed shared cache file\n"); + exit(1); + } - if (args.op == OP_LIST_LINKEDIT) { + if ( options.mode == modeLinkEdit ) { // dump -linkedit information - for (std::map::iterator it = sPageToContent.begin(); it != sPageToContent.end(); ++it) { - printf("0x%0llX %s\n", sLinkeditBase+it->first, it->second); + for (std::map::iterator it = results.pageToContent.begin(); it != results.pageToContent.end(); ++it) { + printf("0x%08X %s\n", it->first, it->second); } } - - if (args.target_path && !args.target_found) { - fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", args.target_path, shared_cache_path); + if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) { + fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath); exit(1); } } diff --git a/launch-cache/update_dyld_shared_cache.cpp b/launch-cache/update_dyld_shared_cache.cpp index ac9cc1c..73cb114 100644 --- a/launch-cache/update_dyld_shared_cache.cpp +++ b/launch-cache/update_dyld_shared_cache.cpp @@ -31,7 +31,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -44,25 +46,29 @@ #include #include #include +#include +#include +#include +#include #include "dyld_cache_format.h" #include #include #include -#include +#include #include "Architectures.hpp" #include "MachOLayout.hpp" #include "MachORebaser.hpp" #include "MachOBinder.hpp" #include "CacheFileAbstraction.hpp" +#include "dyld_cache_config.h" #define SELOPT_WRITE #include "objc-shared-cache.h" -#define FIRST_DYLIB_TEXT_OFFSET 0x7000 -#define FIRST_DYLIB_DATA_OFFSET 0x1000 +#define FIRST_DYLIB_TEXT_OFFSET 0x8000 #ifndef LC_FUNCTION_STARTS #define LC_FUNCTION_STARTS 0x26 @@ -95,15 +101,27 @@ static void warn(const char *arch, const char *format, ...) } -static uint64_t pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } +class CStringHash { +public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; +class CStringEquals +{ +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; + + class ArchGraph { public: - struct CStringEquals { - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_map, CStringEquals> StringToString; + typedef std::unordered_map StringToString; static void addArchPair(ArchPair ap); static void addRoot(const char* vpath, const std::set& archs); @@ -140,7 +158,7 @@ private: std::set fRootsDependentOnThis; }; - typedef __gnu_cxx::hash_map, CStringEquals> PathToNode; + typedef std::unordered_map PathToNode; ArchGraph(ArchPair ap) : fArchPair(ap) {} @@ -325,12 +343,25 @@ ArchGraph::DependencyNode* ArchGraph::getNode(const char* path) //fprintf(stderr, "adding %s node alias %s for %s\n", archName(fArchPair), node->getLayout()->getID().name, realPath); pos = fNodes.find(node->getLayout()->getID().name); if ( pos != fNodes.end() ) { - // warn if two dylib in cache have same install_name - char* msg; - asprintf(&msg, "update_dyld_shared_cache: warning, found two dylibs with same install path: %s\n\t%s\n\t%s\n", - node->getLayout()->getID().name, pos->second->getPath(), node->getPath()); - fprintf(stderr, "%s", msg); - warnings.push_back(msg); + // get uuids of two dylibs to see if this is accidental copy of a dylib or two differnent dylibs with same -install_name + uuid_t uuid1; + uuid_t uuid2; + node->getLayout()->uuid(uuid1); + pos->second->getLayout()->uuid(uuid2); + if ( memcmp(&uuid1, &uuid2, 16) == 0 ) { + // warn if two dylib in cache have same install_name + char* msg; + asprintf(&msg, "update_dyld_shared_cache: warning, found two copies of the same dylib with same install path: %s\n\t%s\n\t%s\n", + node->getLayout()->getID().name, pos->second->getPath(), node->getPath()); + fprintf(stderr, "%s", msg); + warnings.push_back(msg); + } + else { + // update_dyld_shared_cache should fail if two images have same install name + fprintf(stderr, "update_dyld_shared_cache: found two different dylibs with same install path: %s\n\t%s\n\t%s\n", + node->getLayout()->getID().name, pos->second->getPath(), node->getPath()); + exit(1); + } } else fNodes[node->getLayout()->getID().name] = node; @@ -508,6 +539,8 @@ const char* ArchGraph::archName(ArchPair ap) return "armv7f"; case CPU_SUBTYPE_ARM_V7K: return "armv7k"; + case CPU_SUBTYPE_ARM_V7S: + return "armv7s"; default: return "arm"; } @@ -595,6 +628,82 @@ bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, c } + +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 std::unordered_map StringToOffset; + + char* fBuffer; + uint32_t fBufferAllocated; + uint32_t fBufferUsed; + StringToOffset fUniqueStrings; +}; + + +StringPool::StringPool() + : fBufferUsed(0), fBufferAllocated(48*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 + throw "string buffer exhausted"; + } + 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]; +} + + + +struct LocalSymbolInfo +{ + uint32_t dylibOffset; + uint32_t nlistStartIndex; + uint32_t nlistCount; +}; + + template class SharedCache { @@ -602,7 +711,7 @@ public: SharedCache(ArchGraph* graph, const char* rootPath, const char* overlayPath, const char* cacheDir, bool explicitCacheDir, bool alphaSort, bool verify, bool optimize, uint64_t dyldBaseAddress); bool update(bool force, bool optimize, bool deleteExistingFirst, int archIndex, - int archCount, bool keepSignatures); + int archCount, bool keepSignatures, bool dontMapLocalSymbols); static const char* cacheFileSuffix(bool optimized, const char* archName); // vm address = address AS WRITTEN into the cache @@ -622,18 +731,19 @@ private: bool notUpToDate(const char* path, unsigned int aliasCount); bool notUpToDate(const void* cache, unsigned int aliasCount); - uint8_t* optimizeLINKEDIT(bool keepSignatures); + uint8_t* optimizeLINKEDIT(bool keepSignatures, bool dontMapLocalSymbols); void optimizeObjC(std::vector& pointersInData); static void getSharedCacheBasAddresses(cpu_type_t arch, uint64_t* baseReadOnly, uint64_t* baseWritable); static cpu_type_t arch(); - static uint64_t sharedRegionReadOnlyStartAddress(); - static uint64_t sharedRegionWritableStartAddress(); - static uint64_t sharedRegionReadOnlySize(); - static uint64_t sharedRegionWritableSize(); + static uint64_t sharedRegionStartAddress(); + static uint64_t sharedRegionSize(); + static uint64_t sharedRegionStartWritableAddress(uint64_t); + static uint64_t sharedRegionStartReadOnlyAddress(uint64_t, uint64_t); static uint64_t getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide); static bool addCacheSlideInfo(); + static uint64_t pageAlign(uint64_t addr); void assignNewBaseAddresses(bool verify); struct LayoutInfo { @@ -695,6 +805,9 @@ private: std::vector fDylibs; std::vector fDylibAliases; std::vector fMappings; + std::vector > fUnmappedLocalSymbols; + StringPool fUnmappedLocalsStringPool; + std::vector fLocalSymbolInfos; uint32_t fHeaderSize; uint8_t* fInMemoryCache; uint64_t fDyldBaseAddress; @@ -718,6 +831,7 @@ private: uint32_t fOffsetOfDataInCodeInCombinedLinkedit; uint32_t fSizeOfDataInCodeInCombinedLinkedit; uint32_t fLinkEditsTotalOptimizedSize; + uint32_t fUnmappedLocalSymbolsSize; }; @@ -814,21 +928,21 @@ template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_I386; } template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_X86_64; } template <> cpu_type_t SharedCache::arch() { return CPU_TYPE_ARM; } -template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x90000000; } -template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x7FFF80000000LL; } -template <> uint64_t SharedCache::sharedRegionReadOnlyStartAddress() { return 0x30000000; } +template <> uint64_t SharedCache::sharedRegionStartAddress() { return 0x90000000; } +template <> uint64_t SharedCache::sharedRegionStartAddress() { return 0x7FFF80000000LL; } +template <> uint64_t SharedCache::sharedRegionStartAddress() { return ARM_SHARED_REGION_START; } -template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0xAC000000; } -template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x7FFF70000000LL; } -template <> uint64_t SharedCache::sharedRegionWritableStartAddress() { return 0x3E000000; } +template <> uint64_t SharedCache::sharedRegionSize() { return 0x20000000; } +template <> uint64_t SharedCache::sharedRegionSize() { return 0x40000000; } +template <> uint64_t SharedCache::sharedRegionSize() { return ARM_SHARED_REGION_SIZE; } -template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x1C000000; } -template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x40000000; } -template <> uint64_t SharedCache::sharedRegionReadOnlySize() { return 0x0E000000; } +template <> uint64_t SharedCache::sharedRegionStartWritableAddress(uint64_t exEnd) { return exEnd + 0x04000000; } +template <> uint64_t SharedCache::sharedRegionStartWritableAddress(uint64_t exEnd) { return 0x7FFF70000000LL; } +template <> uint64_t SharedCache::sharedRegionStartWritableAddress(uint64_t exEnd) { return (exEnd + 16383) & (-16384); } -template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x04000000; } -template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x10000000; } -template <> uint64_t SharedCache::sharedRegionWritableSize() { return 0x02000000; } +template <> uint64_t SharedCache::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd) { return wrEnd + 0x04000000; } +template <> uint64_t SharedCache::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd){ return exEnd; } +template <> uint64_t SharedCache::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd) { return (wrEnd + 16383) & (-16384); } template <> const char* SharedCache::archName() { return "i386"; } @@ -839,6 +953,12 @@ template <> const char* SharedCache::cacheFileSuffix(bool, const char* arc template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName){ return archName; } template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName) { return archName; } + +template <> uint64_t SharedCache::pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } +template <> uint64_t SharedCache::pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } +template <> uint64_t SharedCache::pageAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } + + template SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* overlayPath, const char* cacheDir, bool explicitCacheDir, bool alphaSort, bool verify, bool optimize, uint64_t dyldBaseAddress) : fArchGraph(graph), fVerify(verify), fExistingIsNotUpToDate(true), @@ -851,7 +971,8 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* fOffsetOfOldIndirectSymbolsInCombinedLinkedit(0), fSizeOfOldIndirectSymbolsInCombinedLinkedit(0), fOffsetOfOldStringPoolInCombinedLinkedit(0), fSizeOfOldStringPoolInCombinedLinkedit(0), fOffsetOfFunctionStartsInCombinedLinkedit(0), fSizeOfFunctionStartsInCombinedLinkedit(0), - fOffsetOfDataInCodeInCombinedLinkedit(0), fSizeOfDataInCodeInCombinedLinkedit(0) + fOffsetOfDataInCodeInCombinedLinkedit(0), fSizeOfDataInCodeInCombinedLinkedit(0), + fUnmappedLocalSymbolsSize(0) { if ( fArchGraph->getArchPair().arch != arch() ) throwf("SharedCache object is wrong architecture: 0x%08X vs 0x%08X", fArchGraph->getArchPair().arch, arch()); @@ -950,6 +1071,7 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* fHeaderSize = sizeof(dyld_cache_header) + fMappings.size()*sizeof(shared_file_mapping_np) + (fDylibs.size()+aliasCount)*sizeof(dyld_cache_image_info); + const uint64_t baseHeaderSize = fHeaderSize; //fprintf(stderr, "aliasCount=%d, fHeaderSize=0x%08X\n", aliasCount, fHeaderSize); // build list of aliases and compute where each ones path string will go for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { @@ -973,21 +1095,21 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* // if no existing cache, say so if ( fExistingCacheForVerification == NULL ) { throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n", - getpid(), archName()); + getpid(), fArchGraph->archName()); } const dyldCacheHeader* header = (dyldCacheHeader*)fExistingCacheForVerification; const dyldCacheImageInfo* cacheEntry = (dyldCacheImageInfo*)(fExistingCacheForVerification + header->imagesOffset()); for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it, ++cacheEntry) { if ( cacheEntry->address() != it->layout->getSegments()[0].newAddress() ) { throwf("update_dyld_shared_cache[%u] warning: for arch=%s, could not verify cache because start address of %s is 0x%llX in cache, but should be 0x%llX\n", - getpid(), archName(), it->layout->getID().name, cacheEntry->address(), it->layout->getSegments()[0].newAddress()); + getpid(), fArchGraph->archName(), it->layout->getID().name, cacheEntry->address(), it->layout->getSegments()[0].newAddress()); } } } if ( fHeaderSize > FIRST_DYLIB_TEXT_OFFSET ) - throwf("header size miscalculation 0x%08X", fHeaderSize); + throwf("header size overflow: allowed=0x%08X, base=0x%08llX, aliases=0x%08llX", FIRST_DYLIB_TEXT_OFFSET, baseHeaderSize, fHeaderSize-baseHeaderSize); } @@ -1001,131 +1123,80 @@ uint64_t SharedCache::getWritableSegmentNewAddress(uint64_t proposedNewAddres template void SharedCache::assignNewBaseAddresses(bool verify) { - uint64_t sharedCacheStartAddress = sharedRegionReadOnlyStartAddress(); -#if 0 - if ( arch() == CPU_TYPE_X86_64 ) { - if ( verify ) { - if ( fExistingCacheForVerification == NULL ) { - throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n", - getpid(), archName()); - } - const dyldCacheHeader* header = (dyldCacheHeader*)fExistingCacheForVerification; - const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)(fExistingCacheForVerification + header->mappingOffset()); - sharedCacheStartAddress = mappings[0].address(); - } - else { - // dyld shared cache can be more random - uint64_t readOnlySize = 0; - for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { - if ( ! it->layout->hasSplitSegInfo() ) - continue; - std::vector& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments(); - for (int i=0; i < segs.size(); ++i) { - MachOLayoutAbstraction::Segment& seg = segs[i]; - if ( ! seg.writable() ) - readOnlySize += pageAlign(seg.size()); - } + // first layout TEXT for dylibs + const uint64_t startExecuteAddress = sharedRegionStartAddress(); + uint64_t currentExecuteAddress = startExecuteAddress + FIRST_DYLIB_TEXT_OFFSET; + 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]; + seg.reset(); + if ( seg.executable() && !seg.writable() ) { + // __TEXT segment + if ( it->info.address == 0 ) + it->info.address = currentExecuteAddress; + seg.setNewAddress(currentExecuteAddress); + currentExecuteAddress += pageAlign(seg.size()); } - uint64_t maxSlide = sharedRegionReadOnlySize() - (readOnlySize + FIRST_DYLIB_TEXT_OFFSET); - sharedCacheStartAddress = sharedRegionReadOnlyStartAddress() + pageAlign(arc4random() % maxSlide); } } -#endif - uint64_t currentExecuteAddress = sharedCacheStartAddress + FIRST_DYLIB_TEXT_OFFSET; - uint64_t currentWritableAddress = sharedRegionWritableStartAddress() + FIRST_DYLIB_DATA_OFFSET; - // first layout TEXT and DATA for dylibs + // layout DATA for dylibs + const uint64_t startWritableAddress = sharedRegionStartWritableAddress(currentExecuteAddress); + uint64_t currentWritableAddress = startWritableAddress; 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]; seg.reset(); if ( seg.writable() ) { - if ( seg.executable() && it->layout->hasSplitSegInfo() ) { - // skip __IMPORT segments in this pass - } - else { - // __DATA segment - 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 - } + if ( seg.executable() ) + throw "found writable and executable segment"; + // __DATA segment + seg.setNewAddress(currentWritableAddress); + currentWritableAddress = pageAlign(seg.newAddress() + 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; + // layout all read-only (but not LINKEDIT) segments + const uint64_t startReadOnlyAddress = sharedRegionStartReadOnlyAddress(currentWritableAddress, currentExecuteAddress); + uint64_t currentReadOnlyAddress = startReadOnlyAddress; 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 + if ( seg.readable() && !seg.writable() && !seg.executable() && (strcmp(seg.name(), "__LINKEDIT") != 0) ) { + // __UNICODE segment 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); - currentWritableExecutableAddress += pageAlign(seg.size()); - } } } - // append all LINKEDIT segments at end of all read-only segments + // layout 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 ( seg.readable() && !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); - - // i386 dyld shared cache overflows after adding libclh.dylib - if ( (currentReadOnlyAddress - sharedRegionReadOnlyStartAddress()) > sharedRegionReadOnlySize() ) - throwf("read-only slice of cache too big: %lluMB (max %lluMB)", - (currentReadOnlyAddress - sharedRegionReadOnlyStartAddress())/(1024*1024), - sharedRegionReadOnlySize()/(1024*1024)); - + fLinkEditsTotalUnoptimizedSize = pageAlign(currentReadOnlyAddress - fLinkEditsStartAddress); // populate large mappings uint64_t cacheFileOffset = 0; - if ( currentExecuteAddress > sharedCacheStartAddress + FIRST_DYLIB_TEXT_OFFSET ) { + if ( currentExecuteAddress > startExecuteAddress ) { shared_file_mapping_np executeMapping; - executeMapping.sfm_address = sharedCacheStartAddress; - executeMapping.sfm_size = currentExecuteAddress - sharedCacheStartAddress; + executeMapping.sfm_address = startExecuteAddress; + executeMapping.sfm_size = currentExecuteAddress - startExecuteAddress; 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; @@ -1133,29 +1204,18 @@ void SharedCache::assignNewBaseAddresses(bool verify) cacheFileOffset += executeMapping.sfm_size; shared_file_mapping_np writableMapping; - writableMapping.sfm_address = sharedRegionWritableStartAddress(); - writableMapping.sfm_size = currentWritableAddress - sharedRegionWritableStartAddress(); + writableMapping.sfm_address = startWritableAddress; + writableMapping.sfm_size = currentWritableAddress - startWritableAddress; 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; - writableExecutableMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_WRITE | 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_address = startReadOnlyAddress; + readOnlyMapping.sfm_size = currentReadOnlyAddress - startReadOnlyAddress; readOnlyMapping.sfm_file_offset = cacheFileOffset; readOnlyMapping.sfm_max_prot = VM_PROT_READ; readOnlyMapping.sfm_init_prot = VM_PROT_READ; @@ -1165,7 +1225,7 @@ void SharedCache::assignNewBaseAddresses(bool verify) else { // empty cache shared_file_mapping_np cacheHeaderMapping; - cacheHeaderMapping.sfm_address = sharedRegionWritableStartAddress(); + cacheHeaderMapping.sfm_address = startExecuteAddress; cacheHeaderMapping.sfm_size = FIRST_DYLIB_TEXT_OFFSET; cacheHeaderMapping.sfm_file_offset = cacheFileOffset; cacheHeaderMapping.sfm_max_prot = VM_PROT_READ; @@ -1322,7 +1382,7 @@ bool SharedCache::notUpToDate(const char* path, unsigned int aliasCount) struct stat stat_buf; ::fstat(fd, &stat_buf); uint32_t cacheFileSize = stat_buf.st_size; - uint32_t cacheAllocatedSize = (cacheFileSize + 4095) & (-4096); + uint32_t cacheAllocatedSize = pageAlign(cacheFileSize); uint8_t* mappingAddr = NULL; if ( vm_allocate(mach_task_self(), (vm_address_t*)(&mappingAddr), cacheAllocatedSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS ) throwf("can't vm_allocate cache of size %u", cacheFileSize); @@ -1348,76 +1408,6 @@ bool SharedCache::notUpToDate(const char* path, unsigned int aliasCount) 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(32*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 - throw "string buffer exhausted"; - } - 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 @@ -1431,7 +1421,10 @@ public: void copyWeakBindInfo(uint32_t&); void copyLazyBindInfo(uint32_t&); void copyExportInfo(uint32_t&); - void copyLocalSymbols(uint32_t symbolTableOffset, uint32_t&); + void copyLocalSymbols(uint32_t symbolTableOffset, uint32_t&, bool dontMapLocalSymbols, + uint8_t* cacheStart, StringPool& unmappedLocalsStringPool, + std::vector >& unmappedSymbols, + std::vector& info); void copyExportedSymbols(uint32_t symbolTableOffset, uint32_t&); void copyImportedSymbols(uint32_t symbolTableOffset, uint32_t&); void copyExternalRelocations(uint32_t& offset); @@ -1482,6 +1475,8 @@ private: uint32_t fIndirectSymbolTableOffsetInfoNewLinkEdit; uint32_t fFunctionStartsOffsetInNewLinkEdit; uint32_t fDataInCodeOffsetInNewLinkEdit; + uint32_t fUnmappedLocalSymbolsStartIndexInNewLinkEdit; + uint32_t fUnmappedLocalSymbolsCountInNewLinkEdit; }; @@ -1500,7 +1495,8 @@ LinkEditOptimizer::LinkEditOptimizer(const MachOLayoutAbstraction& layout, co fExportedSymbolsStartIndexInNewLinkEdit(0), fExportedSymbolsCountInNewLinkEdit(0), fImportSymbolsStartIndexInNewLinkEdit(0), fImportedSymbolsCountInNewLinkEdit(0), fExternalRelocationsOffsetIntoNewLinkEdit(0), fIndirectSymbolTableOffsetInfoNewLinkEdit(0), - fFunctionStartsOffsetInNewLinkEdit(0), fDataInCodeOffsetInNewLinkEdit(0) + fFunctionStartsOffsetInNewLinkEdit(0), fDataInCodeOffsetInNewLinkEdit(0), + fUnmappedLocalSymbolsStartIndexInNewLinkEdit(0), fUnmappedLocalSymbolsCountInNewLinkEdit(0) { fHeader = (const macho_header

*)fLayout.getSegments()[0].mappedAddress(); @@ -1609,25 +1605,46 @@ void LinkEditOptimizer::copyExportInfo(uint32_t& offset) } - template -void LinkEditOptimizer::copyLocalSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex) +void LinkEditOptimizer::copyLocalSymbols(uint32_t symbolTableOffset, uint32_t& symbolIndex, bool dontMapLocalSymbols, uint8_t* cacheStart, + StringPool& unmappedLocalsStringPool, std::vector >& unmappedSymbols, + std::vector& dylibInfos) { fLocalSymbolsStartIndexInNewLinkEdit = symbolIndex; + LocalSymbolInfo localInfo; + localInfo.dylibOffset = ((uint8_t*)fHeader) - cacheStart; + localInfo.nlistStartIndex = unmappedSymbols.size(); + localInfo.nlistCount = 0; fSymbolTableStartOffsetInNewLinkEdit = symbolTableOffset + symbolIndex*sizeof(macho_nlist

); macho_nlist

* const newSymbolTableStart = (macho_nlist

*)(fNewLinkEditStart+symbolTableOffset); const macho_nlist

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

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

* entry = firstLocal; entry < lastLocal; ++entry, ++oldIndex) { - if ( (entry->n_type() & N_TYPE) == N_SECT ) { + // don't copy stab symbols + if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) { + const char* name = &fStrings[entry->n_strx()]; macho_nlist

* newSymbolEntry = &newSymbolTableStart[symbolIndex]; *newSymbolEntry = *entry; - newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()])); - ++symbolIndex; + if ( dontMapLocalSymbols ) { + // if local in __text, add symbol name to shared cache so backtraces don't have bogus names + if ( entry->n_sect() == 1 ) { + newSymbolEntry->set_n_strx(fNewStringPool.addUnique("")); + ++symbolIndex; + } + // copy local symbol to unmmapped locals area + unmappedSymbols.push_back(*entry); + unmappedSymbols.back().set_n_strx(unmappedLocalsStringPool.addUnique(name)); + } + else { + newSymbolEntry->set_n_strx(fNewStringPool.addUnique(name)); + ++symbolIndex; + } } } fLocalSymbolsCountInNewLinkEdit = symbolIndex - fLocalSymbolsStartIndexInNewLinkEdit; + localInfo.nlistCount = unmappedSymbols.size() - localInfo.nlistStartIndex; + dylibInfos.push_back(localInfo); //fprintf(stderr, "%u locals starting at %u for %s\n", fLocalSymbolsCountInNewLinkEdit, fLocalSymbolsStartIndexInNewLinkEdit, fLayout.getFilePath()); } @@ -1870,7 +1887,7 @@ void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t si template -uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures) +uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures, bool dontMapLocalSymbols) { // allocate space for optimized LINKEDIT area uint8_t* newLinkEdit = new uint8_t[fLinkEditsTotalUnoptimizedSize]; @@ -1916,8 +1933,11 @@ uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures) fOffsetOfOldSymbolTableInfoInCombinedLinkedit = offset; uint32_t symbolTableOffset = offset; uint32_t symbolTableIndex = 0; + if ( dontMapLocalSymbols ) + fUnmappedLocalSymbols.reserve(16384); for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->copyLocalSymbols(symbolTableOffset, symbolTableIndex); + (*it)->copyLocalSymbols(symbolTableOffset, symbolTableIndex, dontMapLocalSymbols, fInMemoryCache, + fUnmappedLocalsStringPool, fUnmappedLocalSymbols, fLocalSymbolInfos); (*it)->copyExportedSymbols(symbolTableOffset, symbolTableIndex); (*it)->copyImportedSymbols(symbolTableOffset, symbolTableIndex); } @@ -1958,11 +1978,11 @@ uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures) fSizeOfOldStringPoolInCombinedLinkedit = stringPool.size(); // total new size round up to page size - fLinkEditsTotalOptimizedSize = (fOffsetOfOldStringPoolInCombinedLinkedit + fSizeOfOldStringPoolInCombinedLinkedit + 4095) & (-4096); + fLinkEditsTotalOptimizedSize = pageAlign(fOffsetOfOldStringPoolInCombinedLinkedit + fSizeOfOldStringPoolInCombinedLinkedit); // choose new linkedit file offset uint32_t linkEditsFileOffset = cacheFileOffsetForVMAddress(fLinkEditsStartAddress); -// uint32_t linkEditsFileOffset = fLinkEditsStartAddress - sharedRegionReadOnlyStartAddress(); +// uint32_t linkEditsFileOffset = fLinkEditsStartAddress - sharedRegionStartAddress(); // 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) { @@ -2315,6 +2335,69 @@ static void cleanup(int sig) } +// update_dyld_shared_cache should use sync_volume_np() instead of sync() +static void sync_volume(const char* volumePath) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 + int error = sync_volume_np(volumePath, SYNC_VOLUME_FULLSYNC|SYNC_VOLUME_FULLSYNC); +#else + int full_sync = 3; // SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_FULLSYNC + int error = 0; + if ( fsctl(volumePath, 0x80004101 /*FSCTL_SYNC_VOLUME*/, &full_sync, 0) == -1) + error = errno; +#endif + if ( error ) + ::sync(); +} + + +// update shared cache should sign the shared cache +static bool adhoc_codesign_share_cache(const char* path) +{ + CFURLRef target = ::CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)path, strlen(path), FALSE); + if ( target == NULL ) + return false; + + SecStaticCodeRef code; + OSStatus status = ::SecStaticCodeCreateWithPath(target, kSecCSDefaultFlags, &code); + CFRelease(target); + if ( status ) { + ::fprintf(stderr, "codesign: failed to create url to signed object\n"); + return false; + } + + const void * keys[1] = { (void *)kSecCodeSignerIdentity } ; + const void * values[1] = { (void *)kCFNull }; + CFDictionaryRef params = ::CFDictionaryCreate(NULL, keys, values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if ( params == NULL ) { + CFRelease(code); + return false; + } + + SecCodeSignerRef signer; + status = ::SecCodeSignerCreate(params, kSecCSDefaultFlags, &signer); + CFRelease(params); + if ( status ) { + CFRelease(code); + ::fprintf(stderr, "codesign: failed to create signer object\n"); + return false; + } + + status = ::SecCodeSignerAddSignatureWithErrors(signer, code, kSecCSDefaultFlags, NULL); + CFRelease(code); + CFRelease(signer); + if ( status ) { + ::fprintf(stderr, "codesign: failed to sign object: %s\n", path); + return false; + } + + if ( verbose ) + ::fprintf(stderr, "codesigning complete of %s\n", path); + + return true; +} + + template <> bool SharedCache::addCacheSlideInfo(){ return true; } template <> bool SharedCache::addCacheSlideInfo() { return true; } @@ -2324,7 +2407,7 @@ template <> bool SharedCache::addCacheSlideInfo() { return false; } template bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, int archIndex, - int archCount, bool keepSignatures) + int archCount, bool keepSignatures, bool dontMapLocalSymbols) { bool didUpdate = false; @@ -2374,6 +2457,8 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, header->set_codeSignatureSize(0); header->set_slideInfoOffset(0); header->set_slideInfoSize(0); + header->set_localSymbolsOffset(0); + header->set_localSymbolsSize(0); // fill in mappings dyldCacheFileMapping* mapping = (dyldCacheFileMapping*)&inMemoryCache[sizeof(dyldCacheHeader)]; @@ -2479,10 +2564,6 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, std::vector pointersInData; pointersInData.reserve(1024); - // add pointer in start of __DATA to start of __TEXT to remain compatible with previous dylds - pint_t* dataStartPtr = (pint_t*)(&inMemoryCache[fMappings[1].sfm_file_offset]); - P::setP(*dataStartPtr, fMappings[0].sfm_address); - // rebase each dylib in shared cache for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { try { @@ -2542,7 +2623,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, // merge/optimize all LINKEDIT segments if ( optimize ) { //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024)); - cacheFileSize = (this->optimizeLINKEDIT(keepSignatures) - inMemoryCache); + cacheFileSize = (this->optimizeLINKEDIT(keepSignatures, dontMapLocalSymbols) - inMemoryCache); //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024)); // update header to reduce mapping size dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; @@ -2623,7 +2704,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, } slideInfo->set_entries_count(entry_count); - int slideInfoPageSize = (slideInfo->entries_offset() + entry_count*entry_size + 4095) & (-4096); + int slideInfoPageSize = pageAlign(slideInfo->entries_offset() + entry_count*entry_size); cacheFileSize += slideInfoPageSize; // update mappings to increase RO size @@ -2644,6 +2725,70 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, memcpy(&inMemoryCache[cacheHeader->slideInfoOffset()], slideInfo, slideInfoPageSize); } + // make sure after all optimizations, that whole cache file fits into shared region address range + { + dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; + dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&inMemoryCache[cacheHeader->mappingOffset()]; + for (int i=0; i < cacheHeader->mappingCount(); ++i) { + uint64_t endAddr = mappings[i].address() + mappings[i].size(); + if ( endAddr > (sharedRegionStartAddress() + sharedRegionSize()) ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in address space: 0x%llX\n", + getpid(), fArchGraph->archName(), endAddr); + } + } + } + + // append local symbol info in an unmapped region + if ( dontMapLocalSymbols ) { + uint32_t spaceAtEnd = allocatedCacheSize - cacheFileSize; + uint32_t localSymbolsOffset = pageAlign(cacheFileSize); + dyldCacheLocalSymbolsInfo* infoHeader = (dyldCacheLocalSymbolsInfo*)(&inMemoryCache[localSymbolsOffset]); + const uint32_t entriesOffset = sizeof(dyldCacheLocalSymbolsInfo); + const uint32_t entriesCount = fLocalSymbolInfos.size(); + const uint32_t nlistOffset = entriesOffset + entriesCount * sizeof(dyldCacheLocalSymbolEntry); + const uint32_t nlistCount = fUnmappedLocalSymbols.size(); + const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist

); + const uint32_t stringsSize = fUnmappedLocalsStringPool.size(); + if ( stringsOffset+stringsSize > spaceAtEnd ) + throwf("update_dyld_shared_cache[%u] for arch=%s, out of space for local symbols. Have 0x%X, Need 0x%X\n", + getpid(), fArchGraph->archName(), spaceAtEnd, stringsOffset+stringsSize); + // fill in local symbols info + infoHeader->set_nlistOffset(nlistOffset); + infoHeader->set_nlistCount(nlistCount); + infoHeader->set_stringsOffset(stringsOffset); + infoHeader->set_stringsSize(stringsSize); + infoHeader->set_entriesOffset(entriesOffset); + infoHeader->set_entriesCount(entriesCount); + // copy info for each dylib + dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)(&inMemoryCache[localSymbolsOffset+entriesOffset]); + for (int i=0; i < entriesCount; ++i) { + entries[i].set_dylibOffset(fLocalSymbolInfos[i].dylibOffset); + entries[i].set_nlistStartIndex(fLocalSymbolInfos[i].nlistStartIndex); + entries[i].set_nlistCount(fLocalSymbolInfos[i].nlistCount); + } + // copy nlists + memcpy(&inMemoryCache[localSymbolsOffset+nlistOffset], &fUnmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist

)); + // copy string pool + memcpy(&inMemoryCache[localSymbolsOffset+stringsOffset], fUnmappedLocalsStringPool.getBuffer(), stringsSize); + + // update state + fUnmappedLocalSymbolsSize = pageAlign(stringsOffset + stringsSize); + cacheFileSize = localSymbolsOffset + fUnmappedLocalSymbolsSize; + + // update header to show location of slidePointers + dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; + cacheHeader->set_localSymbolsOffset(localSymbolsOffset); + cacheHeader->set_localSymbolsSize(stringsOffset+stringsSize); + cacheHeader->set_codeSignatureOffset(cacheFileSize); + } + + // compute UUID of whole cache + uint8_t digest[16]; + CC_MD5(inMemoryCache, cacheFileSize, digest); + // uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats + digest[6] = ( digest[6] & 0x0F ) | ( 3 << 4 ); + digest[8] = ( digest[8] & 0x3F ) | 0x80; + ((dyldCacheHeader*)inMemoryCache)->set_uuid(digest); if ( fVerify ) { // if no existing cache, say so @@ -2758,6 +2903,9 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, if ( result != 0 ) fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath); + if ( !iPhoneOS ) + adhoc_codesign_share_cache(tempCachePath); + // Make life easier for the kernel at shutdown. // If we just move the new cache file over the old, the old file // may need to exist in the open-unlink state. But because it @@ -2788,7 +2936,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, // flush everything to disk to assure rename() gets recorded - ::sync(); + sync_volume(fCacheFilePath); didUpdate = true; // restore default signal handlers @@ -2817,57 +2965,71 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, 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), + 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, + fprintf(fmap, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, it->sfm_size/1024, it->sfm_address, it->sfm_address+it->sfm_size); } - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX weak binding info\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX weak binding info\n", (fOffsetOfExportInfoInCombinedLinkedit-fOffsetOfWeakBindInfoInCombinedLinkedit)/1024, fLinkEditsStartAddress+fOffsetOfWeakBindInfoInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfExportInfoInCombinedLinkedit); - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX export info\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX export info\n", (fOffsetOfBindInfoInCombinedLinkedit-fOffsetOfExportInfoInCombinedLinkedit)/1024, fLinkEditsStartAddress+fOffsetOfExportInfoInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfBindInfoInCombinedLinkedit); - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX binding info\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX binding info\n", (fOffsetOfLazyBindInfoInCombinedLinkedit-fOffsetOfBindInfoInCombinedLinkedit)/1024, fLinkEditsStartAddress+fOffsetOfBindInfoInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit); - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX lazy binding info\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX lazy binding info\n", (fOffsetOfOldSymbolTableInfoInCombinedLinkedit-fOffsetOfLazyBindInfoInCombinedLinkedit)/1024, fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit); - fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table size\n", + fprintf(fmap, " linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table size\n", (fSizeOfOldSymbolTableInfoInCombinedLinkedit)/(1024*1024), fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit+fSizeOfOldSymbolTableInfoInCombinedLinkedit); if ( fSizeOfFunctionStartsInCombinedLinkedit != 0 ) - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld functions starts size\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld functions starts size\n", fSizeOfFunctionStartsInCombinedLinkedit/1024, fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit+fSizeOfFunctionStartsInCombinedLinkedit); if ( fSizeOfDataInCodeInCombinedLinkedit != 0 ) - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld data-in-code info size\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld data-in-code info size\n", fSizeOfDataInCodeInCombinedLinkedit/1024, fLinkEditsStartAddress+fOffsetOfDataInCodeInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfDataInCodeInCombinedLinkedit+fSizeOfDataInCodeInCombinedLinkedit); if ( fSizeOfOldExternalRelocationsInCombinedLinkedit != 0 ) - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld external relocs size\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld external relocs size\n", fSizeOfOldExternalRelocationsInCombinedLinkedit/1024, fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit+fSizeOfOldExternalRelocationsInCombinedLinkedit); - fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld indirect symbol table size\n", + fprintf(fmap, " linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld indirect symbol table size\n", fSizeOfOldIndirectSymbolsInCombinedLinkedit/1024, fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit+fSizeOfOldIndirectSymbolsInCombinedLinkedit); - fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld string pool\n", + fprintf(fmap, " linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld string pool\n", (fSizeOfOldStringPoolInCombinedLinkedit)/(1024*1024), fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit+fSizeOfOldStringPoolInCombinedLinkedit); + fprintf(fmap, "unmapped -- %4uMB local symbol info\n", fUnmappedLocalSymbolsSize/(1024*1024)); + + uint64_t endMappingAddr = fMappings[2].sfm_address + fMappings[2].sfm_size; + fprintf(fmap, "total map %4lluMB\n", (endMappingAddr - sharedRegionStartAddress())/(1024*1024)); + if ( sharedRegionStartWritableAddress(0) == 0x7FFF70000000LL ) { + // x86_64 has different slide constraints + uint64_t freeSpace = 256*1024*1024 - fMappings[1].sfm_size; + fprintf(fmap, "r/w space %4lluMB -> %d bits of entropy for ASLR\n\n", freeSpace/(1024*1024), (int)log2(freeSpace/4096)); + } + else { + uint64_t freeSpace = sharedRegionStartAddress() + sharedRegionSize() - endMappingAddr; + fprintf(fmap, "free space %4lluMB -> %d bits of entropy for ASLR\n\n", freeSpace/(1024*1024), (int)log2(freeSpace/4096)); + } + for(typename std::vector::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { fprintf(fmap, "%s\n", it->layout->getID().name); for (std::vector::const_iterator ait = it->aliases.begin(); ait != it->aliases.end(); ++ait) @@ -2967,8 +3129,9 @@ static void parsePathsFile(const char* filePath, std::vector& paths // images in shared cache are bound against different IOKit than found at runtime // HACK: Just ignore the known bad IOKit if ( strcmp(symbolStart, "/System/Library/Frameworks/IOKit.framework/IOKit") == 0 ) { - fprintf(stderr, "update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n"); - warnings.push_back("update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n"); + // Disable warning because after three years has still not been fixed... + //fprintf(stderr, "update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n"); + //warnings.push_back("update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n"); } else { paths.push_back(symbolStart); @@ -3108,7 +3271,7 @@ static void deleteOrphanTempCacheFiles() static bool updateSharedeCacheFile(const char* rootPath, const char* overlayPath, const char* cacheDir, bool explicitCacheDir, const std::set& onlyArchs, - bool force, bool alphaSort, bool optimize, bool deleteExistingFirst, bool verify, bool keepSignatures) + bool force, bool alphaSort, bool optimize, bool deleteExistingFirst, bool verify, bool keepSignatures, bool dontMapLocalSymbols) { bool didUpdate = false; // get dyld load address info @@ -3136,19 +3299,19 @@ static bool updateSharedeCacheFile(const char* rootPath, const char* overlayPath case CPU_TYPE_I386: { SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPath, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); - didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures); + didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols); } break; case CPU_TYPE_X86_64: { SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPath, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); - didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures); + didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols); } break; case CPU_TYPE_ARM: { SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPath, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); - didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures); + didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols); } break; } @@ -3179,6 +3342,7 @@ int main(int argc, const char* argv[]) bool verify = false; bool keepSignatures = false; bool explicitCacheDir = false; + bool dontMapLocalSymbols = false; const char* cacheDir = NULL; try { @@ -3207,8 +3371,12 @@ int main(int argc, const char* argv[]) else if ( strcmp(arg, "-no_opt") == 0 ) { optimize = false; } + else if ( strcmp(arg, "-dont_map_local_symbols") == 0 ) { + dontMapLocalSymbols = true; + } else if ( strcmp(arg, "-iPhone") == 0 ) { iPhoneOS = true; + alphaSort = true; } else if ( strcmp(arg, "-dylib_list") == 0 ) { dylibListFile = argv[++i]; @@ -3249,6 +3417,8 @@ int main(int argc, const char* argv[]) onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7F)); else if ( strcmp(arch, "armv7k") == 0 ) onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K)); + else if ( strcmp(arch, "armv7s") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S)); else throwf("unknown architecture %s", arch); } @@ -3316,8 +3486,23 @@ int main(int argc, const char* argv[]) setSharedDylibs(rootPath, overlayPath, dylibListFile, onlyArchs); else scanForSharedDylibs(rootPath, overlayPath, "/var/db/dyld/shared_region_roots/", onlyArchs); - updateSharedeCacheFile(rootPath, overlayPath, cacheDir, explicitCacheDir, onlyArchs, force, alphaSort, optimize, - false, verify, keepSignatures); + bool didUpdate = updateSharedeCacheFile(rootPath, overlayPath, cacheDir, explicitCacheDir, onlyArchs, force, alphaSort, optimize, + false, verify, keepSignatures, dontMapLocalSymbols); + + if ( didUpdate && !iPhoneOS ) { + void* handle = dlopen("/usr/lib/libspindump.dylib", RTLD_LAZY); + if ( handle != NULL ) { + typedef bool (*dscsym_proc_t)(const char *root); + dscsym_proc_t proc = (dscsym_proc_t)dlsym(handle, "dscsym_save_nuggets_for_current_caches"); + const char* nuggetRootPath = "/"; + if ( overlayPath[0] != '\0' ) + nuggetRootPath = overlayPath; + else if ( rootPath[0] != '\0' ) + nuggetRootPath = rootPath; + (*proc)(nuggetRootPath); + } + dlclose(handle); + } } catch (const char* msg) { fprintf(stderr, "update_dyld_shared_cache failed: %s\n", msg); diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 58b2282..65820ed 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -24,6 +24,7 @@ #define __STDC_LIMIT_MACROS #include +#include #include #include #include @@ -65,32 +66,24 @@ uintptr_t ImageLoader::fgNextPIEDylibAddress = 0; ImageLoader::ImageLoader(const char* path, unsigned int libCount) : fPath(path), fRealPath(NULL), fDevice(0), fInode(0), fLastModified(0), - fPathHash(0), fDlopenReferenceCount(0), fStaticReferenceCount(0), - fDynamicReferenceCount(0), fDynamicReferences(NULL), fInitializerRecursiveLock(NULL), + fPathHash(0), fDlopenReferenceCount(0), fInitializerRecursiveLock(NULL), fDepth(0), fLoadOrder(fgLoadOrdinal++), fState(0), fLibraryCount(libCount), fAllLibraryChecksumsAndLoadAddressesMatch(false), fLeaveMapped(false), fNeverUnload(false), fHideSymbols(false), fMatchByInstallName(false), fInterposed(false), fRegisteredDOF(false), fAllLazyPointersBound(false), fBeingRemoved(false), fAddFuncNotified(false), fPathOwnedByImage(false), fIsReferencedDownward(false), - fIsReferencedUpward(false), fWeakSymbolsBound(false) + fWeakSymbolsBound(false) { if ( fPath != NULL ) fPathHash = hash(fPath); + if ( libCount > 512 ) + dyld::throwf("too many dependent dylibs in %s", path); } void ImageLoader::deleteImage(ImageLoader* image) { - // this cannot be done in destructor because libImage() is implemented - // in a subclass - DependentLibraryInfo libraryInfos[image->libraryCount()]; - image->doGetDependentLibraries(libraryInfos); - for(unsigned int i=0; i < image->libraryCount(); ++i) { - ImageLoader* lib = image->libImage(i); - if ( (lib != NULL) && ! libraryInfos[i].upward ) - lib->fStaticReferenceCount--; - } delete image; } @@ -101,12 +94,6 @@ ImageLoader::~ImageLoader() delete [] fRealPath; if ( fPathOwnedByImage && (fPath != NULL) ) delete [] fPath; - if ( fDynamicReferences != NULL ) { - for (std::vector::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) { - const_cast(*it)->fDynamicReferenceCount--; - } - delete fDynamicReferences; - } } void ImageLoader::setFileInfo(dev_t device, ino_t inode, time_t modDate) @@ -122,27 +109,6 @@ void ImageLoader::setMapped(const LinkContext& context) context.notifySingle(dyld_image_state_mapped, this); // note: can throw exception } -void ImageLoader::addDynamicReference(const ImageLoader* target) -{ - bool alreadyInVector = false; - if ( fDynamicReferences == NULL ) { - fDynamicReferences = new std::vector(); - } - else { - for (std::vector::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) { - if ( *it == target ) { - alreadyInVector = true; - break; - } - } - } - if ( ! alreadyInVector ) { - fDynamicReferences->push_back(target); - const_cast(target)->fDynamicReferenceCount++; - } - //dyld::log("dyld: addDynamicReference() from %s to %s, fDynamicReferences->size()=%lu\n", this->getPath(), target->getPath(), fDynamicReferences->size()); -} - int ImageLoader::compare(const ImageLoader* right) const { if ( this->fDepth == right->fDepth ) { @@ -304,6 +270,16 @@ void ImageLoader::getMappedRegions(MappedRegion*& regions) const } + +bool ImageLoader::dependsOn(ImageLoader* image) { + for(unsigned int i=0; i < libraryCount(); ++i) { + if ( libImage(i) == image ) + return true; + } + return false; +} + + static bool notInImgageList(const ImageLoader* image, const ImageLoader** dsiStart, const ImageLoader** dsiCur) { for (const ImageLoader** p = dsiStart; p < dsiCur; ++p) @@ -375,9 +351,9 @@ void ImageLoader::applyInterposing(const LinkContext& context) this->recursiveApplyInterposing(context); } -void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, const RPathChain& loaderRPaths) +void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths) { - //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", this->getPath(), fStaticReferenceCount, fNeverUnload); + //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", this->getPath(), fDlopenReferenceCount, fNeverUnload); // clear error strings (*context.setErrorStrings)(dyld_error_kind_none, NULL, NULL, NULL); @@ -399,10 +375,11 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr context.notifyBatch(dyld_image_state_rebased); uint64_t t3 = mach_absolute_time(); - this->recursiveBind(context, forceLazysBound); + this->recursiveBind(context, forceLazysBound, neverUnload); uint64_t t4 = mach_absolute_time(); - this->weakBind(context); + if ( !context.linkingMainExecutable ) + this->weakBind(context); uint64_t t5 = mach_absolute_time(); context.notifyBatch(dyld_image_state_bound); @@ -434,8 +411,7 @@ void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool pr void ImageLoader::printReferenceCounts() { - dyld::log(" dlopen=%d, static=%d, dynamic=%d for %s\n", - fDlopenReferenceCount, fStaticReferenceCount, fDynamicReferenceCount, getPath() ); + dyld::log(" dlopen=%d for %s\n", fDlopenReferenceCount, getPath() ); } @@ -484,6 +460,29 @@ bool ImageLoader::allDependentLibrariesAsWhenPreBound() const } +void ImageLoader::markedUsedRecursive(const std::vector& dynamicReferences) +{ + // already visited here + if ( fMarkedInUse ) + return; + fMarkedInUse = true; + + // clear mark on all statically dependent dylibs + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + dependentImage->markedUsedRecursive(dynamicReferences); + } + } + + // clear mark on all dynamically dependent dylibs + for (std::vector::const_iterator it=dynamicReferences.begin(); it != dynamicReferences.end(); ++it) { + if ( it->from == this ) + it->to->markedUsedRecursive(dynamicReferences); + } + +} + unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) { // the purpose of this phase is to make the images sortable such that @@ -555,10 +554,8 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli if ( fNeverUnload ) dependentLib->setNeverUnload(); if ( requiredLibInfo.upward ) { - dependentLib->fIsReferencedUpward = true; } else { - dependentLib->fStaticReferenceCount += 1; dependentLib->fIsReferencedDownward = true; } LibraryInfo actualInfo = dependentLib->doGetLibraryInfo(); @@ -698,7 +695,7 @@ void ImageLoader::recursiveApplyInterposing(const LinkContext& context) -void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound) +void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload) { // Normally just non-lazy pointers are bound immediately. // The exceptions are: @@ -713,13 +710,16 @@ void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound for(unsigned int i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); if ( dependentImage != NULL ) - dependentImage->recursiveBind(context, forceLazysBound); + dependentImage->recursiveBind(context, forceLazysBound, neverUnload); } // bind this image this->doBind(context, forceLazysBound); // mark if lazys are also bound if ( forceLazysBound || this->usablePrebinding(context) ) fAllLazyPointersBound = true; + // mark as never-unload if requested + if ( neverUnload ) + this->setNeverUnload(); context.notifySingle(dyld_image_state_bound, this); } @@ -736,6 +736,7 @@ void ImageLoader::weakBind(const LinkContext& context) { if ( context.verboseWeakBind ) dyld::log("dyld: weak bind start:\n"); + uint64_t t1 = mach_absolute_time(); // get set of ImageLoaders that participate in coalecsing ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing]; int count = context.getCoalescedImages(imagesNeedingCoalescing); @@ -841,6 +842,9 @@ void ImageLoader::weakBind(const LinkContext& context) imagesNeedingCoalescing[i]->fWeakSymbolsBound = true; } } + uint64_t t2 = mach_absolute_time(); + fgTotalWeakBindTime += t2 - t1; + if ( context.verboseWeakBind ) dyld::log("dyld: weak bind end\n"); } @@ -863,6 +867,19 @@ void ImageLoader::recursiveGetDOFSections(const LinkContext& context, std::vecto } } +void ImageLoader::setNeverUnloadRecursive() { + if ( ! fNeverUnload ) { + // break cycles + fNeverUnload = true; + + // gather lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->setNeverUnloadRecursive(); + } + } +} void ImageLoader::recursiveSpinLock(recursive_lock& rlock) { @@ -920,7 +937,7 @@ void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_ // initialize this image bool hasInitializers = this->doInitialization(context); - + // initialize any upward depedencies if ( hasUpwards ) { for(unsigned int i=0; i < libraryCount(); ++i) { @@ -1074,5 +1091,8 @@ void ImageLoader::addSuffix(const char* path, const char* suffix, char* result) } +VECTOR_NEVER_DESTRUCTED_IMPL(ImageLoader::InterposeTuple); +VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair); + diff --git a/src/ImageLoader.h b/src/ImageLoader.h index 83bc1fe..4e00826 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -28,16 +28,19 @@ #include #include +#include #include // struct mach_timebase_info #include // struct mach_thread_self #include #include #include #include +#include +#include #include #include -#if (__i386__ || __x86_64__) +#if __MAC_OS_X_VERSION_MIN_REQUIRED #include #else // work around until iOS has CrashReporterClient.h @@ -78,26 +81,50 @@ }; #endif -#define SPLIT_SEG_SHARED_REGION_SUPPORT __arm__ -#define SPLIT_SEG_DYLIB_SUPPORT (__i386__ || __arm__) -#define PREBOUND_IMAGE_SUPPORT (__i386__ || __arm__) -#define TEXT_RELOC_SUPPORT (__i386__ || __arm__) -#define DYLD_SHARED_CACHE_SUPPORT (__i386__ || __x86_64__ || __arm__) -#define SUPPORT_OLD_CRT_INITIALIZATION (__i386__) -#define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__) -#define SUPPORT_VERSIONED_PATHS (__i386__ || __x86_64__) -#if __IPHONE_OS_VERSION_MIN_REQUIRED - #define CORESYMBOLICATION_SUPPORT 1 +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 + #define SPLIT_SEG_DYLIB_SUPPORT 0 + #define PREBOUND_IMAGE_SUPPORT 0 + #define TEXT_RELOC_SUPPORT __i386__ + #define DYLD_SHARED_CACHE_SUPPORT __arm__ + #define SUPPORT_OLD_CRT_INITIALIZATION 0 + #define SUPPORT_LC_DYLD_ENVIRONMENT 0 + #define SUPPORT_VERSIONED_PATHS 0 + #define SUPPORT_CLASSIC_MACHO __arm__ + #define CORESYMBOLICATION_SUPPORT __arm__ + #define INITIAL_IMAGE_COUNT 256 #else - #define CORESYMBOLICATION_SUPPORT (__i386__ || __x86_64__) -#endif -#if __arm__ - #define INITIAL_IMAGE_COUNT 256 -#else - #define INITIAL_IMAGE_COUNT 200 + #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 + #define SPLIT_SEG_DYLIB_SUPPORT __i386__ + #define PREBOUND_IMAGE_SUPPORT __i386__ + #define TEXT_RELOC_SUPPORT __i386__ + #define DYLD_SHARED_CACHE_SUPPORT 1 + #define SUPPORT_OLD_CRT_INITIALIZATION __i386__ + #define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__) + #define SUPPORT_VERSIONED_PATHS 1 + #define SUPPORT_CLASSIC_MACHO 1 + #define CORESYMBOLICATION_SUPPORT 1 + #define INITIAL_IMAGE_COUNT 200 #endif -#define CODESIGNING_SUPPORT __arm__ + + +// optimize away dyld's initializers +#define VECTOR_NEVER_DESTRUCTED(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base() { } \ + } +#define VECTOR_NEVER_DESTRUCTED_EXTERN(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base(); \ + } +#define VECTOR_NEVER_DESTRUCTED_IMPL(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base() { } \ + } // utilities namespace dyld { @@ -108,7 +135,9 @@ namespace dyld { #if LOG_BINDINGS extern void logBindings(const char* format, ...) __attribute__((format(printf, 1, 2))); #endif -}; +} +extern "C" int vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags); +extern "C" void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset); #if __LP64__ @@ -176,6 +205,11 @@ public: const char* imageShortName; }; + struct DynamicReference { + ImageLoader* from; + ImageLoader* to; + }; + struct LinkContext { ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths); void (*terminationRecorder)(ImageLoader* image); @@ -196,6 +230,9 @@ public: bool (*inSharedCache)(const char* path); void (*setErrorStrings)(unsigned errorCode, const char* errorClientOfDylibPath, const char* errorTargetDylibPath, const char* errorSymbol); + ImageLoader* (*findImageContainingAddress)(const void* addr); + void (*addDynamicReference)(ImageLoader* from, ImageLoader* to); + #if SUPPORT_OLD_CRT_INITIALIZATION void (*setRunInitialzersOldWay)(); #endif @@ -211,7 +248,9 @@ public: const char** rootPaths; PrebindMode prebindUsage; SharedRegionMode sharedRegionMode; - bool dyldLoadedAtSameAddressNeededBySharedCache; + bool dyldLoadedAtSameAddressNeededBySharedCache; + bool codeSigningEnforced; + bool mainExecutableCodeSigned; bool preFetchDisabled; bool prebinding; bool bindFlat; @@ -231,6 +270,7 @@ public: bool verboseWarnings; bool verboseRPaths; bool verboseInterposing; + bool verboseCodeSignatures; }; struct CoalIterator @@ -269,7 +309,7 @@ public: // link() takes a newly instantiated ImageLoader and does all // fixups needed to make it usable by the process - void link(const LinkContext& context, bool forceLazysBound, bool preflight, const RPathChain& loaderRPaths); + void link(const LinkContext& context, bool forceLazysBound, bool preflight, bool neverUnload, const RPathChain& loaderRPaths); // runInitializers() is normally called in link() but the main executable must // run crt code before initializers @@ -464,12 +504,16 @@ public: virtual uintptr_t segActualEndAddress(unsigned int) const = 0; + virtual uint32_t sdkVersion() const = 0; + // if the image contains interposing functions, register them virtual void registerInterposing() = 0; // when resolving symbols look in subImage if symbol can't be found void reExport(ImageLoader* subImage); + void weakBind(const LinkContext& context); + void applyInterposing(const LinkContext& context); dyld_image_states getState() { return (dyld_image_states)fState; } @@ -483,15 +527,17 @@ public: void printReferenceCounts(); - uint32_t referenceCount() const { return fDlopenReferenceCount + fStaticReferenceCount + fDynamicReferenceCount; } + uint32_t dlopenCount() const { return fDlopenReferenceCount; } + + void setCanUnload() { fNeverUnload = false; fLeaveMapped = false; } bool neverUnload() const { return fNeverUnload; } void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; } + void setNeverUnloadRecursive(); bool isReferencedDownward() { return fIsReferencedDownward; } - bool isReferencedUpward() { return fIsReferencedUpward; } // triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo); @@ -504,7 +550,9 @@ public: // used instead of directly deleting image static void deleteImage(ImageLoader*); - void setPath(const char* path); + bool dependsOn(ImageLoader* image); + + void setPath(const char* path); void setPaths(const char* path, const char* realPath); void setPathUnowned(const char* path); @@ -514,15 +562,25 @@ public: void setBeingRemoved() { fBeingRemoved = true; } bool isBeingRemoved() const { return fBeingRemoved; } + void markNotUsed() { fMarkedInUse = false; } + void markedUsedRecursive(const std::vector&); + bool isMarkedInUse() const { return fMarkedInUse; } + void setAddFuncNotified() { fAddFuncNotified = true; } bool addFuncNotified() const { return fAddFuncNotified; } + struct InterposeTuple { + uintptr_t replacement; + ImageLoader* replacementImage; // don't apply replacement to this image + uintptr_t replacee; + }; + protected: // abstract base class so all constructors protected ImageLoader(const char* path, unsigned int libCount); ImageLoader(const ImageLoader&); void operator=(const ImageLoader&); - void operator delete(void* image) throw() { free(image); } + void operator delete(void* image) throw() { ::free(image); } struct LibraryInfo { @@ -548,12 +606,6 @@ protected: }; - struct InterposeTuple { - uintptr_t replacement; - ImageLoader* replacementImage; // don't apply replacement to this image - uintptr_t replacee; - }; - typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars); typedef void (*Terminator)(void); @@ -565,7 +617,6 @@ protected: virtual bool libIsUpward(unsigned int) const = 0; virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) = 0; - // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized. // These methods do the above, exactly once, and it the right order void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths); @@ -573,8 +624,7 @@ protected: unsigned int recursiveUpdateDepth(unsigned int maxDepth); void recursiveValidate(const LinkContext& context); void recursiveRebase(const LinkContext& context); - void recursiveBind(const LinkContext& context, bool forceLazysBound); - void weakBind(const LinkContext& context); + void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload); void recursiveApplyInterposing(const LinkContext& context); void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs); void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, ImageLoader::InitializerTimingList&); @@ -626,10 +676,7 @@ protected: // 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); - + void setFileInfo(dev_t device, ino_t inode, time_t modDate); static uintptr_t fgNextPIEDylibAddress; @@ -653,6 +700,7 @@ protected: static uint64_t fgTotalDOF; static uint64_t fgTotalInitTime; static std::vector fgInterposingTuples; + const char* fPath; const char* fRealPath; dev_t fDevice; @@ -660,9 +708,6 @@ protected: time_t fLastModified; uint32_t fPathHash; uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image - uint32_t fStaticReferenceCount; // count of images that have a fLibraries entry pointing to this image - uint32_t fDynamicReferenceCount; // count of images that have a fDynamicReferences entry pointer to this image - std::vector* fDynamicReferences; // list of all images this image used because of a flat/coalesced lookup private: struct recursive_lock { @@ -691,17 +736,18 @@ private: fInterposed : 1, fRegisteredDOF : 1, fAllLazyPointersBound : 1, + fMarkedInUse : 1, fBeingRemoved : 1, fAddFuncNotified : 1, fPathOwnedByImage : 1, fIsReferencedDownward : 1, - fIsReferencedUpward : 1, fWeakSymbolsBound : 1; static uint16_t fgLoadOrdinal; }; +VECTOR_NEVER_DESTRUCTED_EXTERN(ImageLoader::InterposeTuple); #endif diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index 3a8ad7d..1f636cb 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -40,13 +40,17 @@ #include #include #include +#include #include #include #include +#include #include "ImageLoaderMachO.h" #include "ImageLoaderMachOCompressed.h" +#if SUPPORT_CLASSIC_MACHO #include "ImageLoaderMachOClassic.h" +#endif #include "mach-o/dyld_images.h" // use stack guard random value to add padding between dylibs @@ -60,12 +64,14 @@ extern "C" long __stack_chk_guard; #if __LP64__ #define LC_SEGMENT_COMMAND LC_SEGMENT_64 #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT struct macho_segment_command : public segment_command_64 {}; struct macho_section : public section_64 {}; struct macho_routines_command : public routines_command_64 {}; #else #define LC_SEGMENT_COMMAND LC_SEGMENT #define LC_ROUTINES_COMMAND LC_ROUTINES + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 struct macho_segment_command : public segment_command {}; struct macho_section : public section {}; struct macho_routines_command : public routines_command {}; @@ -114,7 +120,7 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns // determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, - unsigned int* segCount, unsigned int* libCount, + unsigned int* segCount, unsigned int* libCount, const LinkContext& context, const linkedit_data_command** codeSigCmd) { *compressed = false; @@ -122,9 +128,11 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat *libCount = 0; *codeSigCmd = NULL; struct macho_segment_command* segCmd; -#if CODESIGNING_SUPPORT bool foundLoadCommandSegment = false; -#endif + uint32_t loadCommandSegmentIndex = 0xFFFFFFFF; + uintptr_t loadCommandSegmentVMStart = 0; + uintptr_t loadCommandSegmentVMEnd = 0; + const uint32_t cmd_count = mh->ncmds; const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds); @@ -140,16 +148,22 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat // ignore zero-sized segments if ( segCmd->vmsize != 0 ) *segCount += 1; -#if CODESIGNING_SUPPORT // all load commands must be in an executable segment - if ( (segCmd->fileoff < mh->sizeofcmds) && (segCmd->filesize != 0) ) { + if ( context.codeSigningEnforced && (segCmd->fileoff < mh->sizeofcmds) && (segCmd->filesize != 0) ) { if ( (segCmd->fileoff != 0) || (segCmd->filesize < (mh->sizeofcmds+sizeof(macho_header))) ) dyld::throwf("malformed mach-o image: segment %s does not span all load commands", segCmd->segname); if ( segCmd->initprot != (VM_PROT_READ | VM_PROT_EXECUTE) ) dyld::throwf("malformed mach-o image: load commands found in segment %s with wrong permissions", segCmd->segname); + if ( foundLoadCommandSegment ) + throw "load commands in multiple segments"; foundLoadCommandSegment = true; + loadCommandSegmentIndex = i; + loadCommandSegmentVMStart = segCmd->vmaddr; + loadCommandSegmentVMEnd = segCmd->vmaddr + segCmd->vmsize; } -#endif + break; + case LC_SEGMENT_COMMAND_WRONG: + dyld::throwf("malformed mach-o image: wrong LC_SEGMENT[_64] for architecture"); break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: @@ -169,11 +183,28 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat } } -#if CODESIGNING_SUPPORT - if ( ! foundLoadCommandSegment ) + if ( context.codeSigningEnforced && !foundLoadCommandSegment ) throw "load commands not in a segment"; -#endif - + // verify another segment does not over-map load commands + cmd = startCmds; + if ( context.codeSigningEnforced ) { + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + if ( i != loadCommandSegmentIndex ) { + segCmd = (struct macho_segment_command*)cmd; + uintptr_t start = segCmd->vmaddr; + uintptr_t end = segCmd->vmaddr + segCmd->vmsize; + if ( ((start <= loadCommandSegmentVMStart) && (end > loadCommandSegmentVMStart)) + || ((start >= loadCommandSegmentVMStart) && (start < loadCommandSegmentVMEnd)) ) + dyld::throwf("malformed mach-o image: segment %s overlaps load commands", segCmd->segname); + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + // fSegmentsArrayCount is only 8-bits if ( *segCount > 255 ) dyld::throwf("malformed mach-o image: more than 255 segments in %s", path); @@ -197,12 +228,16 @@ ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd); + sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, context, &codeSigCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); else +#if SUPPORT_CLASSIC_MACHO return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } @@ -225,12 +260,16 @@ ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, con unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount, &codeSigCmd); + sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount, context, &codeSigCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); else +#if SUPPORT_CLASSIC_MACHO return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } // create image by using cached mach-o file @@ -241,12 +280,16 @@ ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, cons unsigned int segCount; unsigned int libCount; const linkedit_data_command* codeSigCmd; - sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd); + sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, context, &codeSigCmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); else +#if SUPPORT_CLASSIC_MACHO return ImageLoaderMachOClassic::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } // create image by copying an in-memory mach-o file @@ -256,15 +299,34 @@ ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, con unsigned int segCount; unsigned int libCount; const linkedit_data_command* sigcmd; - sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount, &sigcmd); + sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount, context, &sigcmd); // instantiate concrete class based on content of load commands if ( compressed ) return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); else +#if SUPPORT_CLASSIC_MACHO return ImageLoaderMachOClassic::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif } +int ImageLoaderMachO::crashIfInvalidCodeSignature() +{ + // Now that segments are mapped in, try reading from first executable segment. + // If code signing is enabled the kernel will validate the code signature + // when paging in, and kill the process if invalid. + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + // return read value to ensure compiler does not optimize away load + int* p = (int*)segActualLoadAddress(i); + return *p; + } + } + return 0; +} + void ImageLoaderMachO::parseLoadCmds() { @@ -689,19 +751,57 @@ void ImageLoaderMachO::setSlide(intptr_t slide) fSlide = slide; } -#if CODESIGNING_SUPPORT -void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile) +void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context) { - fsignatures_t siginfo; - siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file - siginfo.fs_blob_start=(void*)(codeSigCmd->dataoff); // start of CD in mach-o file - siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD - int result = fcntl(fd, F_ADDFILESIGS, &siginfo); - if ( result == -1 ) - dyld::log("dyld: F_ADDFILESIGS failed for %s with errno=%d\n", this->getPath(), errno); - //dyld::log("dyld: registered code signature for %s\n", this->getPath()); -} + // if dylib being loaded has no code signature load command + if ( codeSigCmd == NULL ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + bool codeSigningEnforced = context.codeSigningEnforced; + if ( context.mainExecutableCodeSigned && !codeSigningEnforced ) { + static bool codeSignEnforcementDynamicallyEnabled = false; + if ( !codeSignEnforcementDynamicallyEnabled ) { + uint32_t flags; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + if ( flags & CS_ENFORCEMENT ) { + codeSignEnforcementDynamicallyEnabled = true; + } + } + } + codeSigningEnforced = codeSignEnforcementDynamicallyEnabled; + } + // if we require dylibs to be code signed + if ( codeSigningEnforced ) { + // if there is a non-load command based code signature, use it + off_t offset = (off_t)offsetInFatFile; + if ( fcntl(fd, F_FINDSIGS, &offset, sizeof(offset)) != -1 ) + return; + // otherwise gracefully return from dlopen() + dyld::throwf("required code signature missing for '%s'\n", this->getPath()); + } #endif + } + else { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // ignore code signatures in binaries built with pre-10.9 tools + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } +#endif + fsignatures_t siginfo; + siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of CD in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD + int result = fcntl(fd, F_ADDFILESIGS, &siginfo); + if ( result == -1 ) { + if ( (errno == EPERM) || (errno == EBADEXEC) ) + dyld::throwf("code signature invalid for '%s'\n", this->getPath()); + if ( context.verboseCodeSignatures ) + dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno); + else + dyld::log("dyld: Registered code signature for %s\n", this->getPath()); + } + } +} const char* ImageLoaderMachO::getInstallPath() const @@ -755,6 +855,24 @@ void ImageLoaderMachO::registerInterposing() } } +uint32_t ImageLoaderMachO::sdkVersion() 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; + const struct version_min_command* versCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + versCmd = (version_min_command*)cmd; + return versCmd->sdk; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + void* ImageLoaderMachO::getThreadPC() const { const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; @@ -960,8 +1078,8 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { // replace all references to 'replacee' with 'replacement' @@ -1526,7 +1646,7 @@ void ImageLoaderMachO::doImageInit(const LinkContext& context) dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); } if ( context.verboseInit ) - dyld::log("dyld: calling -init function 0x%p in %s\n", func, this->getPath()); + dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath()); func(context.argc, context.argv, context.envp, context.apple, &context.programVars); break; } @@ -1727,14 +1847,14 @@ uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoad // add small (0-3 pages) random padding between dylibs addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*4096; //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard)); - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_FIXED); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r == KERN_SUCCESS ) { fgNextPIEDylibAddress = addr + size; return addr; } fgNextPIEDylibAddress = 0; } - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_ANYWHERE); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r != KERN_SUCCESS ) throw "out of address space"; @@ -1745,7 +1865,7 @@ bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length) { vm_address_t addr = start; vm_size_t size = length; - kern_return_t r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r != KERN_SUCCESS ) return false; return true; @@ -1787,7 +1907,7 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF dyld::throwf("truncated mach-o error: segment %s extends to %llu which is past end of file %llu", segName(i), (uint64_t)(fileOffset+size), fileLen); } - void* loadAddress = mmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); + void* loadAddress = xmmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); if ( loadAddress == ((void*)(-1)) ) { dyld::throwf("mmap() error %d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s", errno, requestedLoadAddress, (uintptr_t)size, segName(i), getPath()); diff --git a/src/ImageLoaderMachO.h b/src/ImageLoaderMachO.h index c3e192b..6d5c744 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -100,6 +100,7 @@ public: virtual uintptr_t segPreferredLoadAddress(unsigned int) const; virtual uintptr_t segActualEndAddress(unsigned int) const; virtual void registerInterposing(); + virtual uint32_t sdkVersion() const; static void printStatistics(unsigned int imageCount, const InitializerTimingList&); @@ -120,7 +121,7 @@ protected: virtual void rebase(const LinkContext& context) = 0; virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0; virtual bool containsSymbol(const void* addr) const = 0; - virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const = 0; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const = 0; virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const = 0; virtual const char* exportedSymbolName(const Symbol* symbol) const = 0; virtual unsigned int exportedSymbolCount() const = 0; @@ -154,14 +155,13 @@ protected: void destroy(); static void sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed, - unsigned int* segCount, unsigned int* libCount, + unsigned int* segCount, unsigned int* libCount, const LinkContext& context, const linkedit_data_command** codeSigCmd); static bool needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh); -#if CODESIGNING_SUPPORT - void loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile); -#endif + void loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context); const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const; void parseLoadCmds(); + int crashIfInvalidCodeSignature(); bool segHasRebaseFixUps(unsigned int) const; bool segHasBindFixUps(unsigned int) const; void segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context); diff --git a/src/ImageLoaderMachOClassic.cpp b/src/ImageLoaderMachOClassic.cpp index 2d0cf1b..3eed9da 100644 --- a/src/ImageLoaderMachOClassic.cpp +++ b/src/ImageLoaderMachOClassic.cpp @@ -136,11 +136,8 @@ ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char // record info about file image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); - #if CODESIGNING_SUPPORT // if this image is code signed, let kernel validate signature before mapping any pages from image - if ( codeSigCmd != NULL ) - image->loadCodeSignature(codeSigCmd, fd, offsetInFat); - #endif + image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); // mmap segments image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context); @@ -953,7 +950,7 @@ bool ImageLoaderMachOClassic::containsSymbol(const void* addr) const } -uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const +uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const { const struct macho_nlist* sym = (macho_nlist*)symbol; uintptr_t result = sym->n_value + fSlide; @@ -1058,8 +1055,8 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, } const Symbol* sym; if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { - if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) - this->addDynamicReference(*foundIn); + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); return (*foundIn)->getExportedSymbolAddress(sym, context, this); } // if a bundle is loaded privately the above will not find its exports @@ -1081,8 +1078,8 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) { const Symbol* sym; if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) { - if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) - this->addDynamicReference(*foundIn); + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); return (*foundIn)->getExportedSymbolAddress(sym, context, this); } //throwSymbolNotFound(context, symbolName, this->getPath(), "coalesced namespace"); @@ -1686,8 +1683,9 @@ void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } - if ( boundSomething && (targetImage != this) && !targetImage->neverUnload() ) - this->addDynamicReference(targetImage); + if ( boundSomething && (targetImage != this) ) { + context.addDynamicReference(this, targetImage); + } // mark that this symbol has already been bound, so we don't try to bind again it.type = 1; diff --git a/src/ImageLoaderMachOClassic.h b/src/ImageLoaderMachOClassic.h index 5d76650..55685a5 100644 --- a/src/ImageLoaderMachOClassic.h +++ b/src/ImageLoaderMachOClassic.h @@ -74,7 +74,7 @@ protected: virtual void rebase(const LinkContext& context); virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const; virtual bool containsSymbol(const void* addr) const; - virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const; virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; virtual const char* exportedSymbolName(const Symbol* symbol) const; virtual unsigned int exportedSymbolCount() const; diff --git a/src/ImageLoaderMachOCompressed.cpp b/src/ImageLoaderMachOCompressed.cpp index 2d0cca2..5ba89ec 100644 --- a/src/ImageLoaderMachOCompressed.cpp +++ b/src/ImageLoaderMachOCompressed.cpp @@ -38,6 +38,9 @@ #include "ImageLoaderMachOCompressed.h" #include "mach-o/dyld_images.h" +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif // relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables #if __LP64__ @@ -110,7 +113,6 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutabl if ( slide != 0 ) fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); - image->setNeverUnload(); image->instantiateFinish(context); image->setMapped(context); @@ -140,15 +142,15 @@ ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(cons // record info about file image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); - #if CODESIGNING_SUPPORT // if this image is code signed, let kernel validate signature before mapping any pages from image - if ( codeSigCmd != NULL ) - image->loadCodeSignature(codeSigCmd, fd, offsetInFat); - #endif + image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); // mmap segments image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); + // probe to see if code signed correctly + image->crashIfInvalidCodeSignature(); + // finish construction image->instantiateFinish(context); @@ -628,7 +630,7 @@ bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const } -uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const +uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const { const uint8_t* exportNode = (uint8_t*)symbol; const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; @@ -637,37 +639,41 @@ uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& c throw "symbol is not in trie"; //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); uint32_t flags = read_uleb128(exportNode, exportTrieEnd); - if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) { - if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { - // this node has a stub and resolver, run the resolver to get target address - uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub - // interposing dylibs have the stub address as their replacee - for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { - // replace all references to 'replacee' with 'replacement' - if ( stub == it->replacee ) { - if ( context.verboseInterposing ) { - dyld::log("dyld interposing: lazy replace 0x%lX with 0x%lX from %s\n", - it->replacee, it->replacement, this->getPath()); + switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + // this node has a stub and resolver, run the resolver to get target address + uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub + // interposing dylibs have the stub address as their replacee + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + // replace all references to 'replacee' with 'replacement' + if ( (stub == it->replacee) && (requestor != it->replacementImage) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld interposing: lazy replace 0x%lX with 0x%lX from %s\n", + it->replacee, it->replacement, this->getPath()); + } + return it->replacement; } - return it->replacement; } + typedef uintptr_t (*ResolverProc)(void); + ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); + uintptr_t result = (*resolver)(); + if ( context.verboseBind ) + dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); + return result; } - typedef uintptr_t (*ResolverProc)(void); - ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); - uintptr_t result = (*resolver)(); - if ( context.verboseBind ) - dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result); - return result; - } - return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; - } - else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL ) { - if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); + return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); + return read_uleb128(exportNode, exportTrieEnd); + default: dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); - return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; } - else - dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); } bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const @@ -719,8 +725,8 @@ uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, co { const Symbol* sym; if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { - if ( (*foundIn != this) && !(*foundIn)->neverUnload() ) - this->addDynamicReference(*foundIn); + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); } // if a bundle is loaded privately the above will not find its exports @@ -1070,7 +1076,7 @@ void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_h case BIND_OPCODE_DO_BIND: if ( address >= segmentEndAddress ) throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); - (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "lazy forced", NULL, true); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false); address += sizeof(intptr_t); break; case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: @@ -1336,7 +1342,7 @@ bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) } break; default: - dyld::throwf("bad weak bind opcode %d", *p); + dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); } } /// hmmm, BIND_OPCODE_DONE is missing... @@ -1430,9 +1436,10 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt default: dyld::throwf("bad bind opcode %d in weak binding info", *p); } - } - if ( boundSomething && (targetImage != this) && !targetImage->neverUnload() ) - this->addDynamicReference(targetImage); + } + // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. + if ( boundSomething && (targetImage != this) ) + context.addDynamicReference(this, targetImage); } uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, @@ -1551,7 +1558,7 @@ void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& co #if __arm__ || __x86_64__ -void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr) +void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) { #if __arm__ uint32_t* instructions = (uint32_t*)stub; @@ -1571,8 +1578,17 @@ void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void* // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache) if ( lazyPointerAddr != originalLazyPointerAddr ) { + // only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) + const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); + if ( lazyPointerImage != NULL ) + return; + // copy newly re-bound lazy pointer value to shared lazy pointer *lazyPointerAddr = *originalLazyPointerAddr; + + if ( context.verboseBind ) + dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", + this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); } } #endif @@ -1635,7 +1651,7 @@ void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& // sanity check symbol index of stub and lazy pointer match if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] ) continue; - this->updateAlternateLazyPointer(stub, lpa); + this->updateAlternateLazyPointer(stub, lpa, context); } #endif diff --git a/src/ImageLoaderMachOCompressed.h b/src/ImageLoaderMachOCompressed.h index 99c5529..6d8c5b7 100644 --- a/src/ImageLoaderMachOCompressed.h +++ b/src/ImageLoaderMachOCompressed.h @@ -76,7 +76,7 @@ protected: virtual void rebase(const LinkContext& context); virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const; virtual bool containsSymbol(const void* addr) const; - virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const; virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; virtual const char* exportedSymbolName(const Symbol* symbol) const; virtual unsigned int exportedSymbolCount() const; @@ -130,7 +130,7 @@ private: uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver); static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* s); void updateOptimizedLazyPointers(const LinkContext& context); - void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr); + void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context); const struct dyld_info_command* fDyldInfo; }; diff --git a/src/dyld.cpp b/src/dyld.cpp index 6854ef5..d143bc9 100644 --- a/src/dyld.cpp +++ b/src/dyld.cpp @@ -30,10 +30,15 @@ #include #include #include // mach_absolute_time() +#include #include #include #include -#include +#include +#include +#include +#include +#include #include #include #include @@ -44,7 +49,8 @@ #include #include #include -#include +#include +#include <_simple.h> #ifndef CPU_SUBTYPE_ARM_V5TEJ @@ -82,6 +88,7 @@ #include "ImageLoader.h" #include "ImageLoaderMachO.h" #include "dyldLibSystemInterface.h" +#include "dyldSyscallInterface.h" #if DYLD_SHARED_CACHE_SUPPORT #include "dyld_cache_format.h" #endif @@ -89,15 +96,8 @@ #include "coreSymbolicationDyldSupport.hpp" #endif -// 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); - +// not libc header for send() syscall interface +extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); // ARM is the only architecture that use cpu-sub-types @@ -134,7 +134,19 @@ extern "C" void* __dso_handle; // // // +namespace dyld { + struct RegisteredDOF { const mach_header* mh; int registrationID; }; + struct DylibOverride { const char* installName; const char* override; }; +} + + +VECTOR_NEVER_DESTRUCTED(ImageLoader*); +VECTOR_NEVER_DESTRUCTED(dyld::RegisteredDOF); +VECTOR_NEVER_DESTRUCTED(dyld::ImageCallback); +VECTOR_NEVER_DESTRUCTED(dyld::DylibOverride); +VECTOR_NEVER_DESTRUCTED(ImageLoader::DynamicReference); +VECTOR_NEVER_DESTRUCTED(dyld_image_state_change_handler); namespace dyld { @@ -181,15 +193,17 @@ struct EnvironmentVariables { // DYLD_PRINT_RPATHS ==> gLinkContext.verboseRPaths // DYLD_PRINT_INTERPOSING ==> gLinkContext.verboseInterposing }; - + + + typedef std::vector StateHandlers; -struct RegisteredDOF { const mach_header* mh; int registrationID; }; -struct DylibOverride { const char* installName; const char* override; }; + enum RestrictedReason { restrictedNot, restrictedBySetGUid, restrictedBySegment, restrictedByEntitlements }; // all global state static const char* sExecPath = NULL; +static const char* sExecShortName = NULL; static const macho_header* sMainExecutableMachHeader = NULL; #if CPU_SUBTYPES_SUPPORTED static cpu_type_t sHostCPU; @@ -197,7 +211,7 @@ static cpu_subtype_t sHostCPUsubtype; #endif static ImageLoader* sMainExecutable = NULL; static bool sProcessIsRestricted = false; -static RestrictedReason sRestrictedReason = restrictedNot; +static RestrictedReason sRestrictedReason = restrictedNot; static unsigned int sInsertedDylibCount = 0; static std::vector sAllImages; static std::vector sImageRoots; @@ -205,8 +219,8 @@ static std::vector sImageFilesNeedingTermination; static std::vector sImageFilesNeedingDOFUnregistration; static std::vector sAddImageCallbacks; static std::vector sRemoveImageCallbacks; -static StateHandlers sSingleHandlers[7]; -static StateHandlers sBatchHandlers[7]; +static void* sSingleHandlers[7][3]; +static void* sBatchHandlers[7][3]; static ImageLoader* sLastImageByAddressCache; static EnvironmentVariables sEnv; static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; @@ -217,7 +231,7 @@ static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked static const dyld_cache_header* sSharedCache = NULL; static long sSharedCacheSlide = 0; static bool sSharedCacheIgnoreInodeAndTimeStamp = false; -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if __IPHONE_OS_VERSION_MIN_REQUIRED && DYLD_SHARED_CACHE_SUPPORT bool gSharedCacheOverridden = false; static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR; static bool sDylibsOverrideCache = false; @@ -232,6 +246,13 @@ const struct LibSystemHelpers* gLibSystemHelpers = NULL; bool gRunInitializersOldWay = false; #endif static std::vector sDylibOverrides; +#if !TARGET_IPHONE_SIMULATOR +static int sLogSocket = -1; +#endif +static bool sFrameworksFoundAsDylibs = false; + +static std::vector sDynamicReferences; + // // The MappedRanges structure is used for fast address->image lookups. @@ -348,8 +369,9 @@ void throwf(const char* format, ...) //#define ALTERNATIVE_LOGFILE "/dev/console" - +#if !TARGET_IPHONE_SIMULATOR static int sLogfile = STDERR_FILENO; +#endif #if LOG_BINDINGS static int sBindingsLogfile = -1; @@ -377,27 +399,96 @@ void logBindings(const char* format, ...) va_end(list); } } - #endif -void log(const char* format, ...) +#if !TARGET_IPHONE_SIMULATOR +// based on CFUtilities.c: also_do_stderr() +static bool useSyslog() +{ + // Use syslog() for processes managed by launchd + if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 11) ) { + if ( (*gLibSystemHelpers->isLaunchdOwned)() ) { + return true; + } + } + + // If stderr is not available, use syslog() + struct stat sb; + int result = fstat(STDERR_FILENO, &sb); + if ( result < 0 ) + return true; // file descriptor 2 is closed + + return false; +} + + +static void socket_syslogv(int priority, const char* format, va_list list) +{ + // lazily create socket and connection to syslogd + if ( sLogSocket == -1 ) { + sLogSocket = ::socket(AF_UNIX, SOCK_DGRAM, 0); + if (sLogSocket == -1) + return; // cannot log + ::fcntl(sLogSocket, F_SETFD, 1); + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, _PATH_LOG, sizeof(addr.sun_path)); + if ( ::connect(sLogSocket, (struct sockaddr *)&addr, sizeof(addr)) == -1 ) { + ::close(sLogSocket); + sLogSocket = -1; + return; + } + } + + // format message to syslogd like: "Process[pid]: message" + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf == NULL ) + return; + if ( _simple_sprintf(buf, "<%d>%s[%d]: ", LOG_USER|LOG_NOTICE, sExecShortName, getpid()) == 0 ) { + if ( _simple_vsprintf(buf, format, list) == 0 ) { + const char* p = _simple_string(buf); + ::__sendto(sLogSocket, p, strlen(p), 0, NULL, 0); + } + } + _simple_sfree(buf); +} + +void vlog(const char* format, va_list list) +{ + if ( useSyslog() ) + socket_syslogv(LOG_ERR, format, list); + else + _simple_vdprintf(sLogfile, format, list); +} + +void log(const char* format, ...) { va_list list; va_start(list, format); - _simple_vdprintf(sLogfile, format, list); + vlog(format, list); va_end(list); } -void warn(const char* format, ...) + +void vwarn(const char* format, va_list list) { _simple_dprintf(sLogfile, "dyld: warning, "); + _simple_vdprintf(sLogfile, format, list); +} + +void warn(const char* format, ...) +{ va_list list; va_start(list, format); - _simple_vdprintf(sLogfile, format, list); + vwarn(format, list); va_end(list); } +#endif // !TARGET_IPHONE_SIMULATOR + + // control access to sAllImages through a lock // because global dyld lock is not held during initialization phase of dlopen() static long sAllImagesLock = 0; @@ -434,7 +525,7 @@ private: FileOpener::FileOpener(const char* path) : fd(-1) { - fd = open(path, O_RDONLY, 0); + fd = my_open(path, O_RDONLY, 0); } FileOpener::~FileOpener() @@ -476,7 +567,7 @@ static void registerDOFs(const std::vector& dofs) 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", + dyld::log("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n", dofs[i].dof, dofs[i].imageShortName, info.registrationID); } } @@ -529,34 +620,33 @@ static const char* updateAllImages(enum dyld_image_states state, uint32_t infoCo } -static StateHandlers* stateToHandlers(dyld_image_states state, StateHandlers handlersArray[8]) +static StateHandlers* stateToHandlers(dyld_image_states state, void* handlersArray[7][3]) { switch ( state ) { case dyld_image_state_mapped: - return &handlersArray[0]; + return reinterpret_cast(&handlersArray[0]); case dyld_image_state_dependents_mapped: - return &handlersArray[1]; + return reinterpret_cast(&handlersArray[1]); case dyld_image_state_rebased: - return &handlersArray[2]; + return reinterpret_cast(&handlersArray[2]); case dyld_image_state_bound: - return &handlersArray[3]; + return reinterpret_cast(&handlersArray[3]); case dyld_image_state_dependents_initialized: - return &handlersArray[4]; + return reinterpret_cast(&handlersArray[4]); case dyld_image_state_initialized: - return &handlersArray[5]; + return reinterpret_cast(&handlersArray[5]); case dyld_image_state_terminated: - return &handlersArray[6]; + return reinterpret_cast(&handlersArray[6]); } return NULL; } - static void notifySingle(dyld_image_states state, const ImageLoader* image) { //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); @@ -592,8 +682,8 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image) if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { dyld::log("dyld core symbolication unload notification: %p %s\n", image->machHeader(), image->getPath()); } - if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) { - CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage; + if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) { + CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld::gProcessInfo->coreSymbolicationShmPage; if ( connection->is_valid_version() ) { coresymbolication_unload_image(connection, image); } @@ -611,7 +701,7 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image) // dylibs needed by the main executable, dyld_all_image_infos is not yet set // up, leading to usually brief crash logs. // -// This function manually adds the images loaded so far to dyld_all_image_infos. +// This function manually adds the images loaded so far to dyld::gProcessInfo. // It should only be called before terminating. // void syncAllImages() @@ -624,8 +714,8 @@ void syncAllImages() info.imageFileModDate = image->lastModified(); // add to all_image_infos if not already there bool found = false; - int existingCount = dyld_all_image_infos.infoArrayCount; - const dyld_image_info* existing = dyld_all_image_infos.infoArray; + int existingCount = dyld::gProcessInfo->infoArrayCount; + const dyld_image_info* existing = dyld::gProcessInfo->infoArray; if ( existing != NULL ) { for (int i=0; i < existingCount; ++i) { if ( existing[i].imageLoadAddress == info.imageLoadAddress ) { @@ -721,8 +811,8 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image dyld::log("dyld core symbolication load notification: %p %s\n", (*it)->machHeader(), (*it)->getPath()); } } - if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) { - CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage; + if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) { + CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld::gProcessInfo->coreSymbolicationShmPage; if ( connection->is_valid_version() ) { // This needs to be captured now uint64_t load_timestamp = mach_absolute_time(); @@ -794,6 +884,29 @@ static void setRunInitialzersOldWay() } #endif +static void addDynamicReference(ImageLoader* from, ImageLoader* to) { + // don't add dynamic reference if either are in the shared cache + if( from->inSharedCache() ) + return; + if( to->inSharedCache() ) + return; + + // don't add dynamic reference if there already is a static one + if ( from->dependsOn(to) ) + return; + + // don't add if this combination already exists + for (std::vector::iterator it=sDynamicReferences.begin(); it != sDynamicReferences.end(); ++it) { + if ( (it->from == from) && (it->to == to) ) + return; + } + //dyld::log("addDynamicReference(%s, %s\n", from->getShortName(), to->getShortName()); + ImageLoader::DynamicReference t; + t.from = from; + t.to = to; + sDynamicReferences.push_back(t); +} + static void addImage(ImageLoader* image) { // add to master list @@ -831,17 +944,23 @@ static void addImage(ImageLoader* image) } +// +// Helper for std::remove_if +// +class RefUsesImage { +public: + RefUsesImage(ImageLoader* image) : _image(image) {} + bool operator()(const ImageLoader::DynamicReference& ref) const { + return ( (ref.from == _image) || (ref.to == _image) ); + } +private: + ImageLoader* _image; +}; + + + void removeImage(ImageLoader* image) { - // if in termination list, pull it out and run terminator - for (std::vector::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) { - if ( *it == image ) { - sImageFilesNeedingTermination.erase(it); - image->doTermination(gLinkContext); - break; - } - } - // 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() ) { @@ -865,10 +984,6 @@ void removeImage(ImageLoader* image) // notify notifySingle(dyld_image_state_terminated, image); - // dyld should directly call __cxa_finalize() - if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 8) ) - (*gLibSystemHelpers->cxa_finalize)(image->machHeader()); - // remove from mapped images table removedMappedRanges(image); @@ -882,6 +997,9 @@ void removeImage(ImageLoader* image) } allImagesUnlock(); + // remove from sDynamicReferences + sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end()); + // 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; @@ -904,6 +1022,28 @@ void removeImage(ImageLoader* image) } +void runImageTerminators(ImageLoader* image) +{ + // if in termination list, pull it out and run terminator + bool mightBeMore; + do { + mightBeMore = false; + for (std::vector::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) { + if ( *it == image ) { + sImageFilesNeedingTermination.erase(it); + image->doTermination(gLinkContext); + mightBeMore = true; + break; + } + } + } while ( mightBeMore ); + + // dyld should directly call __cxa_finalize() + if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 8) ) + (*gLibSystemHelpers->cxa_finalize)(image->machHeader()); + +} + static void terminationRecorder(ImageLoader* image) { sImageFilesNeedingTermination.push_back(image); @@ -917,10 +1057,6 @@ const char* getExecutablePath() void initializeMainExecutable() { - // apply interposing to initial set of images - // do this before making the __IMPORT segments in shared cache read-only - sMainExecutable->applyInterposing(gLinkContext); - // record that we've reached this step gLinkContext.startedInitializingMainExecutable = true; @@ -1007,7 +1143,11 @@ static void checkDylibOverride(const char* dylibFile) if ( altVersion > prevVersion ) { // found an even newer override free((void*)(it->override)); - it->override = strdup(dylibFile); + char resolvedPath[PATH_MAX]; + if ( realpath(dylibFile, resolvedPath) != NULL ) + it->override = strdup(resolvedPath); + else + it->override = strdup(dylibFile); break; } } @@ -1016,7 +1156,11 @@ static void checkDylibOverride(const char* dylibFile) if ( ! entryExists ) { DylibOverride entry; entry.installName = strdup(sysInstallName); - entry.override = strdup(dylibFile); + char resolvedPath[PATH_MAX]; + if ( realpath(dylibFile, resolvedPath) != NULL ) + entry.override = strdup(resolvedPath); + else + entry.override = strdup(dylibFile); sDylibOverrides.push_back(entry); //dyld::log("added override: %s -> %s\n", entry.installName, entry.override); } @@ -1362,6 +1506,9 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch else if ( strcmp(key, "DYLD_PRINT_INTERPOSING") == 0 ) { gLinkContext.verboseInterposing = true; } + else if ( strcmp(key, "DYLD_PRINT_CODE_SIGNATURES") == 0 ) { + gLinkContext.verboseCodeSignatures = true; + } else if ( strcmp(key, "DYLD_SHARED_REGION") == 0 ) { if ( strcmp(value, "private") == 0 ) { gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; @@ -1460,6 +1607,20 @@ static void checkLoadCommandEnvironmentVariables() } #endif // SUPPORT_LC_DYLD_ENVIRONMENT + +static bool hasCodeSignatureLoadCommand(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_CODE_SIGNATURE) + return true; + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} + #if SUPPORT_VERSIONED_PATHS static void checkVersionedPaths() @@ -1500,6 +1661,8 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep) } } *d++ = NULL; +// Disable warnings about DYLD_ env vars being ignored. The warnings are causing too much confusion. +#if 0 if ( removedCount != 0 ) { dyld::log("dyld: DYLD_ environment variables being ignored because "); switch (sRestrictedReason) { @@ -1516,7 +1679,7 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep) break; } } - +#endif // slide apple parameters if ( removedCount > 0 ) { *applep = d; @@ -1583,6 +1746,12 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths; } + // DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together + if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) { + dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n"); + gLinkContext.imageSuffix = NULL; + } + #if SUPPORT_VERSIONED_PATHS checkVersionedPaths(); #endif @@ -1800,6 +1969,9 @@ static const cpu_subtype_t kARM[kARM_RowCount][9] = { // armv7k can run: v7k, v6, v5, and v4 { CPU_SUBTYPE_ARM_V7K, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + // armv7s can run: v7s, v7, v7f, v7k, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7S, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V7K, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + // armv7 can run: v7, v6, v5, and v4 { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, @@ -2015,7 +2187,7 @@ static bool findInSharedCacheImage(const char* path, const struct stat* stat_buf // if stat() not done yet, do it now struct stat statb; if ( stat_buf == NULL ) { - if ( stat(path, &statb) == -1 ) + if ( my_stat(path, &statb) == -1 ) return false; stat_buf = &statb; } @@ -2045,7 +2217,7 @@ static bool findInSharedCacheImage(const char* path, const struct stat* stat_buf // 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(aPath, &pathInCacheStatBuf) != -1 ) + if ( my_stat(aPath, &pathInCacheStatBuf) != -1 ) cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf->st_dev) && (pathInCacheStatBuf.st_ino == stat_buf->st_ino) ); } if ( cacheHit ) { @@ -2227,7 +2399,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const // just return NULL if file not found, but record any other errors struct stat stat_buf; - if ( stat(path, &stat_buf) == -1 ) { + if ( my_stat(path, &stat_buf) == -1 ) { int err = errno; if ( err != ENOENT ) { exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, err)); @@ -2268,7 +2440,7 @@ static ImageLoader* loadPhase5stat(const char* path, const LoadContext& context, ImageLoader* image = NULL; *imageFound = false; *statErrNo = 0; - if ( stat(path, stat_buf) == 0 ) { + if ( my_stat(path, stat_buf) == 0 ) { // in case image was renamed or found via symlinks, check for inode match image = findLoadedImage(*stat_buf); if ( image != NULL ) { @@ -2566,7 +2738,9 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load } } } - if ( libraryPaths != NULL ) { + // An executable with the same name as a framework & DYLD_LIBRARY_PATH pointing to it gets loaded twice + // Some apps depend on frameworks being found via library paths + if ( (libraryPaths != NULL) && ((frameworkPartialPath == NULL) || sFrameworksFoundAsDylibs) ) { const char* libraryLeafName = getLibraryLeafName(path); const int libraryLeafNameLen = strlen(libraryLeafName); for(const char* const* lp = libraryPaths; *lp != NULL; ++lp) { @@ -2685,7 +2859,7 @@ ImageLoader* load(const char* path, const LoadContext& context) for (std::vector::iterator it = exceptions.begin(); it != exceptions.end(); ++it) { free((void*)(*it)); } -#if __IPHONE_OS_VERSION_MIN_REQUIRED +#if __IPHONE_OS_VERSION_MIN_REQUIRED && DYLD_SHARED_CACHE_SUPPORT // if loaded image is not from cache, but original path is in cache // set gSharedCacheOverridden flag to disable some ObjC optimizations if ( !gSharedCacheOverridden ) { @@ -2732,11 +2906,6 @@ ImageLoader* load(const char* path, const LoadContext& context) #elif __x86_64__ #define ARCH_NAME "x86_64" #define ARCH_CACHE_MAGIC "dyld_v1 x86_64" - #define SHARED_REGION_READ_ONLY_START 0x7FFF80000000LL - #define SHARED_REGION_READ_ONLY_END 0x7FFFC0000000LL - #define SHARED_REGION_WRITABLE_START 0x7FFF70000000LL - #define SHARED_REGION_WRITABLE_END 0x7FFF80000000LL - #define SLIDEABLE_CACHE_SUPPORT 1 #elif __ARM_ARCH_5TEJ__ #define ARCH_NAME "armv5" #define ARCH_CACHE_MAGIC "dyld_v1 armv5" @@ -2746,27 +2915,15 @@ ImageLoader* load(const char* path, const LoadContext& context) #elif __ARM_ARCH_7F__ #define ARCH_NAME "armv7f" #define ARCH_CACHE_MAGIC "dyld_v1 armv7f" - #define SHARED_REGION_READ_ONLY_START 0x30000000 - #define SHARED_REGION_READ_ONLY_END 0x3E000000 - #define SHARED_REGION_WRITABLE_START 0x3E000000 - #define SHARED_REGION_WRITABLE_END 0x40000000 - #define SLIDEABLE_CACHE_SUPPORT 1 #elif __ARM_ARCH_7A__ #define ARCH_NAME "armv7" #define ARCH_CACHE_MAGIC "dyld_v1 armv7" - #define SHARED_REGION_READ_ONLY_START 0x30000000 - #define SHARED_REGION_READ_ONLY_END 0x3E000000 - #define SHARED_REGION_WRITABLE_START 0x3E000000 - #define SHARED_REGION_WRITABLE_END 0x40000000 - #define SLIDEABLE_CACHE_SUPPORT 1 +#elif __ARM_ARCH_7S__ + #define ARCH_NAME "armv7s" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7s" #elif __ARM_ARCH_7K__ #define ARCH_NAME "armv7k" #define ARCH_CACHE_MAGIC "dyld_v1 armv7k" - #define SHARED_REGION_READ_ONLY_START 0x30000000 - #define SHARED_REGION_READ_ONLY_END 0x3E000000 - #define SHARED_REGION_WRITABLE_START 0x3E000000 - #define SHARED_REGION_WRITABLE_END 0x40000000 - #define SLIDEABLE_CACHE_SUPPORT 1 #endif @@ -2778,10 +2935,9 @@ static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_add } -static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], +static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], int codeSignatureMappingIndex, int slide, void* slideInfo, uint32_t slideInfoSize) { -#if __IPHONE_OS_VERSION_MIN_REQUIRED // register code signature blob for whole dyld cache if ( codeSignatureMappingIndex != -1 ) { fsignatures_t siginfo; @@ -2789,10 +2945,11 @@ static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uin siginfo.fs_blob_start = (void*)mappings[codeSignatureMappingIndex].sfm_file_offset; siginfo.fs_blob_size = mappings[codeSignatureMappingIndex].sfm_size; int result = fcntl(fd, F_ADDFILESIGS, &siginfo); - if ( result == -1 ) - dyld::log("dyld: code signature for shared cache failed with errno=%d\n", errno); + // don't warn in chrooted case because mapping syscall is about to fail too + if ( (result == -1) && gLinkContext.verboseMapping ) + dyld::log("dyld: code signature registration for shared cache failed with errno=%d\n", errno); } -#endif + if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) { return syscall(438, fd, count, mappings, slide, slideInfo, slideInfoSize); } @@ -2801,7 +2958,7 @@ static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uin vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE); // notify gdb or other lurkers that this process is no longer using the shared region - dyld_all_image_infos.processDetachedFromSharedRegion = true; + dyld::gProcessInfo->processDetachedFromSharedRegion = true; // map cache just for this process with mmap() const shared_file_mapping_np* const start = mappings; @@ -2833,7 +2990,6 @@ static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uin } } -#if SLIDEABLE_CACHE_SUPPORT // update all __DATA pages with slide info if ( slide != 0 ) { const uintptr_t dataPagesStart = mappings[1].sfm_address; @@ -2860,7 +3016,6 @@ static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uin } } } -#endif // SLIDEABLE_CACHE_SUPPORT // succesfully mapped shared cache for just this process gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; @@ -2876,54 +3031,64 @@ const void* imMemorySharedCacheHeader() int openSharedCacheFile() { - char path[1024]; - strcpy(path, sSharedCacheDir); - strcat(path, "/"); - strcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME); - return ::open(path, O_RDONLY); + char path[MAXPATHLEN]; + strlcpy(path, sSharedCacheDir, MAXPATHLEN); + strlcat(path, "/", MAXPATHLEN); + strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, MAXPATHLEN); + return my_open(path, O_RDONLY, 0); } -#if SLIDEABLE_CACHE_SUPPORT static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappings[]) { +#if __x86_64__ + // x86_64 has a two memory regions: + // 256MB at 0x00007FFF70000000 + // 1024MB at 0x00007FFF80000000 + // Some old shared caches have r/w region after rx region, so all regions slide within 1GB range + // Newer shared caches have r/w region based at 0x7FFF70000000 and r/o regions at 0x7FFF80000000, so each part has max slide + if ( (mappingsCount >= 3) && (mappings[1].sfm_init_prot == (VM_PROT_READ|VM_PROT_WRITE)) && (mappings[1].sfm_address == 0x00007FFF70000000) ) { + const uint64_t rwSize = mappings[1].sfm_size; + const uint64_t rwSlop = 0x10000000ULL - rwSize; + const uint64_t roSize = (mappings[2].sfm_address + mappings[2].sfm_size) - mappings[0].sfm_address; + const uint64_t roSlop = 0x40000000ULL - roSize; + const uint64_t space = (rwSlop < roSlop) ? rwSlop : roSlop; + + // choose new random slide + long slide = (arc4random() % space) & (-4096); + //dyld::log("rwSlop=0x%0llX, roSlop=0x%0llX\n", rwSlop, roSlop); + //dyld::log("space=0x%0llX, slide=0x%0lX\n", space, slide); + + // update mappings + for(uint32_t i=0; i < mappingsCount; ++i) { + mappings[i].sfm_address += slide; + } + + return slide; + } + // else fall through to handle old style cache +#endif // get bounds of cache - uint64_t readOnlyLowAddress = 0; - uint64_t readOnlyHighAddress = 0; - uint64_t writableLowAddress = 0; - uint64_t writableHighAddress = 0; + uint64_t lowAddress = 0; + uint64_t highAddress = 0; for(uint32_t i=0; i < mappingsCount; ++i) { - if ( mappings[i].sfm_init_prot & VM_PROT_WRITE ) { - writableLowAddress = mappings[i].sfm_address; - writableHighAddress = mappings[i].sfm_address + mappings[i].sfm_size; + if ( lowAddress == 0 ) { + lowAddress = mappings[i].sfm_address; + highAddress = mappings[i].sfm_address + mappings[i].sfm_size; } else { - if ( readOnlyLowAddress == 0 ) { - readOnlyLowAddress = mappings[i].sfm_address; - readOnlyHighAddress = mappings[i].sfm_address + mappings[i].sfm_size; - } - else { - if ( readOnlyLowAddress < mappings[i].sfm_address ) { - readOnlyHighAddress = mappings[i].sfm_address + mappings[i].sfm_size; - } - else { - readOnlyLowAddress = mappings[i].sfm_address; - } - } + if ( mappings[i].sfm_address < lowAddress ) + lowAddress = mappings[i].sfm_address; + if ( (mappings[i].sfm_address + mappings[i].sfm_size) > highAddress ) + highAddress = mappings[i].sfm_address + mappings[i].sfm_size; } } - // find read-only slop space - uint64_t roSpace = SHARED_REGION_READ_ONLY_END - readOnlyHighAddress; - - // find writable slop space - uint64_t rwSpace = SHARED_REGION_WRITABLE_END - writableHighAddress; + // find slop space + const uint64_t space = (SHARED_REGION_BASE + SHARED_REGION_SIZE) - highAddress; // choose new random slide - long slideSpace = (roSpace > rwSpace) ? rwSpace : roSpace; - long slide = (arc4random() % slideSpace) & (-4096); - //dyld::log("roSpace=0x%0llX\n", roSpace); - //dyld::log("rwSpace=0x%0llX\n", rwSpace); - //dyld::log("slideSpace=0x%0lX\n", slideSpace); + long slide = (arc4random() % space) & (-4096); + //dyld::log("slideSpace=0x%0llX\n", space); //dyld::log("slide=0x%0lX\n", slide); // update mappings @@ -2933,31 +3098,36 @@ static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappin return slide; } -#endif // SLIDEABLE_CACHE_SUPPORT static void mapSharedCache() { - uint64_t cacheBaseAddress; + uint64_t cacheBaseAddress = 0; // 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 ) + if ( gLinkContext.verboseMapping ) { dyld::log("dyld: existing shared cached in memory is not compatible\n"); + return; + } } // check if cache file is slidable - dyld_cache_header* header = (dyld_cache_header*)sSharedCache; + const dyld_cache_header* header = sSharedCache; if ( (header->mappingOffset >= 0x48) && (header->slideInfoSize != 0) ) { // solve for slide by comparing loaded address to address of first region const uint8_t* loadedAddress = (uint8_t*)sSharedCache; const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)(loadedAddress+header->mappingOffset); const uint8_t* preferedLoadAddress = (uint8_t*)(long)(mappings[0].address); sSharedCacheSlide = loadedAddress - preferedLoadAddress; - dyld_all_image_infos.sharedCacheSlide = sSharedCacheSlide; + dyld::gProcessInfo->sharedCacheSlide = sSharedCacheSlide; //dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress); } + // if cache has a uuid, copy it + if ( header->mappingOffset >= 0x68 ) { + memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); + } } else { #if __i386__ || __x86_64__ @@ -2970,7 +3140,7 @@ static void mapSharedCache() // user booted machine in safe-boot mode struct stat dyldCacheStatInfo; // Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path - if ( ::stat(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) { + if ( my_stat(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) { struct timeval bootTimeValue; size_t bootTimeValueSize = sizeof(bootTimeValue); if ( (sysctlbyname("kern.boottime", &bootTimeValue, &bootTimeValueSize, NULL, 0) == 0) && (bootTimeValue.tv_sec != 0) ) { @@ -2996,6 +3166,8 @@ static void mapSharedCache() shared_file_mapping_np mappings[header->mappingCount+1]; // add room for code-sig unsigned int mappingCount = header->mappingCount; int codeSignatureMappingIndex = -1; + int readWriteMappingIndex = -1; + int readOnlyMappingIndex = -1; // validate that the cache file has not been truncated bool goodCache = false; struct stat stat_buf; @@ -3014,8 +3186,13 @@ static void mapSharedCache() dyld::log("dyld: shared cached file is corrupt: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); goodCache = false; } + if ( (mappings[i].sfm_init_prot & (VM_PROT_READ|VM_PROT_WRITE)) == (VM_PROT_READ|VM_PROT_WRITE) ) { + readWriteMappingIndex = i; + } + if ( mappings[i].sfm_init_prot == VM_PROT_READ ) { + readOnlyMappingIndex = i; + } } -#if __IPHONE_OS_VERSION_MIN_REQUIRED // if shared cache is code signed, add a mapping for the code signature uint32_t signatureSize = header->codeSignatureSize; // zero size in header means signature runs to end-of-file @@ -3030,13 +3207,12 @@ static void mapSharedCache() mappings[codeSignatureMappingIndex].sfm_max_prot = VM_PROT_READ; mappings[codeSignatureMappingIndex].sfm_init_prot = VM_PROT_READ; } -#endif } #if __MAC_OS_X_VERSION_MIN_REQUIRED // sanity check that /usr/lib/libSystem.B.dylib stat() info matches cache if ( header->imagesCount * sizeof(dyld_cache_image_info) + header->imagesOffset < 8192 ) { bool foundLibSystem = false; - if ( stat("/usr/lib/libSystem.B.dylib", &stat_buf) == 0 ) { + if ( my_stat("/usr/lib/libSystem.B.dylib", &stat_buf) == 0 ) { const dyld_cache_image_info* images = (dyld_cache_image_info*)&firstPages[header->imagesOffset]; const dyld_cache_image_info* const imagesEnd = &images[header->imagesCount]; for (const dyld_cache_image_info* p = images; p < imagesEnd; ++p) { @@ -3052,12 +3228,19 @@ static void mapSharedCache() goodCache = false; } } -#endif +#endif + if ( goodCache && (readWriteMappingIndex == -1) ) { + dyld::log("dyld: shared cached file is missing read/write mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); + goodCache = false; + } + if ( goodCache && (readOnlyMappingIndex == -1) ) { + dyld::log("dyld: shared cached file is missing read-only mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); + goodCache = false; + } if ( goodCache ) { long cacheSlide = 0; void* slideInfo = NULL; uint32_t slideInfoSize = 0; - #if SLIDEABLE_CACHE_SUPPORT // check if shared cache contains slid info if ( header->slideInfoSize != 0 ) { // don't slide shared cache if ASLR disabled (main executable didn't slide) @@ -3066,20 +3249,23 @@ static void mapSharedCache() else { // generate random slide amount cacheSlide = pickCacheSlide(mappingCount, mappings); - slideInfo = (void*)(long)(mappings[2].sfm_address + (header->slideInfoOffset - mappings[2].sfm_file_offset)); + slideInfo = (void*)(long)(mappings[readOnlyMappingIndex].sfm_address + (header->slideInfoOffset - mappings[readOnlyMappingIndex].sfm_file_offset)); slideInfoSize = header->slideInfoSize; // add VM_PROT_SLIDE bit to __DATA area of cache - mappings[1].sfm_max_prot |= VM_PROT_SLIDE; - mappings[1].sfm_init_prot |= VM_PROT_SLIDE; + mappings[readWriteMappingIndex].sfm_max_prot |= VM_PROT_SLIDE; + mappings[readWriteMappingIndex].sfm_init_prot |= VM_PROT_SLIDE; } } - #endif if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, codeSignatureMappingIndex, cacheSlide, slideInfo, slideInfoSize) == 0) { // successfully mapped cache into shared region sSharedCache = (dyld_cache_header*)mappings[0].sfm_address; sSharedCacheSlide = cacheSlide; - dyld_all_image_infos.sharedCacheSlide = cacheSlide; + dyld::gProcessInfo->sharedCacheSlide = cacheSlide; //dyld::log("sSharedCache=%p sSharedCacheSlide=0x%08lX\n", sSharedCache, sSharedCacheSlide); + // if cache has a uuid, copy it + if ( header->mappingOffset >= 0x68 ) { + memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); + } } else { if ( gLinkContext.verboseMapping ) @@ -3150,7 +3336,6 @@ static void mapSharedCache() } #endif } -#if __IPHONE_OS_VERSION_MIN_REQUIRED if ( gLinkContext.verboseMapping ) { // list the code blob dyld_cache_header* header = (dyld_cache_header*)sSharedCache; @@ -3158,7 +3343,7 @@ static void mapSharedCache() // zero size in header means signature runs to end-of-file if ( signatureSize == 0 ) { struct stat stat_buf; - if ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) + if ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 ) signatureSize = stat_buf.st_size - header->codeSignatureOffset; } if ( signatureSize != 0 ) { @@ -3167,9 +3352,10 @@ static void mapSharedCache() dyld::log(" 0x%08llX->0x%08llX (code signature)\n", codeBlobStart, codeBlobStart+signatureSize); } } +#if __IPHONE_OS_VERSION_MIN_REQUIRED // check for file that enables dyld shared cache dylibs to be overridden struct stat enableStatBuf; - sDylibsOverrideCache = ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0 ); + sDylibsOverrideCache = ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0 ); #endif } } @@ -3293,10 +3479,10 @@ void halt(const char* message) static void setErrorStrings(unsigned errorCode, const char* errorClientOfDylibPath, const char* errorTargetDylibPath, const char* errorSymbol) { - dyld_all_image_infos.errorKind = errorCode; - dyld_all_image_infos.errorClientOfDylibPath = errorClientOfDylibPath; - dyld_all_image_infos.errorTargetDylibPath = errorTargetDylibPath; - dyld_all_image_infos.errorSymbol = errorSymbol; + dyld::gProcessInfo->errorKind = errorCode; + dyld::gProcessInfo->errorClientOfDylibPath = errorClientOfDylibPath; + dyld::gProcessInfo->errorTargetDylibPath = errorTargetDylibPath; + dyld::gProcessInfo->errorSymbol = errorSymbol; } @@ -3583,6 +3769,8 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha #if SUPPORT_OLD_CRT_INITIALIZATION gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay; #endif + gLinkContext.findImageContainingAddress = &findImageContainingAddress; + gLinkContext.addDynamicReference = &addDynamicReference; gLinkContext.bindingOptions = ImageLoader::kBindingNone; gLinkContext.argc = argc; gLinkContext.argv = argv; @@ -3717,13 +3905,13 @@ static void printAllImages() 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(); + dyld::log(" state=%d, dlopen-count=%d, never-unload=%d, in-use=%d, name=%s\n", + imageState, image->dlopenCount(), image->neverUnload(), image->isMarkedInUse(), image->getShortName()); } } #endif -void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChain& loaderRPaths) +void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths) { // add to list of known images. This did not happen at creation time for bundles if ( image->isBundle() && !image->isLinked() ) @@ -3735,7 +3923,7 @@ void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChai // process images try { - image->link(gLinkContext, forceLazysBound, false, loaderRPaths); + image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths); } catch (const char* msg) { garbageCollectImages(); @@ -3752,36 +3940,100 @@ void runInitializers(ImageLoader* image) image->runInitializers(gLinkContext, initializerTimes[0]); } +// This function is called at the end of dlclose() when the reference count goes to zero. +// The dylib being unloaded may have brought in other dependent dylibs when it was loaded. +// Those dependent dylibs need to be unloaded, but only if they are not referenced by +// something else. We use a standard mark and sweep garbage collection. +// +// The tricky part is that when a dylib is unloaded it may have a termination function that +// can run and itself call dlclose() on yet another dylib. The problem is that this +// sort of gabage collection is not re-entrant. Instead a terminator's call to dlclose() +// which calls garbageCollectImages() will just set a flag to re-do the garbage collection +// when the current pass is done. +// +// Also note that this is done within the dyld global lock, so it is always single threaded. +// void garbageCollectImages() { - // keep scanning list of images until entire list is scanned with no unreferenced images - bool mightBeUnreferencedImages = true; - while ( mightBeUnreferencedImages ) { - mightBeUnreferencedImages = false; + static bool sDoingGC = false; + static bool sRedo = false; + + if ( sDoingGC ) { + // GC is currently being run, just set a flag to have it run again. + sRedo = true; + return; + } + + sDoingGC = true; + do { + sRedo = false; + + // mark phase: mark all images not-in-use for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { ImageLoader* image = *it; - if ( (image->referenceCount() == 0) && !image->neverUnload() && !image->isBeingRemoved() ) { - if ( image->isReferencedUpward() ) { - // temp hack for rdar://problem/10973109 - // if an image is upwardly referenced, we really need to scan all images - // to see if any are still using it. - continue; - } - try { - //dyld::log("garbageCollectImages: deleting %p %s\n", image, image->getPath()); - image->setBeingRemoved(); - removeImage(image); - ImageLoader::deleteImage(image); - } - catch (const char* msg) { - dyld::warn("problem deleting image: %s\n", msg); - } - mightBeUnreferencedImages = true; - break; + //dyld::log("gc: neverUnload=%d name=%s\n", image->neverUnload(), image->getShortName()); + image->markNotUsed(); + } + + // sweep phase: mark as in-use, images reachable from never-unload or in-use image + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( (image->dlopenCount() != 0) || image->neverUnload() ) { + image->markedUsedRecursive(sDynamicReferences); } } - } + + // collect phase: build array of images not marked in-use + ImageLoader* deadImages[sAllImages.size()]; + unsigned deadCount = 0; + unsigned i = 0; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( ! image->isMarkedInUse() ) { + deadImages[i++] = image; + if (gLogAPIs) dyld::log("dlclose(), found unused image %p %s\n", image, image->getShortName()); + ++deadCount; + } + } + + // collect phase: run termination routines for images not marked in-use + // TO DO: When libc has cxa_finalize() that takes array of images, pass deadImages[] instead of the for loop here + for (unsigned i=0; i < deadCount; ++i) { + ImageLoader* image = deadImages[i]; + try { + if (gLogAPIs) dyld::log("dlclose(), running terminators for %p %s\n", image, image->getShortName()); + runImageTerminators(image); + } + catch (const char* msg) { + dyld::warn("problem running terminators for image: %s\n", msg); + } + } + + // collect phase: delete all images which are not marked in-use + bool mightBeMore; + do { + mightBeMore = false; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( ! image->isMarkedInUse() ) { + try { + if (gLogAPIs) dyld::log("dlclose(), deleting %p %s\n", image, image->getShortName()); + removeImage(image); + ImageLoader::deleteImage(image); + mightBeMore = true; + break; // interator in invalidated by this removal + } + catch (const char* msg) { + dyld::warn("problem deleting image: %s\n", msg); + } + } + } + } while ( mightBeMore ); + } while (sRedo); + sDoingGC = false; + //printAllImages(); + } @@ -3801,7 +4053,7 @@ void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths) try { if ( image->isBundle() ) sBundleBeingLoaded = image; // hack - image->link(gLinkContext, false, true, loaderRPaths); + image->link(gLinkContext, false, true, false, loaderRPaths); } catch (const char* msg) { preflight_finally(image); @@ -3827,46 +4079,53 @@ static void loadInsertedDylib(const char* path) context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES context.rpath = NULL; image = load(path, context); - image->setNeverUnload(); + } + catch (const char* msg) { + halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg)); } catch (...) { - halt(dyld::mkstringf("could not load inserted library: %s\n", path)); + halt(dyld::mkstringf("could not load inserted library '%s'\n", path)); } } static bool processRestricted(const macho_header* mainExecutableMH) -{ - // all processes with setuid or setgid bit set are restricted +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // ask kernel if code signature of program makes it restricted + uint32_t flags; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + if ( flags & CS_ENFORCEMENT ) { + gLinkContext.codeSigningEnforced = true; + } + } + if (flags & CS_RESTRICT) { + sRestrictedReason = restrictedByEntitlements; + return true; + } +#else + gLinkContext.codeSigningEnforced = true; +#endif + + // all processes with setuid or setgid bit set are restricted if ( issetugid() ) { sRestrictedReason = restrictedBySetGUid; return true; } - const uid_t euid = geteuid(); - if ( (euid != 0) && hasRestrictedSegment(mainExecutableMH) ) { + // Respect __RESTRICT,__restrict section for root processes + if ( hasRestrictedSegment(mainExecutableMH) ) { // existence of __RESTRICT/__restrict section make process restricted sRestrictedReason = restrictedBySegment; return true; } - -#if __MAC_OS_X_VERSION_MIN_REQUIRED - // ask kernel if code signature of program makes it restricted - uint32_t flags; - if ( syscall(SYS_csops /* 169 */, - 0 /* asking about myself */, - CS_OPS_STATUS, - &flags, - sizeof(flags)) != -1) { - if (flags & CS_RESTRICT) { - sRestrictedReason = restrictedByEntitlements; - return true; - } - } -#endif return false; } +bool processIsRestricted() +{ + return sProcessIsRestricted; +} // Add dyld to uuidArray to enable symbolication of stackshots @@ -3891,6 +4150,166 @@ static void addDyldImageToUUIDList() } } +#if __MAC_OS_X_VERSION_MIN_REQUIRED +typedef int (*open_proc_t)(const char*, int, int); +typedef int (*fcntl_proc_t)(int, int, void*); +typedef int (*ioctl_proc_t)(int, unsigned long, void*); +static void* getProcessInfo() { return dyld::gProcessInfo; } +static SyscallHelpers sSysCalls = { + 1, + (open_proc_t)&open, + &close, + &pread, + &write, + &mmap, + &munmap, + &madvise, + &stat, + (fcntl_proc_t)&fcntl, + (ioctl_proc_t)&ioctl, + &issetugid, + &getcwd, + &realpath, + &vm_allocate, + &vm_deallocate, + &vm_protect, + &vlog, + &vwarn, + &pthread_mutex_lock, + &pthread_mutex_unlock, + &mach_thread_self, + &mach_port_deallocate, + &task_self_trap, + &mach_timebase_info, + &OSAtomicCompareAndSwapPtrBarrier, + &OSMemoryBarrier, + &getProcessInfo, + &__error, + &mach_absolute_time +}; + +__attribute__((noinline)) +static uintptr_t useSimulatorDyld(int fd, const macho_header* mainExecutableMH, const char* dyldPath, + int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue) +{ + *startGlue = 0; + + // verify simulator dyld file is owned by root + struct stat sb; + if ( fstat(fd, &sb) == -1 ) + return 0; + if ( sb.st_uid != 0 ) + return 0; + + // read first page of dyld file + uint8_t firstPage[4096]; + if ( pread(fd, firstPage, 4096, 0) != 4096 ) + return 0; + + // if fat file, pick matching slice + uint64_t fileOffset = 0; + uint64_t fileLength = sb.st_size; + const fat_header* fileStartAsFat = (fat_header*)firstPage; + if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + if ( !fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) + return 0; + // re-read buffer from start of mach-o slice in fat file + if ( pread(fd, firstPage, 4096, fileOffset) != 4096 ) + return 0; + } + else if ( !isCompatibleMachO(firstPage, dyldPath) ) { + return 0; + } + + // calculate total size of dyld segments + const macho_header* mh = (const macho_header*)firstPage; + uintptr_t mappingSize = 0; + uintptr_t preferredLoadAddress = 0; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + mappingSize += seg->vmsize; + if ( seg->fileoff == 0 ) + preferredLoadAddress = seg->vmaddr; + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // reserve space, then mmap each segment + vm_address_t loadAddress = 0; + uintptr_t entry = 0; + if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 ) + return 0; + cmd = cmds; + struct linkedit_data_command* codeSigCmd = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + uintptr_t requestedLoadAddress = seg->vmaddr - preferredLoadAddress + loadAddress; + void* segAddress = ::mmap((void*)requestedLoadAddress, seg->filesize, seg->initprot, MAP_FIXED | MAP_PRIVATE, fd, fileOffset + seg->fileoff); + //dyld::log("dyld_sim %s mapped at %p\n", seg->segname, segAddress); + if ( segAddress == (void*)(-1) ) + return 0; + } + break; + case LC_UNIXTHREAD: + { + #if __i386__ + const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); + entry = (registers->__eip + loadAddress - preferredLoadAddress); + #elif __x86_64__ + const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); + entry = (registers->__rip + loadAddress - preferredLoadAddress); + #endif + } + break; + case LC_CODE_SIGNATURE: + codeSigCmd = (struct linkedit_data_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + if ( codeSigCmd != NULL ) { + fsignatures_t siginfo; + siginfo.fs_file_start=fileOffset; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of code-signature in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of code-signature + int result = fcntl(fd, F_ADDFILESIGS, &siginfo); + if ( result == -1 ) { + if ( (errno == EPERM) || (errno == EBADEXEC) ) + return 0; + } + } + close(fd); + + // notify debugger that dyld_sim is loaded + dyld_image_info info; + info.imageLoadAddress = (mach_header*)loadAddress; + info.imageFilePath = strdup(dyldPath); + info.imageFileModDate = sb.st_mtime; + addImagesToAllImages(1, &info); + dyld::gProcessInfo->notification(dyld_image_adding, 1, &info); + + // jump into new simulator dyld + typedef uintptr_t (*sim_entry_proc_t)(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers* vtable, uintptr_t* startGlue); + sim_entry_proc_t newDyld = (sim_entry_proc_t)entry; + return (*newDyld)(argc, argv, envp, apple, mainExecutableMH, (macho_header*)loadAddress, + loadAddress - preferredLoadAddress, + &sSysCalls, startGlue); +} +#endif // @@ -3903,8 +4322,27 @@ uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[], uintptr_t* startGlue) -{ +{ uintptr_t result = 0; + sMainExecutableMachHeader = mainExecutableMH; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // if this is host dyld, check to see if iOS simulator is being run + const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH"); + if ( rootPath != NULL ) { + // look to see if simulator has its own dyld + char simDyldPath[PATH_MAX]; + strlcpy(simDyldPath, rootPath, PATH_MAX); + strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX); + int fd = my_open(simDyldPath, O_RDONLY, 0); + if ( fd != -1 ) { + result = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue); + if ( !result && (*startGlue == 0) ) + halt("problem loading iOS simulator dyld"); + return result; + } + } +#endif + CRSetCrashLogMessage("dyld: launch started"); #ifdef ALTERNATIVE_LOGFILE sLogfile = open(ALTERNATIVE_LOGFILE, O_WRONLY | O_CREAT | O_APPEND); @@ -3936,6 +4374,11 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, setContext(mainExecutableMH, argc, argv, envp, apple); // Pickup the pointer to the exec path. + sExecPath = _simple_getenv(apple, "executable_path"); + + // Remove interim apple[0] transition code from dyld + if (!sExecPath) sExecPath = apple[0]; + sExecPath = apple[0]; bool ignoreEnvironmentVariables = false; if ( sExecPath[0] != '/' ) { @@ -3950,7 +4393,12 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, sExecPath = s; } } - sMainExecutableMachHeader = mainExecutableMH; + // Remember short name of process for later logging + sExecShortName = ::strrchr(sExecPath, '/'); + if ( sExecShortName != NULL ) + ++sExecShortName; + else + sExecShortName = sExecPath; sProcessIsRestricted = processRestricted(mainExecutableMH); if ( sProcessIsRestricted ) { #if SUPPORT_LC_DYLD_ENVIRONMENT @@ -3983,18 +4431,22 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, #ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE // Add gating mechanism to dyld support system order file generation process - WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld_all_image_infos.systemOrderFlag); + WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag); #endif + try { // add dyld itself to UUID list addDyldImageToUUIDList(); - CRSetCrashLogMessage("dyld: launch, loading dependent libraries"); + if ( sProcessIsRestricted ) + CRSetCrashLogMessage("dyld: launch, loading dependent libraries, ignoring DYLD_* env vars"); + else + CRSetCrashLogMessage("dyld: launch, loading dependent libraries"); // instantiate ImageLoader for main executable sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); - sMainExecutable->setNeverUnload(); gLinkContext.mainExecutable = sMainExecutable; gLinkContext.processIsRestricted = sProcessIsRestricted; + gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH); // load shared cache checkSharedRegionDisable(); #if DYLD_SHARED_CACHE_SUPPORT @@ -4012,8 +4464,8 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, // link main executable gLinkContext.linkingMainExecutable = true; - link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, ImageLoader::RPathChain(NULL, NULL)); - gLinkContext.linkingMainExecutable = false; + link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL)); + sMainExecutable->setNeverUnloadRecursive(); if ( sMainExecutable->forceFlat() ) { gLinkContext.bindFlat = true; gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; @@ -4025,11 +4477,20 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, 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)); + link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL)); + image->setNeverUnloadRecursive(); // only INSERTED libraries can interpose image->registerInterposing(); } } + // apply interposing to initial set of images + for(int i=0; i < sImageRoots.size(); ++i) { + sImageRoots[i]->applyInterposing(gLinkContext); + } + gLinkContext.linkingMainExecutable = false; + + // do weak binding only after all inserted images linked + sMainExecutable->weakBind(gLinkContext); CRSetCrashLogMessage("dyld: launch, running initializers"); #if SUPPORT_OLD_CRT_INITIALIZATION @@ -4052,7 +4513,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, else { // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main() result = (uintptr_t)sMainExecutable->getMain(); - *startGlue = NULL; + *startGlue = 0; } } catch(const char* message) { @@ -4077,8 +4538,7 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, - -}; // namespace +} // namespace diff --git a/src/dyld.h b/src/dyld.h index 19b4431..9c55ecd 100644 --- a/src/dyld.h +++ b/src/dyld.h @@ -23,6 +23,7 @@ */ #include +#include #include "ImageLoader.h" #include "mach-o/dyld_priv.h" @@ -58,8 +59,11 @@ namespace dyld { extern ImageLoader::LinkContext gLinkContext; + extern struct dyld_all_image_infos* gProcessInfo; extern bool gLogAPIs; +#if DYLD_SHARED_CACHE_SUPPORT extern bool gSharedCacheOverridden; +#endif extern const struct LibSystemHelpers* gLibSystemHelpers; #if SUPPORT_OLD_CRT_INITIALIZATION extern bool gRunInitializersOldWay; @@ -69,9 +73,10 @@ namespace dyld { extern void registerUndefinedHandler(UndefinedHandler); extern void initializeMainExecutable(); extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths); - extern void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChain& loaderRPaths); + extern void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths); extern void runInitializers(ImageLoader* image); extern void runTerminators(void*); + extern void runImageTerminators(ImageLoader* image); extern const char* getExecutablePath(); extern bool validImage(const ImageLoader*); extern ImageLoader* getIndexedImage(uint32_t index); @@ -89,7 +94,7 @@ namespace dyld { extern ImageLoader* cloneImage(ImageLoader* image); extern void forEachImageDo( void (*)(ImageLoader*, void*), void*); extern uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], - const char* apple[], uintptr_t* startGlue); + const char* apple[], uintptr_t* startGlue) __attribute__((noinline)); // extern void halt(const char* message) __attribute__((noreturn)); extern void setErrorMessage(const char* msg); extern const char* getErrorMessage(); @@ -103,9 +108,14 @@ namespace dyld { extern int openSharedCacheFile(); extern const void* imMemorySharedCacheHeader(); extern uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset); +#if DYLD_SHARED_CACHE_SUPPORT extern bool inSharedCache(const char* path); +#endif #if LOG_BINDINGS extern void logBindings(const char* format, ...); #endif -}; + extern bool processIsRestricted(); + extern int my_stat(const char* path, struct stat* buf); + extern int my_open(const char* path, int flag, int other); +} diff --git a/src/dyldAPIs.cpp b/src/dyldAPIs.cpp index d001184..b99ea46 100644 --- a/src/dyldAPIs.cpp +++ b/src/dyldAPIs.cpp @@ -147,6 +147,7 @@ static struct dyld_func dyld_funcs[] = { #if __IPHONE_OS_VERSION_MIN_REQUIRED {"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden }, #endif + {"__dyld_process_is_restricted", (void*)dyld::processIsRestricted }, // deprecated #if DEPRECATED_APIS_SUPPORTED @@ -239,6 +240,9 @@ struct __NSObjectFileImage const void* imageBaseAddress; // not used with OFI created from files size_t imageLength; // not used with OFI created from files }; + + +VECTOR_NEVER_DESTRUCTED(NSObjectFileImage); static std::vector sObjectFileImages; @@ -542,7 +546,7 @@ const struct mach_header* addImage(void* callerAddress, const char* path, bool s if ( image != NULL ) { if ( context.matchByInstallName ) image->setMatchInstallPath(true); - dyld::link(image, false, callersRPaths); + dyld::link(image, false, false, callersRPaths); dyld::runInitializers(image); // images added with NSAddImage() can never be unloaded image->setNeverUnload(); @@ -972,6 +976,9 @@ NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, dyld::clearErrorMessage(); try { + if ( (options & NSLINKMODULE_OPTION_CAN_UNLOAD) != 0 ) + objectFileImage->image->setCanUnload(); + // NSLinkModule allows a bundle to be link multpile times // each link causes the bundle to be copied to a new address if ( objectFileImage->image->isLinked() ) { @@ -998,7 +1005,7 @@ NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); // load libraries, rebase, bind, to make this image usable - dyld::link(objectFileImage->image, forceLazysBound, ImageLoader::RPathChain(NULL,NULL)); + dyld::link(objectFileImage->image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL)); // bump reference count to keep this bundle from being garbage collected objectFileImage->image->incrementDlopenReferenceCount(); @@ -1044,7 +1051,7 @@ static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_s bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); // load libraries, rebase, bind, to make this image usable - dyld::link(image, forceLazysBound, ImageLoader::RPathChain(NULL,NULL)); + dyld::link(image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL)); // run initializers unless magic flag says not to if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) @@ -1107,6 +1114,7 @@ bool NSUnLinkModule(NSModule module, uint32_t options) ImageLoader* image = NSModuleToImageLoader(module); if ( image == NULL ) return false; + dyld::runImageTerminators(image); dyld::removeImage(image); if ( (options & NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) != 0 ) @@ -1182,9 +1190,9 @@ void _dyld_fork_child() // If dyld is sending load/unload notices to CoreSymbolication, the shared memory // page is not copied on fork. // NULL the CoreSymbolication shared memory pointer to prevent a crash. - dyld_all_image_infos.coreSymbolicationShmPage = NULL; + dyld::gProcessInfo->coreSymbolicationShmPage = NULL; // for safety, make sure child starts with clean systemOrderFlag - dyld_all_image_infos.systemOrderFlag = 0; + dyld::gProcessInfo->systemOrderFlag = 0; } typedef void (*MonitorProc)(char *lowpc, char *highpc); @@ -1262,7 +1270,7 @@ static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) dyld::gLibSystemHelpers = helpers; // let gdb know it is safe to run code in inferior that might call malloc() - dyld_all_image_infos.libSystemInitialized = true; + dyld::gProcessInfo->libSystemInitialized = true; #if __arm__ if ( helpers->version >= 5 ) { @@ -1279,6 +1287,13 @@ static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) static void dlerrorClear() { if ( dyld::gLibSystemHelpers != NULL ) { + // dlerror buffer leak + // dlerrorClear() should not force allocation, but zero it if already allocated + if ( dyld::gLibSystemHelpers->version >= 10 ) { + if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) + return; + } + // first char of buffer is flag whether string (starting at second char) is valid char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); buffer[0] = '\0'; @@ -1468,7 +1483,7 @@ void* dlopen(const char* path, int mode) if ( (mode & RTLD_NOLOAD) == 0 ) { bool alreadyLinked = image->isLinked(); bool forceLazysBound = ( (mode & RTLD_NOW) != 0 ); - dyld::link(image, forceLazysBound, callersRPaths); + dyld::link(image, forceLazysBound, false, callersRPaths); if ( ! alreadyLinked ) { // only hide exports if image is not already in use if ( (mode & RTLD_LOCAL) != 0 ) @@ -1514,9 +1529,12 @@ void* dlopen(const char* path, int mode) // load() succeeded but, link() failed // back down reference count and do GC image->decrementDlopenReferenceCount(); - dyld::garbageCollectImages(); + if ( image->dlopenCount() == 0 ) + dyld::garbageCollectImages(); } const char* str = dyld::mkstringf("dlopen(%s, %d): %s", path, mode, msg); + if ( dyld::gLogAPIs ) + dyld::log(" %s() failed, error: '%s'\n", __func__, str); dlerrorSet(str); free((void*)str); free((void*)msg); // our free() will do nothing if msg is a string literal @@ -1537,6 +1555,8 @@ void* dlopen(const char* path, int mode) CRSetCrashLogMessage(NULL); dyld::gLibSystemHelpers->releaseGlobalDyldLock(); } + if ( dyld::gLogAPIs && (result != NULL) ) + dyld::log(" %s(%s) ==> %p\n", __func__, path, result); return result; } @@ -1562,7 +1582,8 @@ int dlclose(void* handle) return -1; } // remove image if reference count went to zero - dyld::garbageCollectImages(); + if ( image->dlopenCount() == 0 ) + dyld::garbageCollectImages(); return 0; } else { @@ -1622,6 +1643,12 @@ char* dlerror() dyld::log("%s()\n", __func__); if ( dyld::gLibSystemHelpers != NULL ) { + // if using newer libdyld.dylib and buffer if buffer not yet allocated, return NULL + if ( dyld::gLibSystemHelpers->version >= 10 ) { + if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) + return NULL; + } + // first char of buffer is flag whether string (starting at second char) is valid char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); if ( buffer[0] != '\0' ) { // if valid buffer @@ -1741,7 +1768,7 @@ void* dlsym(void* handle, const char* symbolName) const struct dyld_all_image_infos* _dyld_get_all_image_infos() { - return &dyld_all_image_infos; + return dyld::gProcessInfo; } #if !__arm__ @@ -1787,7 +1814,11 @@ const char* dyld_image_path_containing_address(const void* address) #if __IPHONE_OS_VERSION_MIN_REQUIRED bool dyld_shared_cache_some_image_overridden() { + #if DYLD_SHARED_CACHE_SUPPORT return dyld::gSharedCacheOverridden; + #else + return true; + #endif } #endif diff --git a/src/dyldAPIsInLibSystem.cpp b/src/dyldAPIsInLibSystem.cpp index 0417585..314c658 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-2009 Apple Inc. All rights reserved. + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -25,14 +25,17 @@ #include #include #include +#include #include #include +#include #include "mach-o/dyld.h" #include "mach-o/dyld_priv.h" #include "dyldLock.h" +#include "start_glue.h" extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso); extern "C" void __cxa_finalize(const void *dso); @@ -423,6 +426,8 @@ const char* libraryName) return(-1); } +#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff)) + /* * Returns the sdk version (encode as nibble XXXX.YY.ZZ) the @@ -435,68 +440,118 @@ const char* libraryName) */ uint32_t dyld_get_sdk_version(const mach_header* mh) { -#if __LP64__ - const load_command* cmds = (load_command*)((char *)mh + sizeof(mach_header_64)); -#else - const load_command* cmds = (load_command*)((char *)mh + sizeof(mach_header)); -#endif + const load_command* cmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + cmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + cmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return 0; // not a mach-o file, or wrong endianness + const version_min_command* versCmd; const dylib_command* dylibCmd; const load_command* cmd = cmds; + const char* dylibName; +#if __IPHONE_OS_VERSION_MIN_REQUIRED + uint32_t foundationVers = 0; +#else uint32_t libSystemVers = 0; +#endif for(uint32_t i = 0; i < mh->ncmds; ++i) { switch ( cmd->cmd ) { - case LC_VERSION_MIN_MACOSX: +#if __IPHONE_OS_VERSION_MIN_REQUIRED case LC_VERSION_MIN_IPHONEOS: +#else + case LC_VERSION_MIN_MACOSX: +#endif versCmd = (version_min_command*)cmd; +#ifdef DICE_KIND_DATA if ( versCmd->sdk != 0 ) return versCmd->sdk; // found explicit SDK version +#else + if ( versCmd->reserved != 0 ) + return versCmd->reserved; // found explicit SDK version +#endif break; case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_LOAD_UPWARD_DYLIB: dylibCmd = (dylib_command*)cmd; - if ( strcmp((char*)dylibCmd + dylibCmd->dylib.name.offset, "/usr/lib/libSystem.B.dylib") == 0 ) + dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset; +#if __IPHONE_OS_VERSION_MIN_REQUIRED + if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 ) + foundationVers = dylibCmd->dylib.current_version; +#else + if ( strcmp(dylibName, "/usr/lib/libSystem.B.dylib") == 0 ) libSystemVers = dylibCmd->dylib.current_version; - else if ( strcmp((char*)dylibCmd + dylibCmd->dylib.name.offset, "/usr/lib/libSystem.dylib") == 0 ) - return 0x00040000; // all iOS simulator have same libSystem.dylib version +#endif break; } cmd = (load_command*)((char *)cmd + cmd->cmdsize); } + + struct DylibToOSMapping { + uint32_t dylibVersion; + uint32_t osVersion; + }; - if ( libSystemVers != 0 ) { - // found linked libSystem.B.dylib version linked against #if __IPHONE_OS_VERSION_MIN_REQUIRED - // convert libSystem.B.dylib version to iOS sdk version - if ( libSystemVers < 0x006F0010 ) // libSystem 111.0.16 in 3.0 - return 0x00020000; // 2.0 - else if ( libSystemVers < 0x006F0201 ) // libSystem 111.2.1 in 3.1 - return 0x00030000; // 3.0 - else if ( libSystemVers < 0x007D020B ) // libSystem 125.2.11 in 4.0 - return 0x00030100; // 3.1 - else if ( libSystemVers < 0x007D0400 ) // libSystem 125.4 in 4.1 and in 4.2 - return 0x00040000; // 4.0 - else if ( libSystemVers < 0x009F0000 ) // libSystem 159 in 4.3 - return 0x00040100; // 4.1 - else if ( libSystemVers < 0x00A10000 ) // libSystem 161 in 5.0 - return 0x00040300; // 4.3 - else - return 0x00050000; + static const DylibToOSMapping foundationMapping[] = { + { PACKED_VERSION(678,24,0), DYLD_IOS_VERSION_2_0 }, + { PACKED_VERSION(678,26,0), DYLD_IOS_VERSION_2_1 }, + { PACKED_VERSION(678,29,0), DYLD_IOS_VERSION_2_2 }, + { PACKED_VERSION(678,47,0), DYLD_IOS_VERSION_3_0 }, + { PACKED_VERSION(678,51,0), DYLD_IOS_VERSION_3_1 }, + { PACKED_VERSION(678,60,0), DYLD_IOS_VERSION_3_2 }, + { PACKED_VERSION(751,32,0), DYLD_IOS_VERSION_4_0 }, + { PACKED_VERSION(751,37,0), DYLD_IOS_VERSION_4_1 }, + { PACKED_VERSION(751,49,0), DYLD_IOS_VERSION_4_2 }, + { PACKED_VERSION(751,58,0), DYLD_IOS_VERSION_4_3 }, + { PACKED_VERSION(881,0,0), DYLD_IOS_VERSION_5_0 }, + { PACKED_VERSION(890,1,0), DYLD_IOS_VERSION_5_1 }, + { PACKED_VERSION(992,0,0), DYLD_IOS_VERSION_6_0 }, + { PACKED_VERSION(993,0,0), DYLD_IOS_VERSION_6_1 }, + { PACKED_VERSION(1038,14,0),DYLD_IOS_VERSION_7_0 }, // check final + { PACKED_VERSION(0,0,0), DYLD_IOS_VERSION_7_0 } + }; + + if ( foundationVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=foundationMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( foundationVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } + } + #else - // convert libSystem.B.dylib version to MacOSX sdk version - if ( libSystemVers < 0x006F0000 ) // libSystem 111 in 10.5 - return 0x000A0400; // 10.4 - else if ( libSystemVers < 0x007B0000 ) // libSystem 123 in 10.6 - return 0x000A0500; // 10.5 - else if ( libSystemVers < 0x009F0000 ) // libSystem 159 in 10.7 - return 0x000A0600; // 10.6 - else if ( libSystemVers < 0x00A10000 ) // libSystem 161 in 10.8 - return 0x000A0700; // 10.7 - else - return 0x000A0800; // 10.8 -#endif + // Note: versions are for the GM release. The last entry should + // always be zero. At the start of the next major version, + // a new last entry needs to be added and the previous zero + // updated to the GM dylib version. + static const DylibToOSMapping libSystemMapping[] = { + { PACKED_VERSION(88,1,3), DYLD_MACOSX_VERSION_10_4 }, + { PACKED_VERSION(111,0,0), DYLD_MACOSX_VERSION_10_5 }, + { PACKED_VERSION(123,0,0), DYLD_MACOSX_VERSION_10_6 }, + { PACKED_VERSION(159,0,0), DYLD_MACOSX_VERSION_10_7 }, + { PACKED_VERSION(169,3,0), DYLD_MACOSX_VERSION_10_8 }, + { PACKED_VERSION(1197,0,0), DYLD_MACOSX_VERSION_10_9 }, + { PACKED_VERSION(0,0,0), DYLD_MACOSX_VERSION_10_9 } + }; + + if ( libSystemVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=libSystemMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( libSystemVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } } +#endif return 0; } @@ -509,17 +564,23 @@ uint32_t dyld_get_program_sdk_version() uint32_t dyld_get_min_os_version(const struct mach_header* mh) { -#if __LP64__ - const load_command* cmds = (load_command*)((char *)mh + sizeof(mach_header_64)); -#else - const load_command* cmds = (load_command*)((char *)mh + sizeof(mach_header)); -#endif + const load_command* cmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + cmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + cmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return 0; // not a mach-o file, or wrong endianness + const version_min_command* versCmd; const load_command* cmd = cmds; for(uint32_t i = 0; i < mh->ncmds; ++i) { switch ( cmd->cmd ) { - case LC_VERSION_MIN_MACOSX: +#if __IPHONE_OS_VERSION_MIN_REQUIRED case LC_VERSION_MIN_IPHONEOS: +#else + case LC_VERSION_MIN_MACOSX: +#endif versCmd = (version_min_command*)cmd; return versCmd->version; // found explicit min OS version break; @@ -1192,6 +1253,32 @@ static char* getPerThreadBufferFor_dlerror(uint32_t sizeRequired) return data->message; } +// dlerror buffer leak +// Only allocate buffer if an actual error message needs to be set +static bool hasPerThreadBufferFor_dlerror() +{ + if (!dlerrorPerThreadKeyInitialized ) + return false; + + return (pthread_getspecific(dlerrorPerThreadKey) != NULL); +} + +// use non-lazy pointer to vproc_swap_integer so that lazy binding does not recurse +typedef vproc_err_t (*vswapproc)(vproc_t vp, vproc_gsk_t key,int64_t *inval, int64_t *outval); +static vswapproc swapProc = &vproc_swap_integer; + +static bool isLaunchdOwned() { + static bool first = true; + static bool result; + if ( first ) { + int64_t val = 0; + (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val); + result = ( val != 0 ); + first = false; + } + return result; +} + #if DYLD_SHARED_CACHE_SUPPORT static void shared_cache_missing() @@ -1205,10 +1292,9 @@ static void shared_cache_out_of_date() } #endif // DYLD_SHARED_CACHE_SUPPORT -extern void* start; // the table passed to dyld containing thread helpers -static dyld::LibSystemHelpers sHelpers = { 9, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, +static dyld::LibSystemHelpers sHelpers = { 12, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, #if DYLD_SHARED_CACHE_SUPPORT &shared_cache_missing, &shared_cache_out_of_date, @@ -1220,7 +1306,11 @@ static dyld::LibSystemHelpers sHelpers = { 9, &dyldGlobalLockAcquire, &dyldGloba &malloc_size, &pthread_getspecific, &__cxa_finalize, - &start}; + address_of_start, + &hasPerThreadBufferFor_dlerror, + &isLaunchdOwned, + &vm_allocate, + &mmap}; // @@ -1230,9 +1320,7 @@ static dyld::LibSystemHelpers sHelpers = { 9, &dyldGlobalLockAcquire, &dyldGloba extern "C" void tlv_initializer(); extern "C" void _dyld_initializer(); void _dyld_initializer() -{ - DYLD_LOCK_INITIALIZER; - +{ void (*p)(dyld::LibSystemHelpers*); _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p); @@ -1383,6 +1471,19 @@ bool dyld_shared_cache_some_image_overridden() #endif +bool dyld_process_is_restricted() +{ + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_process_is_restricted", (void**)&p); + return p(); +} + + + + // SPI called __fork void _dyld_fork_child() { diff --git a/src/dyldExceptions.c b/src/dyldExceptions.c index 2520f8f..b015be5 100644 --- a/src/dyldExceptions.c +++ b/src/dyldExceptions.c @@ -67,7 +67,7 @@ static char sPreMainCxaGlobals[2*sizeof(long)]; char* __cxa_get_globals() { // if libSystem.dylib not yet initialized, or is old libSystem, use shared global - if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) ) + if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 7) ) return sPreMainCxaGlobals; if ( sCxaKey == 0 ) { @@ -75,7 +75,7 @@ char* __cxa_get_globals() // we don't need a lock because only one thread can be in dyld at a time _ZN4dyld17gLibSystemHelpersE->pthread_key_create(&sCxaKey, &free); } - char* data = (char*)pthread_getspecific(sCxaKey); + char* data = (char*)_ZN4dyld17gLibSystemHelpersE->pthread_getspecific(sCxaKey); if ( data == NULL ) { data = calloc(2,sizeof(void*)); _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data); @@ -87,10 +87,10 @@ char* __cxa_get_globals() char* __cxa_get_globals_fast() { // if libSystem.dylib not yet initialized, or is old libSystem, use shared global - if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) ) + if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 7) ) return sPreMainCxaGlobals; - return pthread_getspecific(sCxaKey); + return _ZN4dyld17gLibSystemHelpersE->pthread_getspecific(sCxaKey); } diff --git a/src/dyldInitialization.cpp b/src/dyldInitialization.cpp index a1266d7..7957c0c 100644 --- a/src/dyldInitialization.cpp +++ b/src/dyldInitialization.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -35,11 +36,18 @@ #include #endif #include "dyld.h" +#include "dyldSyscallInterface.h" + +// from dyld_gdb.cpp +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern void syncProcessInfo(); #ifndef MH_PIE #define MH_PIE 0x200000 #endif +// currently dyld has no initializers, but if some come back, set this to non-zero +#define DYLD_INITIALIZER_SUPPORT 0 #if __LP64__ #define LC_SEGMENT_COMMAND LC_SEGMENT_64 @@ -59,8 +67,10 @@ #define POINTER_RELOC GENERIC_RELOC_VANILLA #endif -// from dyld.cpp -namespace dyld { extern bool isRosetta(); }; + +#if TARGET_IPHONE_SIMULATOR +const dyld::SyscallHelpers* gSyscallHelpers = NULL; +#endif // @@ -71,8 +81,14 @@ namespace dyld { extern bool isRosetta(); }; namespace dyldbootstrap { + +#if DYLD_INITIALIZER_SUPPORT + typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[]); +extern const Initializer inits_start __asm("section$start$__DATA$__mod_init_func"); +extern const Initializer inits_end __asm("section$end$__DATA$__mod_init_func"); + // // For a regular executable, the crt code calls dyld to run the executables initializers. // For a static executable, crt directly runs the initializers. @@ -81,33 +97,11 @@ typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], co // static void runDyldInitializers(const struct macho_header* mh, intptr_t slide, int argc, const char* argv[], const char* envp[], const char* apple[]) { - const uint32_t cmd_count = mh->ncmds; - const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); - const struct load_command* cmd = cmds; - for (uint32_t i = 0; i < cmd_count; ++i) { - switch (cmd->cmd) { - case LC_SEGMENT_COMMAND: - { - const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; - 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 + slide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=0; i < count; ++i) { - Initializer func = inits[i]; - func(argc, argv, envp, apple); - } - } - } - } - break; - } - cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + for (const Initializer* p = &inits_start; p < &inits_end; ++p) { + (*p)(argc, argv, envp, apple); } } +#endif // DYLD_INITIALIZER_SUPPORT // @@ -202,17 +196,7 @@ static void rebaseDyld(const struct macho_header* mh, intptr_t slide) 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(); -} +extern "C" void __guard_setup(const char* apple[]); // @@ -229,12 +213,6 @@ uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* rebaseDyld(dyldsMachHeader, slide); } -#if __IPHONE_OS_VERSION_MIN_REQUIRED - // set pthread keys to dyld range - __pthread_tsd_first = 1; - _pthread_keys_init(); -#endif - // allow dyld to use mach messaging mach_init(); @@ -246,15 +224,55 @@ uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* while(*apple != NULL) { ++apple; } ++apple; + // set up random value for stack canary + __guard_setup(apple); + +#if DYLD_INITIALIZER_SUPPORT // run all C++ initializers inside dyld runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); - +#endif + // now that we are done bootstrapping dyld, call dyld's main uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader); return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); } +#if TARGET_IPHONE_SIMULATOR + +extern "C" uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers*, uintptr_t* startGlue); + + +uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers* sc, uintptr_t* startGlue) +{ + // if simulator dyld loaded slid, it needs to rebase itself + // we have to do this before using any global variables + if ( dyldSlide != 0 ) { + rebaseDyld(dyldMH, dyldSlide); + } + + // save table of syscall pointers + gSyscallHelpers = sc; + + // allow dyld to use mach messaging + mach_init(); + + // set up random value for stack canary + __guard_setup(apple); + + // setup gProcessInfo to point to host dyld's struct + dyld::gProcessInfo = (struct dyld_all_image_infos*)(sc->getProcessInfo()); + syncProcessInfo(); + + // now that we are done bootstrapping dyld, call dyld's main + uintptr_t appsSlide = slideOfMainExecutable(mainExecutableMH); + return dyld::_main(mainExecutableMH, appsSlide, argc, argv, envp, apple, startGlue); +} +#endif } // end of namespace diff --git a/src/dyldLibSystemGlue.c b/src/dyldLibSystemGlue.c index 0fdda05..20c6cdf 100644 --- a/src/dyldLibSystemGlue.c +++ b/src/dyldLibSystemGlue.c @@ -24,7 +24,13 @@ #include #include +#include + +// simulator does not have full libdyld.dylib - just a small libdyld_sim.dylib +#if ! TARGET_IPHONE_SIMULATOR + + // // This is the temporary private interface between libSystem.B.dylib and dyld // @@ -72,4 +78,5 @@ int _dyld_func_lookup(const char* dyld_func_name, void **address) return (*myDyldSection.lookup)(dyld_func_name, address); } +#endif //! TARGET_IPHONE_SIMULATOR diff --git a/src/dyldLibSystemInterface.h b/src/dyldLibSystemInterface.h index 9a5fd60..f191f78 100644 --- a/src/dyldLibSystemInterface.h +++ b/src/dyldLibSystemInterface.h @@ -63,9 +63,16 @@ namespace dyld { void (*cxa_finalize)(const void*); // added in version 9 void* startGlueToCallExit; + // added in version 10 + bool (*hasPerThreadBufferFor_dlerror)(); + // added in version 11 + bool (*isLaunchdOwned)(); + // added in version 12 + kern_return_t (*vm_alloc)(vm_map_t task, vm_address_t* addr, vm_size_t size, int flags); + void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); }; #if __cplusplus -}; +} #endif diff --git a/src/dyldLock.cpp b/src/dyldLock.cpp index dad1390..18dfe14 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -28,25 +28,12 @@ -static pthread_mutex_t sGlobalMutex; +static pthread_mutex_t sGlobalMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; // Need a way to determine if a gdb call to dlopen() would block int __attribute__((visibility("hidden"))) _dyld_global_lock_held = 0; -// -// This initializer can go away once the following is available: -// implement PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP -// -void dyldGlobalLockInitialize() -{ - pthread_mutexattr_t recursiveMutexAttr; - pthread_mutexattr_init(&recursiveMutexAttr); - pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&sGlobalMutex, &recursiveMutexAttr); -} - - LockHelper::LockHelper() { dyldGlobalLockAcquire(); @@ -60,12 +47,12 @@ LockHelper::~LockHelper() void dyldGlobalLockAcquire() { pthread_mutex_lock(&sGlobalMutex); - _dyld_global_lock_held = 1; + ++_dyld_global_lock_held; } void dyldGlobalLockRelease() { - _dyld_global_lock_held = 0; + --_dyld_global_lock_held; pthread_mutex_unlock(&sGlobalMutex); } diff --git a/src/dyldLock.h b/src/dyldLock.h index 2f4ec59..461012d 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-2007 Apple Inc. All rights reserved. + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -47,7 +47,6 @@ // } // -#define DYLD_LOCK_INITIALIZER dyldGlobalLockInitialize() #define DYLD_LOCK_THIS_BLOCK LockHelper _dyld_lock; #define DYLD_NO_LOCK_THIS_BLOCK diff --git a/src/dyldNew.cpp b/src/dyldNew.cpp index fe828ee..bd99cde 100644 --- a/src/dyldNew.cpp +++ b/src/dyldNew.cpp @@ -27,6 +27,7 @@ #include #include #include +#include extern "C" void* __dso_handle; @@ -40,7 +41,7 @@ extern "C" void* __dso_handle; #if __LP64__ // room for about ~1000 initial dylibs - #define DYLD_POOL_CHUNK_SIZE 200*1024 + #define DYLD_POOL_CHUNK_SIZE 224*1024 #else // room for about ~900 initial dylibs #define DYLD_POOL_CHUNK_SIZE 150*1024 @@ -147,3 +148,26 @@ extern "C" int _malloc_lock; int _malloc_lock = 0; +// dyld calls this which uses libSystem.dylib's vm_allocate if available +int vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags) +{ + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 12) ) { + return dyld::gLibSystemHelpers->vm_alloc(mach_task_self(), addr, size, flags); + } + else { + return ::vm_allocate(mach_task_self(), addr, size, flags); + } +} + +void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 12) ) { + return dyld::gLibSystemHelpers->mmap(addr, len, prot, flags, fd, offset); + } + else { + return ::mmap(addr, len, prot, flags, fd, offset); + } +} + + + diff --git a/src/dyldStartup.s b/src/dyldStartup.s index 11c8f1f..d340ee8 100644 --- a/src/dyldStartup.s +++ b/src/dyldStartup.s @@ -64,25 +64,17 @@ */ - - // Hack to make _offset_to_dyld_all_image_infos work - // Without this local symbol, assembler will error out about in subtraction expression - // The real _dyld_all_image_infos (non-weak) _dyld_all_image_infos is defined in dyld_gdb.o - // and the linker with throw this one away and use the real one instead. - .section __DATA,__datacoal_nt,coalesced - .globl _dyld_all_image_infos - .weak_definition _dyld_all_image_infos -_dyld_all_image_infos: .long 0 - - +#include .globl __dyld_start #ifdef __i386__ +#if !TARGET_IPHONE_SIMULATOR .data __dyld_start_static_picbase: .long L__dyld_start_picbase Lmh: .long ___dso_handle +#endif .text .align 2 @@ -99,14 +91,12 @@ _dyld_func_lookup: nop nop nop -_offset_to_dyld_all_image_infos: - .long _dyld_all_image_infos - . + 0x1010 - .long 0 + # space for future stable entry points - .space 16 + .space 32 - +#if !TARGET_IPHONE_SIMULATOR .text .align 4, 0x90 .globl __dyld_start @@ -160,7 +150,7 @@ Lapple: movl (%ebx),%ecx # look for NULL ending env[] array movl %ebx,12(%esp) # main param4 = apple pushl %edx # simulate return address into _start in libdyld jmp *%eax # jump to main(argc,argv,env,apple) with return address set to _start - +#endif .globl dyld_stub_binding_helper dyld_stub_binding_helper: @@ -171,10 +161,12 @@ L_end: #if __x86_64__ +#if !TARGET_IPHONE_SIMULATOR .data .align 3 __dyld_start_static: .quad __dyld_start +#endif # stable entry points into dyld .text @@ -191,13 +183,11 @@ _dyld_func_lookup: nop nop nop -_offset_to_dyld_all_image_infos: - .long _dyld_all_image_infos - . + 0x1010 - .long 0 - # space for future stable entry points - .space 16 + # space for future stable entry points + .space 24 +#if !TARGET_IPHONE_SIMULATOR .text .align 2,0x90 .globl __dyld_start @@ -239,7 +229,8 @@ Lapple: movq (%rcx),%r8 testq %r8,%r8 # look for NULL ending env[] array jne Lapple # main param4 = apple into %rcx jmp *%rax # jump to main(argc,argv,env,apple) with return address set to _start - + +#endif /* TARGET_IPHONE_SIMULATOR */ #endif /* __x86_64__ */ @@ -263,11 +254,8 @@ _dyld_func_lookup: b _branch_to_lookupDyldFunction nop -_offset_to_dyld_all_image_infos: - .long _dyld_all_image_infos - . + 0x1010 - .long 0 # space for future stable entry points - .space 16 + .space 24 // Hack to make ___dso_handle work diff --git a/src/dyldSyscallInterface.h b/src/dyldSyscallInterface.h new file mode 100644 index 0000000..a9bb0bf --- /dev/null +++ b/src/dyldSyscallInterface.h @@ -0,0 +1,79 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __DYLD_SYSCALL_HELPERS__ +#define __DYLD_SYSCALL_HELPERS__ + + +#if __cplusplus +namespace dyld { +#endif + + // + // This file contains the table of function pointers the host dyld supplies + // to the iOS simulator dyld. + // + struct SyscallHelpers + { + uintptr_t version; + int (*open)(const char* path, int oflag, int extra); + int (*close)(int fd); + ssize_t (*pread)(int fd, void* buf, size_t nbyte, off_t offset); + ssize_t (*write)(int fd, const void* buf, size_t nbyte); + void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + int (*munmap)(void* addr, size_t len); + int (*madvise)(void* addr, size_t len, int advice); + int (*stat)(const char* path, struct stat* buf); + int (*fcntl)(int fildes, int cmd, void* result); + int (*ioctl)(int fildes, unsigned long request, void* result); + int (*issetugid)(void); + char* (*getcwd)(char* buf, size_t size); + char* (*realpath)(const char* file_name, char* resolved_name); + kern_return_t (*vm_allocate)(vm_map_t target_task, vm_address_t *address, vm_size_t size, int flags); + kern_return_t (*vm_deallocate)(vm_map_t target_task, vm_address_t address, vm_size_t size); + kern_return_t (*vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t max, vm_prot_t prot); + void (*vlog)(const char* format, va_list list); + void (*vwarn)(const char* format, va_list list); + int (*pthread_mutex_lock)(pthread_mutex_t* m); + int (*pthread_mutex_unlock)(pthread_mutex_t* m); + mach_port_t (*mach_thread_self)(void); + kern_return_t (*mach_port_deallocate)(ipc_space_t task, mach_port_name_t name); + mach_port_name_t(*task_self_trap)(void); + kern_return_t (*mach_timebase_info)(mach_timebase_info_t info); + bool (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value); + void (*OSMemoryBarrier)(void); + void* (*getProcessInfo)(void); // returns dyld_all_image_infos*; + int* (*errnoAddress)(); + uint64_t (*mach_absolute_time)(); + }; + + extern const struct SyscallHelpers* gSyscallHelpers; + + +#if __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/dyld_gdb.cpp b/src/dyld_gdb.cpp index 2afa79c..3f1fa0e 100644 --- a/src/dyld_gdb.cpp +++ b/src/dyld_gdb.cpp @@ -33,6 +33,7 @@ #include "mach-o/dyld_gdb.h" #include "mach-o/dyld_images.h" #include "ImageLoader.h" +#include "dyld.h" #if __IPHONE_OS_VERSION_MIN_REQUIRED #define INITIAL_UUID_IMAGE_COUNT 4 @@ -40,9 +41,13 @@ #define INITIAL_UUID_IMAGE_COUNT 32 #endif +VECTOR_NEVER_DESTRUCTED(dyld_image_info); +VECTOR_NEVER_DESTRUCTED(dyld_uuid_info); + static std::vector sImageInfos; static std::vector sImageUUIDs; + void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) { // make initial size large enough that we probably won't need to re-alloc it @@ -51,26 +56,44 @@ void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) if ( sImageUUIDs.capacity() == 0 ) sImageUUIDs.reserve(4); // set infoArray to NULL to denote it is in-use - dyld_all_image_infos.infoArray = NULL; + dyld::gProcessInfo->infoArray = NULL; // append all new images for (uint32_t i=0; i < infoCount; ++i) sImageInfos.push_back(info[i]); - dyld_all_image_infos.infoArrayCount = sImageInfos.size(); + dyld::gProcessInfo->infoArrayCount = sImageInfos.size(); // set infoArray back to base address of vector (other process can now read) - dyld_all_image_infos.infoArray = &sImageInfos[0]; + dyld::gProcessInfo->infoArray = &sImageInfos[0]; } +#if TARGET_IPHONE_SIMULATOR +// called once in dyld_sim start up to copy image list from host dyld to sImageInfos +void syncProcessInfo() +{ + // may want to set version field of gProcessInfo if it might be different than host + if ( sImageInfos.size() == 0 ) { + sImageInfos.reserve(INITIAL_IMAGE_COUNT); + if ( dyld::gProcessInfo->infoArray != NULL ) { + for (uint32_t i=0; i < dyld::gProcessInfo->infoArrayCount; ++i) { + sImageInfos.push_back(dyld::gProcessInfo->infoArray[i]); + } + dyld::gProcessInfo->infoArray = &sImageInfos[0]; + dyld::gProcessInfo->infoArrayCount = sImageInfos.size(); + } + } + dyld::gProcessInfo->notification(dyld_image_info_change, 0, NULL); +} +#endif const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]) { // tell gdb that about the new images - dyld_all_image_infos.notification(dyld_image_adding, infoCount, info); + dyld::gProcessInfo->notification(dyld_image_adding, infoCount, info); // record initial count of images // so CrashReporter can note which images were dynamically loaded - if ( dyld_all_image_infos.initialImageCount == 0 ) - dyld_all_image_infos.initialImageCount = infoCount; + if ( dyld::gProcessInfo->initialImageCount == 0 ) + dyld::gProcessInfo->initialImageCount = infoCount; return NULL; } @@ -79,14 +102,14 @@ const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dy void addNonSharedCacheImageUUID(const dyld_uuid_info& info) { // set uuidArray to NULL to denote it is in-use - dyld_all_image_infos.uuidArray = NULL; + dyld::gProcessInfo->uuidArray = NULL; // append all new images sImageUUIDs.push_back(info); - dyld_all_image_infos.uuidArrayCount = sImageUUIDs.size(); + dyld::gProcessInfo->uuidArrayCount = sImageUUIDs.size(); // set uuidArray back to base address of vector (other process can now read) - dyld_all_image_infos.uuidArray = &sImageUUIDs[0]; + dyld::gProcessInfo->uuidArray = &sImageUUIDs[0]; } void removeImageFromAllImages(const struct mach_header* loadAddress) @@ -94,7 +117,7 @@ void removeImageFromAllImages(const struct mach_header* loadAddress) dyld_image_info goingAway; // set infoArray to NULL to denote it is in-use - dyld_all_image_infos.infoArray = NULL; + dyld::gProcessInfo->infoArray = NULL; // remove image from infoArray for (std::vector::iterator it=sImageInfos.begin(); it != sImageInfos.end(); it++) { @@ -104,14 +127,14 @@ void removeImageFromAllImages(const struct mach_header* loadAddress) break; } } - dyld_all_image_infos.infoArrayCount = sImageInfos.size(); + dyld::gProcessInfo->infoArrayCount = sImageInfos.size(); // set infoArray back to base address of vector - dyld_all_image_infos.infoArray = &sImageInfos[0]; + dyld::gProcessInfo->infoArray = &sImageInfos[0]; // set uuidArrayCount to NULL to denote it is in-use - dyld_all_image_infos.uuidArray = NULL; + dyld::gProcessInfo->uuidArray = NULL; // remove image from infoArray for (std::vector::iterator it=sImageUUIDs.begin(); it != sImageUUIDs.end(); it++) { @@ -120,50 +143,59 @@ void removeImageFromAllImages(const struct mach_header* loadAddress) break; } } - dyld_all_image_infos.uuidArrayCount = sImageUUIDs.size(); + dyld::gProcessInfo->uuidArrayCount = sImageUUIDs.size(); // set infoArray back to base address of vector - dyld_all_image_infos.uuidArray = &sImageUUIDs[0]; + dyld::gProcessInfo->uuidArray = &sImageUUIDs[0]; // tell gdb that about the new images - dyld_all_image_infos.notification(dyld_image_removing, 1, &goingAway); + dyld::gProcessInfo->notification(dyld_image_removing, 1, &goingAway); } -#if __arm__ -// work around for: gdb-1109: notifier in dyld does not work if it is in thumb -extern "C" void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]); -#else -static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]) -{ - // do nothing - // gdb sets a break point here to catch notifications - //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) - // dyld::log("dyld: %d loading at %p %s\n", i, dyld_all_image_infos.infoArray[i].imageLoadAddress, dyld_all_image_infos.infoArray[i].imageFilePath); -} -#endif - void setAlImageInfosHalt(const char* message, uintptr_t flags) { - dyld_all_image_infos.errorMessage = message; - dyld_all_image_infos.terminationFlags = flags; + dyld::gProcessInfo->errorMessage = message; + dyld::gProcessInfo->terminationFlags = flags; } -extern void* __dso_handle; -#define STR(s) # s -#define XSTR(s) STR(s) +#if TARGET_IPHONE_SIMULATOR + namespace dyld { + struct dyld_all_image_infos* gProcessInfo = NULL; + } +#else -struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) - = { - 12, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL, - XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos, - 0, dyld_error_kind_none, NULL, NULL, NULL, 0 - }; + #if __arm__ + // work around for: gdb-1109: notifier in dyld does not work if it is in thumb + extern "C" void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]); + #else + static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]) + { + // do nothing + // gdb sets a break point here to catch notifications + //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::gProcessInfo->infoArrayCount; ++i) + // dyld::log("dyld: %d loading at %p %s\n", i, dyld::gProcessInfo->infoArray[i].imageLoadAddress, dyld::gProcessInfo->infoArray[i].imageFilePath); + } + #endif + + extern void* __dso_handle; + #define STR(s) # s + #define XSTR(s) STR(s) -struct dyld_shared_cache_ranges dyld_shared_cache_ranges; + struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) + = { + 14, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL, + XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos, + 0, dyld_error_kind_none, NULL, NULL, NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, 0 + }; + struct dyld_shared_cache_ranges dyld_shared_cache_ranges; + namespace dyld { + struct dyld_all_image_infos* gProcessInfo = &dyld_all_image_infos; + } +#endif diff --git a/src/dyld_sim.exp b/src/dyld_sim.exp new file mode 100644 index 0000000..ccc744a --- /dev/null +++ b/src/dyld_sim.exp @@ -0,0 +1,31 @@ +# +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +# + +# +# Be sure the following are not dead stripped +# + +# Used by various tools to see build number of dyld +_dyld_simVersionString +_dyld_simVersionNumber + diff --git a/src/dyld_stub_binder.s b/src/dyld_stub_binder.s index 0636dbd..9949a87 100644 --- a/src/dyld_stub_binder.s +++ b/src/dyld_stub_binder.s @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,7 +21,13 @@ * @APPLE_LICENSE_HEADER_END@ */ +#include +#include + +// simulator does not have full libdyld.dylib - just a small libdyld_sim.dylib +#if ! TARGET_IPHONE_SIMULATOR + #ifdef __i386__ @@ -54,7 +60,7 @@ dyld_stub_binder: 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 + add $LP_OLD_BP_SAVE,%ebp movl %ecx,ECX_SAVE(%esp) movl %edx,EDX_SAVE(%esp) .align 0,0x90 @@ -97,16 +103,24 @@ dyld_stub_binder_: #define R8_SAVE 32 #define R9_SAVE 40 #define RAX_SAVE 48 -#define XMMM0_SAVE 64 /* 16-byte align */ -#define XMMM1_SAVE 80 -#define XMMM2_SAVE 96 -#define XMMM3_SAVE 112 -#define XMMM4_SAVE 128 -#define XMMM5_SAVE 144 -#define XMMM6_SAVE 160 -#define XMMM7_SAVE 176 -#define STACK_SIZE 192 /* (XMMM7_SAVE+16) must be 16 byte aligned too */ - +#define XMM0_SAVE 64 /* 16-byte align */ +#define XMM1_SAVE 80 +#define XMM2_SAVE 96 +#define XMM3_SAVE 112 +#define XMM4_SAVE 128 +#define XMM5_SAVE 144 +#define XMM6_SAVE 160 +#define XMM7_SAVE 176 +#define YMM0_SAVE 64 +#define YMM1_SAVE 96 +#define YMM2_SAVE 128 +#define YMM3_SAVE 160 +#define YMM4_SAVE 192 +#define YMM5_SAVE 224 +#define YMM6_SAVE 256 +#define YMM7_SAVE 288 +#define STACK_SIZE 320 + /* * sp+4 lazy binding info offset @@ -126,28 +140,55 @@ dyld_stub_binder: movq %r9,R9_SAVE(%rsp) movq %rax,RAX_SAVE(%rsp) misaligned_stack_error_entering_dyld_stub_binder: - movdqa %xmm0,XMMM0_SAVE(%rsp) - movdqa %xmm1,XMMM1_SAVE(%rsp) - movdqa %xmm2,XMMM2_SAVE(%rsp) - movdqa %xmm3,XMMM3_SAVE(%rsp) - movdqa %xmm4,XMMM4_SAVE(%rsp) - movdqa %xmm5,XMMM5_SAVE(%rsp) - movdqa %xmm6,XMMM6_SAVE(%rsp) - movdqa %xmm7,XMMM7_SAVE(%rsp) + movq $(_COMM_PAGE_CPU_CAPABILITIES), %rax + movl (%rax), %eax + testl $kHasAVX1_0, %eax + jne L2 + movdqa %xmm0,XMM0_SAVE(%rsp) + movdqa %xmm1,XMM1_SAVE(%rsp) + movdqa %xmm2,XMM2_SAVE(%rsp) + movdqa %xmm3,XMM3_SAVE(%rsp) + movdqa %xmm4,XMM4_SAVE(%rsp) + movdqa %xmm5,XMM5_SAVE(%rsp) + movdqa %xmm6,XMM6_SAVE(%rsp) + movdqa %xmm7,XMM7_SAVE(%rsp) + jmp L3 +L2: vmovdqu %ymm0,YMM0_SAVE(%rsp) # stack is only 16-byte aligned, so must use unaligned stores for avx registers + vmovdqu %ymm1,YMM1_SAVE(%rsp) + vmovdqu %ymm2,YMM2_SAVE(%rsp) + vmovdqu %ymm3,YMM3_SAVE(%rsp) + vmovdqu %ymm4,YMM4_SAVE(%rsp) + vmovdqu %ymm5,YMM5_SAVE(%rsp) + vmovdqu %ymm6,YMM6_SAVE(%rsp) + vmovdqu %ymm7,YMM7_SAVE(%rsp) +L3: dyld_stub_binder_: movq MH_PARAM_BP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo) movq LP_PARAM_BP(%rbp),%rsi call __Z21_dyld_fast_stub_entryPvl movq %rax,%r11 # save target - movdqa XMMM0_SAVE(%rsp),%xmm0 # restore registers - movdqa XMMM1_SAVE(%rsp),%xmm1 - movdqa XMMM2_SAVE(%rsp),%xmm2 - movdqa XMMM3_SAVE(%rsp),%xmm3 - movdqa XMMM4_SAVE(%rsp),%xmm4 - movdqa XMMM5_SAVE(%rsp),%xmm5 - movdqa XMMM6_SAVE(%rsp),%xmm6 - movdqa XMMM7_SAVE(%rsp),%xmm7 - movq RDI_SAVE(%rsp),%rdi + movq $(_COMM_PAGE_CPU_CAPABILITIES), %rax + movl (%rax), %eax + testl $kHasAVX1_0, %eax + jne L4 + movdqa XMM0_SAVE(%rsp),%xmm0 + movdqa XMM1_SAVE(%rsp),%xmm1 + movdqa XMM2_SAVE(%rsp),%xmm2 + movdqa XMM3_SAVE(%rsp),%xmm3 + movdqa XMM4_SAVE(%rsp),%xmm4 + movdqa XMM5_SAVE(%rsp),%xmm5 + movdqa XMM6_SAVE(%rsp),%xmm6 + movdqa XMM7_SAVE(%rsp),%xmm7 + jmp L5 +L4: vmovdqu YMM0_SAVE(%rsp),%ymm0 + vmovdqu YMM1_SAVE(%rsp),%ymm1 + vmovdqu YMM2_SAVE(%rsp),%ymm2 + vmovdqu YMM3_SAVE(%rsp),%ymm3 + vmovdqu YMM4_SAVE(%rsp),%ymm4 + vmovdqu YMM5_SAVE(%rsp),%ymm5 + vmovdqu YMM6_SAVE(%rsp),%ymm6 + vmovdqu YMM7_SAVE(%rsp),%ymm7 +L5: movq RDI_SAVE(%rsp),%rdi movq RSI_SAVE(%rsp),%rsi movq RDX_SAVE(%rsp),%rdx movq RCX_SAVE(%rsp),%rcx @@ -189,3 +230,7 @@ dyld_stub_binder: #endif /* __arm__ */ + + +// simulator does not have full libdyld.dylib - just a small libdyld_sim.dylib +#endif // ! TARGET_IPHONE_SIMULATOR diff --git a/src/glue.c b/src/glue.c index 9ccc656..69b5f22 100644 --- a/src/glue.c +++ b/src/glue.c @@ -27,10 +27,23 @@ #include #include #include +#include #include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if TARGET_IPHONE_SIMULATOR + #include "dyldSyscallInterface.h" +#endif // from _simple.h in libc typedef struct _SIMPLE* _SIMPLE_STRING; @@ -167,8 +180,9 @@ void __cxa_atexit() // make our own custom ones. // long __stack_chk_guard = 0; -static __attribute__((constructor)) -void __guard_setup(int argc, const char* argv[], const char* envp[], const char* apple[]) + + +void __guard_setup(const char* apple[]) { for (const char** p = apple; *p != NULL; ++p) { if ( strncmp(*p, "stack_guard=", 12) == 0 ) { @@ -189,13 +203,15 @@ void __guard_setup(int argc, const char* argv[], const char* envp[], const char* return; } } - +#if !TARGET_IPHONE_SIMULATOR #if __LP64__ __stack_chk_guard = ((long)arc4random() << 32) | arc4random(); #else __stack_chk_guard = arc4random(); #endif +#endif } + extern void _ZN4dyld4haltEPKc(const char*); void __stack_chk_fail() { @@ -277,3 +293,175 @@ void* memset(void* b, int c, size_t len) return b; } + +// wrap calls to stat() with check for EAGAIN +int _ZN4dyld7my_statEPKcP4stat(const char* path, struct stat* buf) +{ + int result; + do { + result = stat(path, buf); + } while ((result == -1) && (errno == EAGAIN)); + + return result; +} + +// dyld should retry open() if it gets an EGAIN +int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other) +{ + int result; + do { + result = open(path, flag, other); + } while ((result == -1) && (errno == EAGAIN)); + + return result; +} + + +// +// The dyld in the iOS simulator cannot do syscalls, so it calls back to +// host dyld. +// + +#if TARGET_IPHONE_SIMULATOR +int myopen(const char* path, int oflag, int extra) __asm("_open"); +int myopen(const char* path, int oflag, int extra) { + return gSyscallHelpers->open(path, oflag, extra); +} + +int close(int fd) { + return gSyscallHelpers->close(fd); +} + +ssize_t pread(int fd, void* buf, size_t nbytes, off_t offset) { + return gSyscallHelpers->pread(fd, buf , nbytes, offset); +} + +ssize_t write(int fd, const void *buf, size_t nbytes) { + return gSyscallHelpers->write(fd, buf , nbytes); +} + +void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) { + return gSyscallHelpers->mmap(addr, len, prot, flags, fd, offset); +} + +int munmap(void* addr, size_t len) { + return gSyscallHelpers->munmap(addr, len); +} + +int madvise(void* addr, size_t len, int advice) { + return gSyscallHelpers->madvise(addr, len, advice); +} + +int stat(const char* path, struct stat* buf) { + return gSyscallHelpers->stat(path, buf); +} + +int myfcntl(int fd, int cmd, void* result) __asm("_fcntl"); +int myfcntl(int fd, int cmd, void* result) { + return gSyscallHelpers->fcntl(fd, cmd, result); +} + +int myioctl(int fd, unsigned long request, void* result) __asm("_ioctl"); +int myioctl(int fd, unsigned long request, void* result) { + return gSyscallHelpers->ioctl(fd, request, result); +} + +int issetugid() { + return gSyscallHelpers->issetugid(); +} + +char* getcwd(char* buf, size_t size) { + return gSyscallHelpers->getcwd(buf, size); +} + +char* realpath(const char* file_name, char* resolved_name) { + return gSyscallHelpers->realpath(file_name, resolved_name); +} + + + +kern_return_t vm_allocate(vm_map_t target_task, vm_address_t *address, + vm_size_t size, int flags) { + return gSyscallHelpers->vm_allocate(target_task, address, size, flags); +} + +kern_return_t vm_deallocate(vm_map_t target_task, vm_address_t address, + vm_size_t size) { + return gSyscallHelpers->vm_deallocate(target_task, address, size); +} + +kern_return_t vm_protect(vm_map_t target_task, vm_address_t address, + vm_size_t size, boolean_t max, vm_prot_t prot) { + return gSyscallHelpers->vm_protect(target_task, address, size, max, prot); +} + + +void _ZN4dyld3logEPKcz(const char* format, ...) { + va_list list; + va_start(list, format); + gSyscallHelpers->vlog(format, list); + va_end(list); +} + +void _ZN4dyld4warnEPKcz(const char* format, ...) { + va_list list; + va_start(list, format); + gSyscallHelpers->vwarn(format, list); + va_end(list); +} + + +int pthread_mutex_lock(pthread_mutex_t* m) { + return gSyscallHelpers->pthread_mutex_lock(m); +} + +int pthread_mutex_unlock(pthread_mutex_t* m) { + return gSyscallHelpers->pthread_mutex_unlock(m); +} + +mach_port_t mach_thread_self() { + return gSyscallHelpers->mach_thread_self(); +} + +kern_return_t mach_port_deallocate(ipc_space_t task, mach_port_name_t name) { + return gSyscallHelpers->mach_port_deallocate(task, name); +} + +mach_port_name_t task_self_trap() { + return gSyscallHelpers->task_self_trap(); +} + +kern_return_t mach_timebase_info(mach_timebase_info_t info) { + return gSyscallHelpers->mach_timebase_info(info); +} + +bool OSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) { + return gSyscallHelpers->OSAtomicCompareAndSwapPtrBarrier(old, new, value); +} + +void OSMemoryBarrier() { + return gSyscallHelpers->OSMemoryBarrier(); +} + +uint64_t mach_absolute_time(void) { + return gSyscallHelpers->mach_absolute_time(); +} + +int* __error(void) { + return gSyscallHelpers->errnoAddress(); +} + +void mach_init() { + mach_task_self_ = task_self_trap(); + //_task_reply_port = _mach_reply_port(); + +} + +mach_port_t mach_task_self_ = MACH_PORT_NULL; + +extern int myerrno_fallback __asm("_errno"); +int myerrno_fallback = 0; + +#endif // TARGET_IPHONE_SIMULATOR + + diff --git a/src/libdyld_sim.exp b/src/libdyld_sim.exp new file mode 100644 index 0000000..be8a4e4 --- /dev/null +++ b/src/libdyld_sim.exp @@ -0,0 +1,5 @@ +_dyld_get_sdk_version +_dyld_get_program_sdk_version +_dyld_get_min_os_version +_dyld_get_program_min_os_version + diff --git a/src/start_glue.h b/src/start_glue.h new file mode 100644 index 0000000..4e21bec --- /dev/null +++ b/src/start_glue.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __START_GLUE_H__ +#define __START_GLUE_H__ + +// Implemented in start_glue.s +extern "C" void start(); + + +// need 'start' to be one atom, but entry is in interior + +#if __x86_64__ || __i386__ + #define address_of_start (void*)((uintptr_t)&start + 1) +#elif __arm__ + #define address_of_start (void*)((uintptr_t)&start + 2) +#endif + + + +#endif // __START_GLUE_H__ diff --git a/src/start_glue.s b/src/start_glue.s index 4ccfd39..b35eb50 100644 --- a/src/start_glue.s +++ b/src/start_glue.s @@ -28,13 +28,13 @@ .align 2 .globl _start .private_extern _start -start: - nop # backtraces of LC_MAIN binaries don't end in "start" _start: + nop # backtraces of LC_MAIN binaries don't end in "start" +Lstart: movl %eax,(%esp) # pass result from main() to exit() call _exit hlt - + #endif /* __i386__ */ @@ -43,9 +43,9 @@ _start: .align 2 .globl _start .private_extern _start -start: - nop # backtraces of LC_MAIN binaries don't end in "start" _start: + nop # backtraces of LC_MAIN binaries don't end in "start" +Lstart: movl %eax,%edi # pass result from main() to exit() call _exit hlt @@ -56,13 +56,17 @@ _start: #if __arm__ .align 2 + .code 16 .globl _start .private_extern _start -start: - nop // backtraces of LC_MAIN binaries don't end in "start" + .thumb_func _start _start: + nop // backtraces of LC_MAIN binaries don't end in "start" +Lstart: bl _exit // result in r0 already in param reg r0 trap #endif /* __arm__ */ + .subsections_via_symbols + diff --git a/src/threadLocalHelpers.s b/src/threadLocalHelpers.s index b5bc9d5..7830d8d 100644 --- a/src/threadLocalHelpers.s +++ b/src/threadLocalHelpers.s @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,10 +21,16 @@ * @APPLE_LICENSE_HEADER_END@ */ - - +#include + +// bool save_xxm = (*((uint32_t*)_COMM_PAGE_CPU_CAPABILITIES) & kHasAVX1_0) != 0; + #if __x86_64__ // returns address of TLV in %rax, all other registers preserved + #define FP_SAVE -192 + #define VECTOR_SAVE -704 + #define STACK_SIZE 704 + .globl _tlv_get_addr .private_extern _tlv_get_addr _tlv_get_addr: @@ -37,7 +43,7 @@ _tlv_get_addr: LlazyAllocate: pushq %rbp movq %rsp, %rbp - subq $592,%rsp + subq $STACK_SIZE,%rsp // fxsave uses 512 bytes of store, xsave uses movq %rdi,-8(%rbp) movq %rsi,-16(%rbp) movq %rdx,-24(%rbp) @@ -46,11 +52,87 @@ LlazyAllocate: movq %r9,-48(%rbp) movq %r10,-56(%rbp) movq %r11,-64(%rbp) - fxsave -592(%rbp) + fnsave FP_SAVE(%rbp) + movq $(_COMM_PAGE_CPU_CAPABILITIES), %rcx + movl (%rcx), %ecx + testl $kHasAVX1_0, %ecx + jne L2 + movdqa %xmm0, VECTOR_SAVE+0x00(%rbp) + movdqa %xmm1, VECTOR_SAVE+0x10(%rbp) + movdqa %xmm2, VECTOR_SAVE+0x20(%rbp) + movdqa %xmm3, VECTOR_SAVE+0x30(%rbp) + movdqa %xmm4, VECTOR_SAVE+0x40(%rbp) + movdqa %xmm5, VECTOR_SAVE+0x50(%rbp) + movdqa %xmm6, VECTOR_SAVE+0x60(%rbp) + movdqa %xmm7, VECTOR_SAVE+0x70(%rbp) + movdqa %xmm8, VECTOR_SAVE+0x80(%rbp) + movdqa %xmm9, VECTOR_SAVE+0x90(%rbp) + movdqa %xmm10,VECTOR_SAVE+0xA0(%rbp) + movdqa %xmm11,VECTOR_SAVE+0xB0(%rbp) + movdqa %xmm12,VECTOR_SAVE+0xC0(%rbp) + movdqa %xmm13,VECTOR_SAVE+0xD0(%rbp) + movdqa %xmm14,VECTOR_SAVE+0xE0(%rbp) + movdqa %xmm15,VECTOR_SAVE+0xF0(%rbp) + jmp L3 +L2: vmovdqu %ymm0, VECTOR_SAVE+0x00(%rbp) + vmovdqu %ymm1, VECTOR_SAVE+0x20(%rbp) + vmovdqu %ymm2, VECTOR_SAVE+0x40(%rbp) + vmovdqu %ymm3, VECTOR_SAVE+0x60(%rbp) + vmovdqu %ymm4, VECTOR_SAVE+0x80(%rbp) + vmovdqu %ymm5, VECTOR_SAVE+0xA0(%rbp) + vmovdqu %ymm6, VECTOR_SAVE+0xC0(%rbp) + vmovdqu %ymm7, VECTOR_SAVE+0xE0(%rbp) + vmovdqu %ymm8, VECTOR_SAVE+0x100(%rbp) + vmovdqu %ymm9, VECTOR_SAVE+0x120(%rbp) + vmovdqu %ymm10,VECTOR_SAVE+0x140(%rbp) + vmovdqu %ymm11,VECTOR_SAVE+0x160(%rbp) + vmovdqu %ymm12,VECTOR_SAVE+0x180(%rbp) + vmovdqu %ymm13,VECTOR_SAVE+0x1A0(%rbp) + vmovdqu %ymm14,VECTOR_SAVE+0x1C0(%rbp) + vmovdqu %ymm15,VECTOR_SAVE+0x1E0(%rbp) +L3: movq -32(%rbp),%rcx movq 8(%rdi),%rdi // get key from descriptor - call _tlv_allocate_and_initialize_for_key - fxrstor -592(%rbp) - movq -64(%rbp),%r11 + call _tlv_allocate_and_initialize_for_key + + frstor FP_SAVE(%rbp) + movq $(_COMM_PAGE_CPU_CAPABILITIES), %rcx + movl (%rcx), %ecx + testl $kHasAVX1_0, %ecx + jne L4 + movdqa VECTOR_SAVE+0x00(%rbp), %xmm0 + movdqa VECTOR_SAVE+0x10(%rbp), %xmm1 + movdqa VECTOR_SAVE+0x20(%rbp), %xmm2 + movdqa VECTOR_SAVE+0x30(%rbp), %xmm3 + movdqa VECTOR_SAVE+0x40(%rbp), %xmm4 + movdqa VECTOR_SAVE+0x50(%rbp), %xmm5 + movdqa VECTOR_SAVE+0x60(%rbp), %xmm6 + movdqa VECTOR_SAVE+0x70(%rbp), %xmm7 + movdqa VECTOR_SAVE+0x80(%rbp), %xmm8 + movdqa VECTOR_SAVE+0x90(%rbp), %xmm9 + movdqa VECTOR_SAVE+0xA0(%rbp), %xmm10 + movdqa VECTOR_SAVE+0xB0(%rbp), %xmm11 + movdqa VECTOR_SAVE+0xC0(%rbp), %xmm12 + movdqa VECTOR_SAVE+0xD0(%rbp), %xmm13 + movdqa VECTOR_SAVE+0xE0(%rbp), %xmm14 + movdqa VECTOR_SAVE+0xF0(%rbp), %xmm15 + jmp L5 +L4: vmovdqu VECTOR_SAVE+0x00(%rbp), %ymm0 + vmovdqu VECTOR_SAVE+0x20(%rbp), %ymm1 + vmovdqu VECTOR_SAVE+0x40(%rbp), %ymm2 + vmovdqu VECTOR_SAVE+0x60(%rbp), %ymm3 + vmovdqu VECTOR_SAVE+0x80(%rbp), %ymm4 + vmovdqu VECTOR_SAVE+0xA0(%rbp), %ymm5 + vmovdqu VECTOR_SAVE+0xC0(%rbp), %ymm6 + vmovdqu VECTOR_SAVE+0xE0(%rbp), %ymm7 + vmovdqu VECTOR_SAVE+0x100(%rbp), %ymm8 + vmovdqu VECTOR_SAVE+0x120(%rbp), %ymm9 + vmovdqu VECTOR_SAVE+0x140(%rbp), %ymm10 + vmovdqu VECTOR_SAVE+0x160(%rbp), %ymm11 + vmovdqu VECTOR_SAVE+0x180(%rbp), %ymm12 + vmovdqu VECTOR_SAVE+0x1A0(%rbp), %ymm13 + vmovdqu VECTOR_SAVE+0x1C0(%rbp), %ymm14 + vmovdqu VECTOR_SAVE+0x1E0(%rbp), %ymm15 +L5: movq -64(%rbp),%r11 movq -56(%rbp),%r10 movq -48(%rbp),%r9 movq -40(%rbp),%r8 @@ -59,7 +141,7 @@ LlazyAllocate: movq -16(%rbp),%rsi movq -8(%rbp),%rdi addq 16(%rdi),%rax // result = buffer + offset - addq $592,%rsp + addq $STACK_SIZE,%rsp popq %rbp ret #endif @@ -123,4 +205,6 @@ L2: ldr r1, [r7, #8] // get offset from descriptor #endif #endif + .subsections_via_symbols + diff --git a/src/threadLocalVariables.c b/src/threadLocalVariables.c index 2586455..b5fcd6d 100644 --- a/src/threadLocalVariables.c +++ b/src/threadLocalVariables.c @@ -73,7 +73,12 @@ #define MH_HAS_TLV_DESCRIPTORS 0x800000 #endif -#if __i386__ || __x86_64__ + +typedef void (*TermFunc)(void*); + + + +#if __has_feature(tls) typedef struct TLVHandler { struct TLVHandler *next; @@ -165,9 +170,12 @@ __attribute__((visibility("hidden"))) void* tlv_allocate_and_initialize_for_key(pthread_key_t key) { const struct mach_header* mh = tlv_get_image_for_key(key); + if ( mh == NULL ) + return NULL; // if data structures are screwed up, don't crash + // first pass, find size and template uint8_t* start = NULL; - unsigned long size; + unsigned long size = 0; intptr_t slide = 0; bool slideComputed = false; bool hasInitializers = false; @@ -360,7 +368,6 @@ void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) // destructor key to come before the deallocation key. // -typedef void (*TermFunc)(void*); struct TLVTerminatorListEntry { TermFunc termFunc; @@ -411,13 +418,14 @@ void _tlv_atexit(TermFunc func, void* objAddr) } } -// called by pthreads when the current thread is going way and +// called by pthreads when the current thread is going away and // _tlv_atexit() has been called on the thread. static void tlv_finalize(void* storage) { struct TLVTerminatorList* list = (struct TLVTerminatorList*)storage; - for(uint32_t i=0; i < list->useCount; ++i) { - struct TLVTerminatorListEntry* entry = &list->entries[i]; + // destroy in reverse order of construction + for(uint32_t i=list->useCount; i > 0 ; --i) { + struct TLVTerminatorListEntry* entry = &list->entries[i-1]; if ( entry->termFunc != NULL ) { (*entry->termFunc)(entry->objAddr); } @@ -425,6 +433,16 @@ static void tlv_finalize(void* storage) free(storage); } +// +// called by exit() before it calls cxa_finalize() so that thread_local +// objects are destroyed before global objects. +void _tlv_exit() +{ + void* termFuncs = pthread_getspecific(tlv_terminators_key); + if ( termFuncs != NULL ) + tlv_finalize(termFuncs); +} + __attribute__((visibility("hidden"))) void tlv_initializer() @@ -446,9 +464,9 @@ void _tlv_bootstrap() } -// __i386__ || __x86_64__ + #else -// !(__i386__ || __x86_64__) + void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler) @@ -459,13 +477,21 @@ void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler) { } +void _tlv_exit() +{ +} + +void _tlv_atexit(TermFunc func, void* objAddr) +{ +} + __attribute__((visibility("hidden"))) void tlv_initializer() { } -// !(__i386__ || __x86_64__) -#endif + +#endif // __has_feature(tls) diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index ba256c8..36b4ed9 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -23,12 +23,19 @@ endif IOSROOT = ifeq "$(OS_NAME)" "iPhoneOS" - IOSROOT = /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.Internal.sdk - CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/cc -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) - CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/c++ -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) + #IOSROOT = $(shell xcodebuild -version -sdk iphoneos.internal Path) + IOSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.Internal.sdk + CC = $(shell xcrun -sdk iphoneos.internal -find cc) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) + CXX = $(shell xcrun -sdk iphoneos.internal -find c++) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) + LIPO = $(shell xcrun -sdk iphoneos.internal -find lipo) + STRIP = $(shell xcrun -sdk iphoneos.internal -find strip) + INSTALL_NAME_TOOL = $(shell xcrun -sdk iphoneos.internal -find install_name_tool) else - CC = cc -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) - CXX = c++ -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) + CC = $(shell xcrun -find cc) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) + CXX = $(shell xcrun -find c++) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) + LIPO = $(shell xcrun -find lipo) + STRIP = $(shell xcrun -find strip) + INSTALL_NAME_TOOL = $(shell xcrun -find install_name_tool) endif CCFLAGS = -Wall -std=c99 @@ -66,3 +73,4 @@ ifeq ($(ARCH),thumb2) else FILEARCH = $(ARCH) endif + diff --git a/unit-tests/run-all-unit-tests b/unit-tests/run-all-unit-tests index ceb2106..d1df892 100755 --- a/unit-tests/run-all-unit-tests +++ b/unit-tests/run-all-unit-tests @@ -8,7 +8,7 @@ CRSTATE=`defaults read com.apple.CrashReporter DialogType` defaults write com.apple.CrashReporter DialogType basic # run test targeting different OS versions -for OSVERSION in 10.8 10.7 10.6 10.5 10.4 +for OSVERSION in 10.9 10.8 10.7 10.6 10.5 10.4 do echo "" echo " * * * Running all unit tests i386 built for $OSVERSION * * *" diff --git a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile index 5c0374b..72ca37b 100644 --- a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile +++ b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile @@ -41,16 +41,17 @@ check-ios: check-macosx: ./main 10 - export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11" && ./main 11 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11" && ./main 11 "alt11/Foo.framework/Versions/A/Foo" export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9" && ./main 10 export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9:${PWD}/alt11" && ./main 11 export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11:${PWD}/alt12" && ./main 12 all: - mkdir -p Foo.framework alt11/Foo.framework alt9/Foo.framework alt12/Foo.framework + mkdir -p Foo.framework alt11/Foo.framework/Versions/A alt9/Foo.framework alt12/Foo.framework ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -install_name "${PWD}/Foo.framework/Foo" -o Foo.framework/Foo ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c Foo.framework/Foo - ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/Foo.framework/Foo" -o alt11/Foo.framework/Foo + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/Foo.framework/Foo" -o alt11/Foo.framework/Versions/A/Foo + cd alt11/Foo.framework && ln -sf Versions/A/Foo ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/Foo.framework/Foo" -o alt9/Foo.framework/Foo ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/Foo.framework/Foo" -o alt12/Foo.framework/Foo diff --git a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c index 2815ba1..a43882f 100644 --- a/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c +++ b/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c @@ -25,6 +25,7 @@ #include #include #include // for atoi() +#include #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() @@ -35,6 +36,21 @@ extern int foo(); int main(int argc, const char* argv[]) { #if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( argc > 2 ) { + bool found = false; + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, argv[2]) != NULL ) + found = true; + //fprintf(stderr, "image[%d]=%s\n", i, name); + } + if ( !found ) { + FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic dylib has wrong path"); + return EXIT_SUCCESS; + } + } + int expectedResult = atoi(argv[1]); int actualResult = foo(); //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); diff --git a/unit-tests/test-cases/absolute-symbol/Makefile b/unit-tests/test-cases/absolute-symbol/Makefile new file mode 100644 index 0000000..1c06dda --- /dev/null +++ b/unit-tests/test-cases/absolute-symbol/Makefile @@ -0,0 +1,21 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: + + ${CC} ${CCFLAGS} foo.c abs.s -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/absolute-symbol/abs.s b/unit-tests/test-cases/absolute-symbol/abs.s new file mode 100644 index 0000000..2352520 --- /dev/null +++ b/unit-tests/test-cases/absolute-symbol/abs.s @@ -0,0 +1,12 @@ + + .global _myAbs1 +_myAbs1 = 0 + + + + .global _myAbs2 +_myAbs2 = 1 + + + +_myLocalAbs = 3 diff --git a/unit-tests/test-cases/absolute-symbol/foo.c b/unit-tests/test-cases/absolute-symbol/foo.c new file mode 100644 index 0000000..8d231e0 --- /dev/null +++ b/unit-tests/test-cases/absolute-symbol/foo.c @@ -0,0 +1,5 @@ + + +int var = 5; +void func() { } + diff --git a/unit-tests/test-cases/absolute-symbol/main.c b/unit-tests/test-cases/absolute-symbol/main.c new file mode 100644 index 0000000..e6b61f3 --- /dev/null +++ b/unit-tests/test-cases/absolute-symbol/main.c @@ -0,0 +1,31 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int var; +extern void func(); +extern int myAbs1 __attribute__((weak_import)); +extern int myAbs2; + + +int main() +{ + + if (&myAbs1 != 0 ) { + FAIL("absolute-symbol: &myAbs1 != 0"); + return 0; + } + + + if ((uintptr_t)&myAbs2 != 1 ) { + FAIL("absolute-symbol: &myAbs2 != 1"); + return 0; + } + + + PASS("absolute-symbol"); + return 0; +} diff --git a/unit-tests/test-cases/big-stack/main.c b/unit-tests/test-cases/big-stack/main.c index 003881f..2c83e2d 100644 --- a/unit-tests/test-cases/big-stack/main.c +++ b/unit-tests/test-cases/big-stack/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-2012 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -48,32 +48,12 @@ void foo(unsigned long long stackSize, char* stackStart) 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(STACK_SIZE, &start); + foo(STACK_SIZE, &start); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/bundle-memory-load-fat/Makefile b/unit-tests/test-cases/bundle-memory-load-fat/Makefile index e87cda5..00641d0 100644 --- a/unit-tests/test-cases/bundle-memory-load-fat/Makefile +++ b/unit-tests/test-cases/bundle-memory-load-fat/Makefile @@ -23,7 +23,7 @@ TESTROOT = ../.. include ${TESTROOT}/include/common.makefile -FATFLAGS = $(shell lipo -detailed_info $(IOSROOT)/usr/lib/libSystem.B.dylib | grep architecture | sed -e 's/architecture/-arch/') +FATFLAGS = $(shell ${LIPO} -detailed_info $(IOSROOT)/usr/lib/libSystem.B.dylib | grep architecture | sed -e 's/architecture/-arch/') all-check: all check diff --git a/unit-tests/test-cases/bundle-terminator/Makefile b/unit-tests/test-cases/bundle-terminator/Makefile new file mode 100644 index 0000000..8e11cff --- /dev/null +++ b/unit-tests/test-cases/bundle-terminator/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CXX} ${CXXFLAGS} -bundle -o test.bundle bundle.cxx + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/unit-tests/test-cases/bundle-terminator/bundle.cxx b/unit-tests/test-cases/bundle-terminator/bundle.cxx new file mode 100644 index 0000000..b94ced2 --- /dev/null +++ b/unit-tests/test-cases/bundle-terminator/bundle.cxx @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +class Foo { +public: + Foo() { buffer = malloc(100); } + ~Foo() { free(buffer); } + void* get() { return buffer; } +private: + void* buffer; + +}; + + +Foo myObject; + diff --git a/unit-tests/test-cases/bundle-terminator/main.c b/unit-tests/test-cases/bundle-terminator/main.c new file mode 100644 index 0000000..d7fd707 --- /dev/null +++ b/unit-tests/test-cases/bundle-terminator/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + +#endif + + PASS("bundle-terminator"); + 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 index bc160be..73b71ac 100644 --- a/unit-tests/test-cases/crt-apple/Makefile +++ b/unit-tests/test-cases/crt-apple/Makefile @@ -45,7 +45,7 @@ main: main.c ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -w main.stripped: main - strip main -o main.stripped + ${STRIP} main -o main.stripped main-setuid: main cp main main-setuid diff --git a/unit-tests/test-cases/crt-libSystem/Makefile b/unit-tests/test-cases/crt-libSystem/Makefile index aab6ce6..367730a 100644 --- a/unit-tests/test-cases/crt-libSystem/Makefile +++ b/unit-tests/test-cases/crt-libSystem/Makefile @@ -42,7 +42,7 @@ main: main.c ${CC} ${CCFLAGS} -I${TESTROOT}/include $(CRTLIB) -o main main.c main.stripped: main - strip main -o main.stripped + ${STRIP} main -o main.stripped clean: ${RM} ${RMFLAGS} *~ main main.stripped diff --git a/unit-tests/test-cases/dladdr-stripped/Makefile b/unit-tests/test-cases/dladdr-stripped/Makefile index a3ac2a4..2e537bd 100644 --- a/unit-tests/test-cases/dladdr-stripped/Makefile +++ b/unit-tests/test-cases/dladdr-stripped/Makefile @@ -30,7 +30,7 @@ check: all: ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c - strip main + ${STRIP} main clean: diff --git a/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile new file mode 100644 index 0000000..ac4fb27 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c new file mode 100644 index 0000000..d0fd23c --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int bar() +{ + return mydata; +} diff --git a/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c new file mode 100644 index 0000000..a3a1b44 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int baz() +{ + return mydata; +} diff --git a/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c new file mode 100644 index 0000000..899bb44 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int foo() +{ + return mydata; +} diff --git a/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c new file mode 100644 index 0000000..9c39733 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + Dl_info info; + + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load bar + void* handleBar = dlopen("libbar.dylib", RTLD_LAZY); + if ( handleBar == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load baz + void* handleBaz = dlopen("libbaz.dylib", RTLD_LAZY); + if ( handleBaz == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libbaz.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + + void* sym_foo = dlsym(handleFoo, "foo"); + if ( sym_foo == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleFoo, \"foo\") failed"); + exit(0); + } + + void* sym_bar = dlsym(handleBar, "bar"); + if ( sym_bar == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleBar, \"bar\") failed"); + exit(0); + } + + void* sym_baz = dlsym(handleBaz, "baz"); + if ( sym_baz == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleBaz, \"baz\") failed"); + exit(0); + } + + // since foo loaded first, bar and baz should have a dynamic reference to foo because all + // the weak mydata symbols were coaleseced to the one in foo. + + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libbar and libbaz + // have dynamic references to libfoo. + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + + if ( dlclose(handleBar) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + // sym_bar should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_bar, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_bar) != 0, but should have failed"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because of external libbaz + // has a dynamic references to libfoo. + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + + if ( dlclose(handleBaz) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_baz should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_baz, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_baz) != 0, but should have failed"); + exit(0); + } + + + // sym_foo should finally be inaccessible via dladdr() because all dynamic references to libfoo are gone + if ( dladdr(sym_foo, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_foo) == 0, but should have succeeded"); + exit(0); + } + + + PASS("dlclose-dylib-dynamic-ref"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile b/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile new file mode 100644 index 0000000..63e2f78 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbase.dylib + diff --git a/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c b/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c new file mode 100644 index 0000000..8312b89 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c @@ -0,0 +1,6 @@ + + +int bar() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlclose-dylib-ref-count/base.c b/unit-tests/test-cases/dlclose-dylib-ref-count/base.c new file mode 100644 index 0000000..e7e0d08 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-ref-count/base.c @@ -0,0 +1 @@ +void base() { } diff --git a/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c b/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c new file mode 100644 index 0000000..8184cd1 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return 10; +} diff --git a/unit-tests/test-cases/dlclose-dylib-ref-count/main.c b/unit-tests/test-cases/dlclose-dylib-ref-count/main.c new file mode 100644 index 0000000..5902d82 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-ref-count/main.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + Dl_info info; + + // load bar + void* handleBar = dlopen("libbar.dylib", RTLD_LAZY); + if ( handleBar == NULL ) { + FAIL("dlclose-dylib-ref-count: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-ref-count: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* sym_base = dlsym(handleBar, "base"); + if ( sym_base == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + void* sym_foo = dlsym(handleFoo, "foo"); + if ( sym_foo == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + void* sym_bar = dlsym(handleBar, "bar"); + if ( sym_bar == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + if ( dlclose(handleBar) != 0 ) { + FAIL("dlclose-dylib-ref-count: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_base should still be accessible via dladdr() because of external reference from libfoo.dylib + if ( dladdr(sym_base, &info) == 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libfoo was dlopen'ed + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_foo) == 0, but should have succeeded"); + exit(0); + } + + // sym_bar should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_bar, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_bar) != 0, but should have failed"); + exit(0); + } + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-ref-count: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_base should no longer be accessible via dladdr() because libfoo and libbar both closed + if ( dladdr(sym_base, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(base) != 0, but should have failed"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libfoo was dlclose'ed + if ( dladdr(sym_foo, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_foo) != 0, but should have failed"); + exit(0); + } + + PASS("dlclose-dylib-ref-count"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlclose-dylib-terminators/Makefile b/unit-tests/test-cases/dlclose-dylib-terminators/Makefile new file mode 100644 index 0000000..e6d0b4c --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-terminators/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# dylibs need to be unloaded in reverse order of creation + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CXX} ${CXXFLAGS} -dynamiclib bar.cpp libbaz.dylib -o libbar.dylib + ${CXX} ${CXXFLAGS} -dynamiclib foo.cpp libbaz.dylib libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp b/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp new file mode 100644 index 0000000..2815167 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp @@ -0,0 +1,17 @@ +extern "C" int bazData; + +class BazUser { +public: + BazUser() { } + ~BazUser() { bazData = 0; } +}; + + +BazUser b; + + +int bar() +{ + return bazData; +} + diff --git a/unit-tests/test-cases/dlclose-dylib-terminators/baz.c b/unit-tests/test-cases/dlclose-dylib-terminators/baz.c new file mode 100644 index 0000000..b5a740e --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-terminators/baz.c @@ -0,0 +1,7 @@ +int bazData = 5; + + +int baz() +{ + return bazData; +} diff --git a/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp b/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp new file mode 100644 index 0000000..ead0e83 --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp @@ -0,0 +1,23 @@ + +extern void bar(); + + + +class A { +public: + A() { bar(); } + ~A() { bar(); } +}; + + +// Create global object which will have its destructor run when +// this dylib is unloaded. The destructor will call into libbar, +// so libbar.dylib can't be unloaded before this dylib. +A a; + + + + +void foo() +{ +} diff --git a/unit-tests/test-cases/dlclose-dylib-terminators/main.c b/unit-tests/test-cases/dlclose-dylib-terminators/main.c new file mode 100644 index 0000000..34d2d0d --- /dev/null +++ b/unit-tests/test-cases/dlclose-dylib-terminators/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-order: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-order: dlclose(handleFoo) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + PASS("dlclose-dylib-order"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlclose-unload-c++/main.c b/unit-tests/test-cases/dlclose-unload-c++/main.c index cada146..8300a41 100644 --- a/unit-tests/test-cases/dlclose-unload-c++/main.c +++ b/unit-tests/test-cases/dlclose-unload-c++/main.c @@ -92,7 +92,7 @@ int main() 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/dlopen-codesign-dynamic/Makefile b/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile new file mode 100644 index 0000000..aabd547 --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify uncodesigned dylibs gracefully fail to load +# + + +all-check: all check + +check: + ./main + ./main-enforce + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -DENFORCE -o main-enforce + codesign -s - main-enforce + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ main main-enforce libfoo.dylib diff --git a/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c b/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/dlopen-codesign-dynamic/main.c b/unit-tests/test-cases/dlopen-codesign-dynamic/main.c new file mode 100644 index 0000000..37dba84 --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign-dynamic/main.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test.h" + + +int main() +{ +#if ENFORCE + uint32_t flags = CS_ENFORCEMENT | CS_KILL; + if ( csops(0, CS_OPS_SET_STATUS, &flags, sizeof(flags)) != 0 ) { + FAIL("dlopen-codesign-dynamic: csops() failed"); + return EXIT_SUCCESS; + } + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib should have failed"); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "signature") == NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib failed, but message was wrong: %s", msg); + return EXIT_SUCCESS; + } + +#else + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib failed"); + return EXIT_SUCCESS; + } + +#endif + + PASS("dlopen-codesign-dynamic"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-codesign/Makefile b/unit-tests/test-cases/dlopen-codesign/Makefile new file mode 100644 index 0000000..9060b25 --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify uncodesigned dylibs gracefully fail to load +# + + +all-check: all check + +check: + ./main + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + codesign -s - -o enforcement,hard main + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib diff --git a/unit-tests/test-cases/dlopen-codesign/foo.c b/unit-tests/test-cases/dlopen-codesign/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/dlopen-codesign/main.c b/unit-tests/test-cases/dlopen-codesign/main.c new file mode 100644 index 0000000..53fce5f --- /dev/null +++ b/unit-tests/test-cases/dlopen-codesign/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-codesign: load of libfoo.dylib should have failed"); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "signature") == NULL ) { + FAIL("dlopen-codesign: load of libfoo.dylib failed, but message was wrong: %s", msg); + return EXIT_SUCCESS; + } + + PASS("dlopen-codesign"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-leak-threaded/Makefile b/unit-tests/test-cases/dlopen-leak-threaded/Makefile index 1afad69..83d46ff 100644 --- a/unit-tests/test-cases/dlopen-leak-threaded/Makefile +++ b/unit-tests/test-cases/dlopen-leak-threaded/Makefile @@ -42,8 +42,8 @@ all-check: all check check: ${CHECK} check-real: - ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "DYLD_LIBRARY_PATH=hide && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" - ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + DYLD_LIBRARY_PATH=hide && ./main + ./main check-xfail: echo "XFAIL dlopen-leak"; diff --git a/unit-tests/test-cases/dlopen-leak-threaded/main.c b/unit-tests/test-cases/dlopen-leak-threaded/main.c index c7fc150..ed9840e 100644 --- a/unit-tests/test-cases/dlopen-leak-threaded/main.c +++ b/unit-tests/test-cases/dlopen-leak-threaded/main.c @@ -75,8 +75,12 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u\n", getpid()); - system(cmd); + sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + int status = system(cmd); + if ( status == EXIT_SUCCESS ) + PASS("dlopen-leak-threaded"); + else + FAIL("dlopen-leak-threaded"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dlopen-leak/Makefile b/unit-tests/test-cases/dlopen-leak/Makefile index b3a47a3..a9f14c8 100644 --- a/unit-tests/test-cases/dlopen-leak/Makefile +++ b/unit-tests/test-cases/dlopen-leak/Makefile @@ -42,8 +42,8 @@ all-check: all check check: ${CHECK} check-real: - ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "DYLD_LIBRARY_PATH=hide && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" - ${TESTROOT}/bin/exit-zero-pass.pl "dlopen-leak" "dlopen-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + DYLD_LIBRARY_PATH=hide && ./main + ./main check-xfail: echo "XFAIL dlopen-leak"; diff --git a/unit-tests/test-cases/dlopen-leak/main.c b/unit-tests/test-cases/dlopen-leak/main.c index 511a140..d84b8e2 100644 --- a/unit-tests/test-cases/dlopen-leak/main.c +++ b/unit-tests/test-cases/dlopen-leak/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -39,10 +39,15 @@ int main() dlopen("libnotthere.dylib", RTLD_LAZY); } + // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u\n", getpid()); - system(cmd); - + sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen-leak"); + else + FAIL("dlopen-leak"); + return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dlopen-search-leak/Makefile b/unit-tests/test-cases/dlopen-search-leak/Makefile index 2a361da..e62fe0d 100644 --- a/unit-tests/test-cases/dlopen-search-leak/Makefile +++ b/unit-tests/test-cases/dlopen-search-leak/Makefile @@ -42,7 +42,7 @@ all-check: all check check: ${CHECK} check-real: - DYLD_LIBRARY_PATH=hide:other:places && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null && echo "PASS dlopen-search-leak" + DYLD_LIBRARY_PATH=hide:other:places && ./main check-xfail: echo "XFAIL dlopen-leak"; diff --git a/unit-tests/test-cases/dlopen-search-leak/main.c b/unit-tests/test-cases/dlopen-search-leak/main.c index 7a4f090..a953bff 100644 --- a/unit-tests/test-cases/dlopen-search-leak/main.c +++ b/unit-tests/test-cases/dlopen-search-leak/main.c @@ -40,8 +40,12 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u\n", getpid()); - system(cmd); + sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen-search-leak"); + else + FAIL("dlopen-search-leak"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile index 9562ac5..f5e572f 100644 --- a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile +++ b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile @@ -45,7 +45,7 @@ all-check: all check check: ${CHECK} check-real: - ${TESTROOT}/bin/exit-zero-pass.pl "dlopen_preflight-leak-image-deny-single" "dlopen_preflight-leak-image-deny-single" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + ./main check-xfail: echo "XFAIL dlopen-leak"; diff --git a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c index a7ffa59..4d90de4 100644 --- a/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c +++ b/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c @@ -54,8 +54,12 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u\n", getpid()); - system(cmd); + sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen_preflight-leak-image-deny-single"); + else + FAIL("dlopen_preflight-leak-image-deny-single"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/dlopen_preflight-leak/Makefile b/unit-tests/test-cases/dlopen_preflight-leak/Makefile index eff03d8..1752c26 100644 --- a/unit-tests/test-cases/dlopen_preflight-leak/Makefile +++ b/unit-tests/test-cases/dlopen_preflight-leak/Makefile @@ -42,8 +42,8 @@ all-check: all check check: ${CHECK} check-real: - ${TESTROOT}/bin/exit-zero-pass.pl "dlopen_preflight-leak" "dlopen_preflight-leak" "DYLD_LIBRARY_PATH=hide && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" - ${TESTROOT}/bin/exit-zero-pass.pl "dlopen_preflight-leak" "dlopen_preflight-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null" + DYLD_LIBRARY_PATH=hide && ./main + ./main check-xfail: echo "XFAIL dlopen-leak"; diff --git a/unit-tests/test-cases/dlopen_preflight-leak/main.c b/unit-tests/test-cases/dlopen_preflight-leak/main.c index 612e141..3f04f2e 100644 --- a/unit-tests/test-cases/dlopen_preflight-leak/main.c +++ b/unit-tests/test-cases/dlopen_preflight-leak/main.c @@ -38,8 +38,12 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u\n", getpid()); - system(cmd); + sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen_preflight-leak"); + else + FAIL("dlopen_preflight-leak"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..569d814 --- /dev/null +++ b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main 10 + export DYLD_LIBRARY_PATH=${PWD}/hide && ./main 10 + +all: + mkdir -p Foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -o "${PWD}/Foo.framework/Foo" + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=0 -o "${PWD}/hide/Foo" + ${CC} ${CCFLAGS} main.c -framework Foo -F. -I../../include -o main + + +clean: + ${RM} -rf Foo.framework hide main \ No newline at end of file diff --git a/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..7e6502e --- /dev/null +++ b/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("framework-DYLD_LIBRARY_PATH using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("framework-DYLD_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} + diff --git a/unit-tests/test-cases/interpose-multiple/Makefile b/unit-tests/test-cases/interpose-multiple/Makefile new file mode 100644 index 0000000..99041ca --- /dev/null +++ b/unit-tests/test-cases/interpose-multiple/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +# + +# +# This unit test verifies that multiple interposing libraries can all +# interpose the same function and the result is that they chain together. +# That is, each one calls through to the next. +# +# On Tiger (10.4.0), this test fails with infinite recursion. +# +# The function foo() does string appends. This allows us to check: +# 1) every interposer was called, and 2) they were called in the +# correct order. +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libfoo1.dylib:libfoo2.dylib" && ./main + export DYLD_INSERT_LIBRARIES="libfoo2.dylib:libfoo1.dylib" && ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbase.dylib -o main + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo1.c libbase.dylib -o libfoo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo2.c libbase.dylib -o libfoo2.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbase.dylib libfoo1.dylib libfoo2.dylib + diff --git a/unit-tests/test-cases/interpose-multiple/base.c b/unit-tests/test-cases/interpose-multiple/base.c new file mode 100644 index 0000000..126f99e --- /dev/null +++ b/unit-tests/test-cases/interpose-multiple/base.c @@ -0,0 +1,6 @@ +#include +#include "base.h" + +int base1() { return 1; } +int base2() { return 2; } + diff --git a/unit-tests/test-cases/interpose-multiple/base.h b/unit-tests/test-cases/interpose-multiple/base.h new file mode 100644 index 0000000..658b8a1 --- /dev/null +++ b/unit-tests/test-cases/interpose-multiple/base.h @@ -0,0 +1,3 @@ + +extern int base1(); +extern int base2(); diff --git a/unit-tests/test-cases/interpose-multiple/foo1.c b/unit-tests/test-cases/interpose-multiple/foo1.c new file mode 100644 index 0000000..0670559 --- /dev/null +++ b/unit-tests/test-cases/interpose-multiple/foo1.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int (*p2)() = &base2; + +__attribute__((constructor)) +void myinit() +{ + if ( (*p2)() == 20 ) + PASS("interpose-multiple"); + else + FAIL("interpose-multiple"); +} + + +int mybase1() +{ + return 10; +} + + +DYLD_INTERPOSE(mybase1, base1) diff --git a/unit-tests/test-cases/interpose-multiple/foo2.c b/unit-tests/test-cases/interpose-multiple/foo2.c new file mode 100644 index 0000000..1f2f362 --- /dev/null +++ b/unit-tests/test-cases/interpose-multiple/foo2.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int (*p1)() = &base1; + +__attribute__((constructor)) +void myinit() +{ + if ( (*p1)() == 10 ) + PASS("interpose-multiple"); + else + FAIL("interpose-multiple"); +} + + +int mybase2() +{ + return 20; +} + + +DYLD_INTERPOSE(mybase2, base2) diff --git a/unit-tests/test-cases/interpose-multiple/main.c b/unit-tests/test-cases/interpose-multiple/main.c new file mode 100644 index 0000000..e0bd1d1 --- /dev/null +++ b/unit-tests/test-cases/interpose-multiple/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() +#include "base.h" + +int main() +{ + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/loader_path-dup/Makefile b/unit-tests/test-cases/loader_path-dup/Makefile index 61d36e4..981e577 100644 --- a/unit-tests/test-cases/loader_path-dup/Makefile +++ b/unit-tests/test-cases/loader_path-dup/Makefile @@ -62,8 +62,8 @@ bar/libbase.dylib : base.c main : main.c foo/libfoo.dylib bar/libbar.dylib ${CC} -I${TESTROOT}/include main.c -o main foo/libfoo.dylib bar/libbar.dylib # this breaks partial makes, but ld can't see @loader_path or it freaks - install_name_tool -change foo/libbase.dylib '@loader_path/libbase.dylib' foo/libfoo.dylib - install_name_tool -change bar/libbase.dylib '@loader_path/libbase.dylib' bar/libbar.dylib + ${INSTALL_NAME_TOOL} -change foo/libbase.dylib '@loader_path/libbase.dylib' foo/libfoo.dylib + ${INSTALL_NAME_TOOL} -change bar/libbase.dylib '@loader_path/libbase.dylib' bar/libbar.dylib clean: diff --git a/unit-tests/test-cases/loader_path/Makefile b/unit-tests/test-cases/loader_path/Makefile index 7e646d1..06eeb2b 100644 --- a/unit-tests/test-cases/loader_path/Makefile +++ b/unit-tests/test-cases/loader_path/Makefile @@ -47,8 +47,8 @@ libfoo2.dylib : foo.c main : main.c libfoo2.dylib hide/libbar.dylib hide/libfoo3.dylib ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main hide/libbar.dylib libfoo2.dylib - install_name_tool -change libfoo2.dylib '@loader_path/libfoo2.dylib' main - install_name_tool -change libbar.dylib '@loader_path/hide/libbar.dylib' main + ${INSTALL_NAME_TOOL} -change libfoo2.dylib '@loader_path/libfoo2.dylib' main + ${INSTALL_NAME_TOOL} -change libbar.dylib '@loader_path/hide/libbar.dylib' main clean: ${RM} ${RMFLAGS} *~ main libfoo.dylib hide libfoo2.dylib diff --git a/unit-tests/test-cases/restrict-environ/Makefile b/unit-tests/test-cases/restrict-environ/Makefile index a22a543..0d2aa80 100644 --- a/unit-tests/test-cases/restrict-environ/Makefile +++ b/unit-tests/test-cases/restrict-environ/Makefile @@ -1,5 +1,5 @@ ## -# Copyright (c) 2009 Apple Inc. All rights reserved. +# Copyright (c) 2009-2013 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # @@ -27,8 +27,10 @@ SHELL = bash # use bash shell so we can redirect just stderr ifeq "$(OS_NAME)" "iPhoneOS" RUN_AS_USER = login -f -l mobile + RUN_AS_ROOT = else RUN_AS_USER = + RUN_AS_ROOT = sudo endif PWD = `pwd` @@ -37,6 +39,7 @@ all-check: all check check: ${RUN_AS_USER} $(PWD)/main-with-env 2>/dev/null + ${RUN_AS_ROOT} $(PWD)/main-with-env 2>/dev/null all: main diff --git a/unit-tests/test-cases/restrict-environ/main.c b/unit-tests/test-cases/restrict-environ/main.c index c4c0da8..e68d038 100644 --- a/unit-tests/test-cases/restrict-environ/main.c +++ b/unit-tests/test-cases/restrict-environ/main.c @@ -23,6 +23,7 @@ #include // fprintf(), NULL #include // exit(), EXIT_SUCCESS #include // strcmp(), strncmp() +#include // dyld_process_is_restricted() #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() @@ -58,6 +59,13 @@ int main(int argc, const char* argv[], const char* envp[], const char* apple[]) return EXIT_SUCCESS; } + // verify SPI says process is restricted + if ( !dyld_process_is_restricted() ) { + FAIL("restrict-environ: dyld_process_is_restrictet() returns false"); + return EXIT_SUCCESS; + } + + PASS("restrict-environ"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/rpath-dlopen-leak/Makefile b/unit-tests/test-cases/rpath-dlopen-leak/Makefile index 0bea13a..9d7195a 100644 --- a/unit-tests/test-cases/rpath-dlopen-leak/Makefile +++ b/unit-tests/test-cases/rpath-dlopen-leak/Makefile @@ -42,8 +42,8 @@ all-check: all check check: if [ ${EMULATED} == 0 ]; \ then \ - ${TESTROOT}/bin/exit-zero-pass.pl "rpath-dlopen-leak" "rpath-dlopen-leak" "./main | grep '0 leaks for 0 total leaked bytes' > /dev/null"; \ - ${TESTROOT}/bin/exit-zero-pass.pl "rpath-dlopen-leak" "rpath-dlopen-leak" "./main.bad | grep '0 leaks for 0 total leaked bytes' > /dev/null"; \ + ./main ; \ + ./main.bad ; \ else \ echo "XFAIL rpath-dlopen-leak"; \ fi; diff --git a/unit-tests/test-cases/rpath-dlopen-leak/main.c b/unit-tests/test-cases/rpath-dlopen-leak/main.c index b33ceca..993ea25 100644 --- a/unit-tests/test-cases/rpath-dlopen-leak/main.c +++ b/unit-tests/test-cases/rpath-dlopen-leak/main.c @@ -40,8 +40,11 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u\n", getpid()); - system(cmd); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("rpath-dlopen-leak"); + else + FAIL("rpath-dlopen-leak"); return EXIT_SUCCESS; } diff --git a/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c b/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c index a85ee6a..6212331 100644 --- a/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c +++ b/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c @@ -4,6 +4,7 @@ int myfoo() { + foo(); return 20; } diff --git a/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile b/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile new file mode 100644 index 0000000..dc1021c --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Test that resolver function is not run before initializer +## even when lazy pointers are bound early. +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -I${TESTROOT}/include + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib libbar.dylib + diff --git a/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c b/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c new file mode 100644 index 0000000..870c431 --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool hasBeenInited = false; + +__attribute__((constructor)) +void myInit() +{ + hasBeenInited = true; +} + + +int barGood() +{ + return 1; +} + +int barBad() +{ + return 0; +} + + +// This bar is a "resolver" function that return the actual address of "bar" +void* bar() +{ + __asm__(".symbol_resolver _bar"); // magic until we have compiler support + // Resolver function run before initializers + if ( hasBeenInited ) + return &barGood; + else + return &barBad; +} + diff --git a/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c b/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c new file mode 100644 index 0000000..bb9054c --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + +extern int bar(); + +int foo() +{ + return bar(); +} + diff --git a/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c b/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c new file mode 100644 index 0000000..212d02f --- /dev/null +++ b/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*Func_t)(void); + +int main() +{ + // RTLD_NOW force lazy pointers to be bound early + void* handle = dlopen("libfoo.dylib", RTLD_NOW); + Func_t pFoo = (Func_t)dlsym(handle, "foo"); + int result = (*pFoo)(); + if ( result == 0 ) + FAIL("symbol-resolver-lazy-prebound: resolver ran before initializer"); + else + PASS("symbol-resolver-lazy-prebound"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/weak-coalesce-inserted/Makefile b/unit-tests/test-cases/weak-coalesce-inserted/Makefile new file mode 100644 index 0000000..2c3f872 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-inserted/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + DYLD_INSERT_LIBRARIES=libfoo1.dylib:libfoo2.dylib ./main + +all: main + +main: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbase.dylib base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo1.dylib foo1.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo2.dylib foo2.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbase.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libbase.dylib + diff --git a/unit-tests/test-cases/weak-coalesce-inserted/base.c b/unit-tests/test-cases/weak-coalesce-inserted/base.c new file mode 100644 index 0000000..a2d200b --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-inserted/base.c @@ -0,0 +1,83 @@ +/* + * 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 "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool wasProblem = false; + +static const char* coal1Where = NULL; +static int* coal1Addr = NULL; +static int checkInCountCoal1 = 0; + +void baseVerifyCoal1(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr); + ++checkInCountCoal1; + if ( coal1Where == NULL ) { + coal1Where = where; + coal1Addr = addr; + } + else { + if ( addr != coal1Addr ) { + fprintf(stderr, "coal1 resolved to different locations. %p %s and %p %s\n", + coal1Addr, coal1Where, addr, where); + wasProblem = true; + } + } +} + + +static const char* coal2Where = NULL; +static int* coal2Addr = NULL; +static int checkInCountCoal2 = 0; + +void baseVerifyCoal2(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr); + ++checkInCountCoal2; + if ( coal2Where == NULL ) { + coal2Where = where; + coal2Addr = addr; + } + else { + if ( addr != coal2Addr ) { + fprintf(stderr, "coal2 resolved to different locations. %p %s and %p %s\n", + coal2Addr, coal2Where, addr, where); + wasProblem = true; + } + } +} + + + +void baseCheck() +{ + if ( wasProblem || (checkInCountCoal1 != 3) || (checkInCountCoal2 != 2) ) + FAIL("weak-coal"); + else + PASS("weak-coal"); +} + diff --git a/unit-tests/test-cases/weak-coalesce-inserted/base.h b/unit-tests/test-cases/weak-coalesce-inserted/base.h new file mode 100644 index 0000000..482b832 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-inserted/base.h @@ -0,0 +1,31 @@ +/* + * 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 baseCheck(); + +extern int coal1; +extern int coal2; + +extern void baseVerifyCoal1(const char* where, int* addr); +extern void baseVerifyCoal2(const char* where, int* addr); diff --git a/unit-tests/test-cases/weak-coalesce-inserted/foo1.c b/unit-tests/test-cases/weak-coalesce-inserted/foo1.c new file mode 100644 index 0000000..0707e0d --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-inserted/foo1.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@ + */ +#include +#include "base.h" + + + +int __attribute__((weak)) coal1 = 1; +int __attribute__((weak)) coal2 = 1; + + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo1", &coal1); + baseVerifyCoal2("in foo1", &coal2); +} + + diff --git a/unit-tests/test-cases/weak-coalesce-inserted/foo2.c b/unit-tests/test-cases/weak-coalesce-inserted/foo2.c new file mode 100644 index 0000000..13e8b07 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-inserted/foo2.c @@ -0,0 +1,35 @@ +/* + * 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 "base.h" + + +int __attribute__((weak)) coal1 = 2; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo2", &coal1); + baseVerifyCoal2("in foo2", &coal2); +} diff --git a/unit-tests/test-cases/weak-coalesce-inserted/main.c b/unit-tests/test-cases/weak-coalesce-inserted/main.c new file mode 100644 index 0000000..bf38e29 --- /dev/null +++ b/unit-tests/test-cases/weak-coalesce-inserted/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int __attribute__((weak)) coal1 = 3; + + +int main() +{ + baseVerifyCoal1("in main", &coal1); + + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/weak-coalesce-stubs/Makefile b/unit-tests/test-cases/weak-coalesce-stubs/Makefile index 03cb107..d55a5c5 100644 --- a/unit-tests/test-cases/weak-coalesce-stubs/Makefile +++ b/unit-tests/test-cases/weak-coalesce-stubs/Makefile @@ -12,7 +12,7 @@ all: $(CC) $(CCFLAGS) -I${TESTROOT}/include main.c -o main $(CC) $(CCFLAGS) bar.c -dynamiclib -o libbar.dylib $(CC) $(CCFLAGS) foo.c -dynamiclib -o libfoo.dylib - strip -c -x libfoo.dylib -o libstub.dylib + $(STRIP) -c -x libfoo.dylib -o libstub.dylib clean: ${RM} ${RMFLAGS} libbar.dylib libfoo.dylib libstub.dylib main -- 2.45.2