From 19894a1236eae932b4028640f24ab843f691d4e4 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 24 Oct 2014 18:04:27 +0000 Subject: [PATCH] dyld-353.2.1.tar.gz --- base.xcconfig | 4 + doc/man/man1/dyld.1 | 17 +- dyld.xcconfig | 20 + dyld.xcodeproj/project.pbxproj | 207 +++--- dyld_sim-entitlements.plist | 8 + include/mach-o/dyld.h | 6 - include/mach-o/dyld_priv.h | 28 +- launch-cache/Architectures.hpp | 7 +- launch-cache/FileAbstraction.hpp | 2 +- launch-cache/MachOBinder.hpp | 27 +- launch-cache/MachOFileAbstraction.hpp | 12 + launch-cache/MachOLayout.hpp | 132 +++- launch-cache/MachORebaser.hpp | 42 +- launch-cache/MachOTrie.hpp | 14 +- launch-cache/ObjCModernAbstraction.hpp | 62 +- launch-cache/dsc_extractor.cpp | 157 +++-- launch-cache/dsc_iterator.cpp | 17 +- launch-cache/dsc_iterator.h | 7 +- launch-cache/dyld_shared_cache_util.cpp | 132 +++- launch-cache/update_dyld_shared_cache.cpp | 368 +++++++---- libdyld.xcconfig | 8 + src/ImageLoader.cpp | 113 +++- src/ImageLoader.h | 71 +- src/ImageLoaderMachO.cpp | 175 +++-- src/ImageLoaderMachO.h | 5 +- src/ImageLoaderMachOClassic.cpp | 137 ++-- src/ImageLoaderMachOClassic.h | 1 + src/ImageLoaderMachOCompressed.cpp | 184 +++--- src/ImageLoaderMachOCompressed.h | 13 +- src/dyld.cpp | 604 +++++++++++++----- src/dyld.h | 7 +- src/dyldAPIs.cpp | 92 +-- src/dyldAPIsInLibSystem.cpp | 174 +++-- src/dyldExceptions.c | 17 +- src/dyldInitialization.cpp | 2 +- src/dyldLibSystemGlue.c | 6 - src/dyldLibSystemInterface.h | 6 +- src/dyldNew.cpp | 6 +- src/dyldStartup.s | 148 ++--- src/dyldSyscallInterface.h | 7 + src/dyld_gdb.cpp | 9 +- src/dyld_stub_binder.s | 299 ++++++--- src/glue.c | 122 +++- src/start_glue.h | 2 + src/start_glue.s | 14 + src/stub_binding_helper.s | 16 +- src/threadLocalHelpers.s | 305 ++++++--- src/threadLocalVariables.c | 8 +- unit-tests/build-iPhoneOS-unit-tests | 4 +- unit-tests/include/common.makefile | 2 +- unit-tests/test-cases/dlclose-order/Makefile | 49 ++ unit-tests/test-cases/dlclose-order/bar.cxx | 12 + unit-tests/test-cases/dlclose-order/base.c | 8 + unit-tests/test-cases/dlclose-order/base.h | 9 + unit-tests/test-cases/dlclose-order/baz.cxx | 12 + .../test-cases/dlclose-order/foo.c | 12 +- unit-tests/test-cases/dlclose-order/main.c | 60 ++ .../test-cases/dlopen-leak-threaded/main.c | 2 +- unit-tests/test-cases/dlopen-leak/main.c | 2 +- .../test-cases/dlopen-search-leak/main.c | 2 +- .../main.c | 2 +- .../test-cases/dlopen_preflight-leak/main.c | 2 +- .../test-cases/image-remove-crash/Makefile | 17 + .../test-cases/image-remove-crash/foo.c | 3 + .../test-cases/image-remove-crash/main.c | 74 +++ .../image-state-deny-cache-leak/Makefile | 38 ++ .../image-state-deny-cache-leak/bar.c | 2 + .../image-state-deny-cache-leak/foo.c | 3 + .../image-state-deny-cache-leak/main.c | 77 +++ .../test-cases/image-state-deny-leak/Makefile | 39 ++ .../test-cases/image-state-deny-leak/bar.c | 2 + .../test-cases/image-state-deny-leak/foo.c | 3 + .../test-cases/image-state-deny-leak/main.c | 82 +++ .../interpose-basic-prebound/mystrdup.c | 2 +- .../test-cases/interpose-basic/mystrdup.c | 2 +- .../interpose-dynamic-basic/Makefile | 37 ++ .../test-cases/interpose-dynamic-basic/main.c | 71 ++ .../interpose-dynamic-dlsym/Makefile | 38 ++ .../test-cases/interpose-dynamic-dlsym/foo.c | 26 + .../test-cases/interpose-dynamic-dlsym/main.c | 53 ++ .../interpose-dynamic-lazy/Makefile | 38 ++ .../test-cases/interpose-dynamic-lazy/foo.c | 27 + .../test-cases/interpose-dynamic-lazy/main.c | 42 ++ .../rpath-no-trailing-slash/Makefile | 46 ++ .../test-cases/rpath-no-trailing-slash/foo.c | 3 + .../test-cases/rpath-no-trailing-slash/main.c | 14 + .../symbol-resolver-lazy-prebound/bar.o | Bin 0 -> 1364 bytes .../test-cases/text-perm-alt-segment/Makefile | 54 ++ .../test-cases/text-perm-alt-segment/foo.c | 36 ++ .../test-cases/text-perm-alt-segment/main.c | 36 ++ unit-tests/test-cases/tlv-basic/Makefile | 2 +- unit-tests/test-cases/tlv-basic/main.c | 54 +- unit-tests/test-cases/tlv-dylib/Makefile | 4 +- .../upward-dylib-init-order/Makefile | 7 +- .../upward-dylib-init-order/common.c | 14 +- .../upward-dylib-init-order/common.h | 1 + .../test-cases/upward-dylib-init-order/u2.c | 10 + 97 files changed, 3699 insertions(+), 1256 deletions(-) create mode 100644 base.xcconfig create mode 100644 dyld.xcconfig create mode 100644 dyld_sim-entitlements.plist create mode 100644 libdyld.xcconfig create mode 100644 unit-tests/test-cases/dlclose-order/Makefile create mode 100644 unit-tests/test-cases/dlclose-order/bar.cxx create mode 100644 unit-tests/test-cases/dlclose-order/base.c create mode 100644 unit-tests/test-cases/dlclose-order/base.h create mode 100644 unit-tests/test-cases/dlclose-order/baz.cxx rename src/dyld_debug.c => unit-tests/test-cases/dlclose-order/foo.c (85%) create mode 100644 unit-tests/test-cases/dlclose-order/main.c create mode 100644 unit-tests/test-cases/image-remove-crash/Makefile create mode 100644 unit-tests/test-cases/image-remove-crash/foo.c create mode 100644 unit-tests/test-cases/image-remove-crash/main.c create mode 100644 unit-tests/test-cases/image-state-deny-cache-leak/Makefile create mode 100644 unit-tests/test-cases/image-state-deny-cache-leak/bar.c create mode 100644 unit-tests/test-cases/image-state-deny-cache-leak/foo.c create mode 100644 unit-tests/test-cases/image-state-deny-cache-leak/main.c create mode 100644 unit-tests/test-cases/image-state-deny-leak/Makefile create mode 100644 unit-tests/test-cases/image-state-deny-leak/bar.c create mode 100644 unit-tests/test-cases/image-state-deny-leak/foo.c create mode 100644 unit-tests/test-cases/image-state-deny-leak/main.c create mode 100644 unit-tests/test-cases/interpose-dynamic-basic/Makefile create mode 100644 unit-tests/test-cases/interpose-dynamic-basic/main.c create mode 100644 unit-tests/test-cases/interpose-dynamic-dlsym/Makefile create mode 100644 unit-tests/test-cases/interpose-dynamic-dlsym/foo.c create mode 100644 unit-tests/test-cases/interpose-dynamic-dlsym/main.c create mode 100644 unit-tests/test-cases/interpose-dynamic-lazy/Makefile create mode 100644 unit-tests/test-cases/interpose-dynamic-lazy/foo.c create mode 100644 unit-tests/test-cases/interpose-dynamic-lazy/main.c create mode 100644 unit-tests/test-cases/rpath-no-trailing-slash/Makefile create mode 100644 unit-tests/test-cases/rpath-no-trailing-slash/foo.c create mode 100644 unit-tests/test-cases/rpath-no-trailing-slash/main.c create mode 100644 unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o create mode 100644 unit-tests/test-cases/text-perm-alt-segment/Makefile create mode 100644 unit-tests/test-cases/text-perm-alt-segment/foo.c create mode 100644 unit-tests/test-cases/text-perm-alt-segment/main.c create mode 100644 unit-tests/test-cases/upward-dylib-init-order/u2.c diff --git a/base.xcconfig b/base.xcconfig new file mode 100644 index 0000000..91782c7 --- /dev/null +++ b/base.xcconfig @@ -0,0 +1,4 @@ +#include "/AppleInternal/XcodeConfig/SimulatorSupport.xcconfig" + +// Set INSTALL_PATH[sdk=macosx*] when SimulatorSupport.xcconfig is unavailable +INSTALL_PATH[sdk=macosx*] = $(INSTALL_PATH_ACTUAL) diff --git a/doc/man/man1/dyld.1 b/doc/man/man1/dyld.1 index b89239d..50729a7 100644 --- a/doc/man/man1/dyld.1 +++ b/doc/man/man1/dyld.1 @@ -14,6 +14,8 @@ DYLD_FALLBACK_LIBRARY_PATH .br DYLD_VERSIONED_LIBRARY_PATH .br +DYLD_PRINT_TO_FILE +.br DYLD_ROOT_PATH .br DYLD_SHARED_REGION @@ -34,8 +36,6 @@ DYLD_PRINT_LIBRARIES_POST_LAUNCH .br DYLD_BIND_AT_LAUNCH .br -DYLD_NO_FIX_PREBINDING -.br DYLD_DISABLE_DOFS .br DYLD_PRINT_APIS @@ -146,6 +146,12 @@ Whichever has the larger current_version value will be used in the process whene a dylib with that install name is required. This is similar to DYLD_LIBRARY_PATH except instead of always overriding, it only overrides is the supplied library is newer. .TP +.B DYLD_PRINT_TO_FILE +This is a path to a (writable) file. Normally, the dynamic linker writes all +logging output (triggered by DYLD_PRINT_* settings) to file descriptor 2 +(which is usually stderr). But this setting causes the dynamic linker to +write logging output to the specified file. +.TP .B DYLD_ROOT_PATH This is a colon separated list of directories. The dynamic linker will prepend each of this directory paths to every image access until a file is found. @@ -211,13 +217,6 @@ lazily bound at the time of their first call. Right before the process's main() is called, dyld prints out information about how dyld spent its time. Useful for analyzing launch performance. .TP -.B DYLD_NO_FIX_PREBINDING -Normally, dyld will trigger the dyld shared cache to be regenerated if it notices -the cache is out of date while launching a process. If this environment variable -is set, dyld will not trigger a cache rebuild. This is useful to set while installing -a large set of OS dylibs, to ensure the cache is not regenerated until the install -is complete. -.TP .B DYLD_DISABLE_DOFS Causes dyld not register dtrace static probes with the kernel. .TP diff --git a/dyld.xcconfig b/dyld.xcconfig new file mode 100644 index 0000000..7c6bbfe --- /dev/null +++ b/dyld.xcconfig @@ -0,0 +1,20 @@ +ALIGNMENT[arch=armv7s] = -Wl,-segalign,0x4000 + +BASE_ADDRESS[arch=armv7*] = 0x1fe00000 +BASE_ADDRESS[arch=arm64] = 0x120000000 +BASE_ADDRESS[arch=i386] = 0x8fe00000 +BASE_ADDRESS[arch=x86_64] = 0x7fff5fc00000 + +ENTRY[sdk=*simulator*] = -Wl,-e,_start_sim +ENTRY[sdk=iphoneos*] = -Wl,-e,__dyld_start +ENTRY[sdk=macosx*] = -Wl,-e,__dyld_start + +EXPORTED_SYMBOLS_FILE[sdk=*simulator*] = $(SRCROOT)/src/dyld_sim.exp +EXPORTED_SYMBOLS_FILE[sdk=iphoneos*] = $(SRCROOT)/src/dyld.exp +EXPORTED_SYMBOLS_FILE[sdk=macosx*] = $(SRCROOT)/src/dyld.exp + +PRODUCT_NAME[sdk=*simulator*] = dyld_sim +PRODUCT_NAME[sdk=iphoneos*] = dyld +PRODUCT_NAME[sdk=macosx*] = dyld + +INSTALL_PATH_ACTUAL = /usr/lib diff --git a/dyld.xcodeproj/project.pbxproj b/dyld.xcodeproj/project.pbxproj index d8e3000..c0dde47 100644 --- a/dyld.xcodeproj/project.pbxproj +++ b/dyld.xcodeproj/project.pbxproj @@ -17,7 +17,6 @@ F908137111D3FB5000626CC1 /* usr|local|include|mach-o */, F908137211D3FB5000626CC1 /* usr|share|man|man1 */, F908137311D3FB5000626CC1 /* usr|share|man|man3 */, - F9F479FE152A63F2008F75C2 /* simulator clean up */, ); dependencies = ( F9B4D78012AD9736000605A6 /* PBXTargetDependency */, @@ -42,7 +41,6 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = F906E2230639E96400B13DB2 /* dyld_debug.c */; }; F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; }; F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; }; F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; }; @@ -206,7 +204,7 @@ F908135111D3ED9000626CC1 /* usr|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)//usr/include/mach-o"; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/include/mach-o"; dstSubfolderSpec = 0; files = ( F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */, @@ -218,7 +216,7 @@ F908137011D3FB5000626CC1 /* usr|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)//usr/include"; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/include"; dstSubfolderSpec = 0; files = ( F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */, @@ -229,7 +227,7 @@ F908137111D3FB5000626CC1 /* usr|local|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)//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 */, @@ -243,7 +241,7 @@ F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/share/man/man1; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man1"; dstSubfolderSpec = 0; files = ( F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */, @@ -254,7 +252,7 @@ F908137311D3FB5000626CC1 /* usr|share|man|man3 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /usr/share/man/man3; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man3"; dstSubfolderSpec = 0; files = ( F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */, @@ -271,7 +269,7 @@ F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_LOCATION)/usr/local/include/mach-o"; + dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/local/include/mach-o"; dstSubfolderSpec = 0; files = ( F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */, @@ -283,7 +281,7 @@ F9C69EFC14EC8AB8009CAE2E /* usr|local|include */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_PATH_PREFIX)//usr/local/include"; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/local/include"; dstSubfolderSpec = 0; files = ( F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */, @@ -294,7 +292,7 @@ F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = "$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1"; + dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1"; dstSubfolderSpec = 0; files = ( F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */, @@ -305,6 +303,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3FC074DF188330C8005F11DD /* base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; 834A90AB0E1D85D600555761 /* ObjCLegacyAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCLegacyAbstraction.hpp; sourceTree = ""; }; 834A90AC0E1D85D600555761 /* ObjCModernAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ObjCModernAbstraction.hpp; sourceTree = ""; }; EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; }; @@ -314,7 +313,6 @@ EF799FEE070D27BB00F78484 /* dlopen.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlopen.3; path = doc/man/man3/dlopen.3; sourceTree = SOURCE_ROOT; }; EF799FEF070D27BB00F78484 /* dlsym.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlsym.3; path = doc/man/man3/dlsym.3; sourceTree = SOURCE_ROOT; }; EF799FF0070D27BB00F78484 /* dyld.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dyld.3; path = doc/man/man3/dyld.3; sourceTree = SOURCE_ROOT; }; - 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 = ""; }; @@ -333,6 +331,8 @@ 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 = ""; }; + F97988FA187F706600EC2C8E /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = ""; }; + F97988FB187F707A00EC2C8E /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; 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 = ""; }; @@ -479,6 +479,9 @@ F9ED4C870630A72200DF4E74 = { isa = PBXGroup; children = ( + 3FC074DF188330C8005F11DD /* base.xcconfig */, + F97988FA187F706600EC2C8E /* dyld.xcconfig */, + F97988FB187F707A00EC2C8E /* libdyld.xcconfig */, F9ED4CBB0630A7AA00DF4E74 /* src */, F9ED4CC30630A7BE00DF4E74 /* doc */, F9ED4CBE0630A7B100DF4E74 /* include */, @@ -535,9 +538,7 @@ F9B01E3D0739ABDE00CF981B /* dyld.exp */, F976F548127B90F8004BA2A5 /* dyld.order */, F9AC7E930B7BB67700FEB38B /* version.c */, - F918691408B16D2500E0F9DB /* dyld-interposing.h */, F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */, - F906E2230639E96400B13DB2 /* dyld_debug.c */, F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */, F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */, ); @@ -547,6 +548,7 @@ F9ED4CBE0630A7B100DF4E74 /* include */ = { isa = PBXGroup; children = ( + F918691408B16D2500E0F9DB /* dyld-interposing.h */, F98D274C0AA79D7400416316 /* dyld_images.h */, F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */, F9ED4CE90630A80600DF4E74 /* dyld_priv.h */, @@ -567,16 +569,6 @@ }; /* End PBXGroup section */ -/* Begin PBXHeadersBuildPhase section */ - F9D1000E14D8D0BA00099D91 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - /* Begin PBXNativeTarget section */ F93937310A94FAF700070A07 /* update_dyld_shared_cache */ = { isa = PBXNativeTarget; @@ -620,7 +612,6 @@ isa = PBXNativeTarget; buildConfigurationList = F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */; buildPhases = ( - F9D1000E14D8D0BA00099D91 /* Headers */, F9D1000F14D8D0BA00099D91 /* Sources */, F9D1001014D8D0BA00099D91 /* Frameworks */, ); @@ -640,6 +631,7 @@ F9D050C811DD701A00FB0A29 /* configure archives */, F9ED4C950630A76000DF4E74 /* Sources */, F907E2490FA6469000BFEDBD /* install iPhone file */, + F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */, F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */, ); buildRules = ( @@ -660,6 +652,7 @@ buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */; buildPhases = ( F9ED4C9C0630A76B00DF4E74 /* Sources */, + F959621018849DF20003E4D4 /* add dyld symlink */, ); buildRules = ( F921D31E070376F1000D1056 /* PBXBuildRule */, @@ -753,68 +746,83 @@ ); 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"; + 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\n\n\techo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_BASE_ARM64/ { 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 ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tawk '/define SHARED_REGION_SIZE_ARM64/ { 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 0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM_SHARED_REGION_SIZE 0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_START 0x180000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\t\techo \"#define ARM64_SHARED_REGION_SIZE 0x20000000 \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\tfi\nfi\n\n"; showEnvVarsInLog = 0; }; - F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = { + F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( ); - name = "do not install duplicates"; + name = "simulator entitlement"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\nfi\n\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"; + shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphonesimulator\" ]\nthen\n /usr/bin/codesign --force --sign - --entitlements ${SRCROOT}/dyld_sim-entitlements.plist ${INSTALL_DIR}/dyld_sim\nfi\n"; showEnvVarsInLog = 0; }; - F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = { + F959621018849DF20003E4D4 /* add dyld symlink */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); inputPaths = ( ); - name = "suppress macosx dyld_shared_cache_util"; + name = "add dyld symlink"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\n\trm -rf ${DSTROOT}/usr/local\nfi\n"; + shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphonesimulator\" ]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n"; showEnvVarsInLog = 0; }; - F9D050C811DD701A00FB0A29 /* configure archives */ = { + F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 12; + buildActionMask = 8; files = ( ); inputPaths = ( ); - name = "configure archives"; + name = "do not install duplicates"; outputPaths = ( - "$(DERIVED_SOURCES_DIR)/archives.txt", ); - runOnlyForDeploymentPostprocessing = 0; + runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n\n"; + 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\t\n mkdir -p \"${DSTROOT}${INSTALL_PATH_PREFIX}/${INSTALL_LOCATION}/usr/lib\"\n mv \"${DSTROOT}${INSTALL_PATH_PREFIX}/${INSTALL_LOCATION}/usr/local/lib/dsc_extractor.bundle\" \"${DSTROOT}${INSTALL_PATH_PREFIX}/${INSTALL_LOCATION}/usr/lib/dsc_extractor.bundle\"\nfi"; showEnvVarsInLog = 0; }; - F9F479FE152A63F2008F75C2 /* simulator clean up */ = { + F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; + buildActionMask = 8; files = ( ); inputPaths = ( ); - name = "simulator clean up"; + name = "suppress macosx dyld_shared_cache_util"; outputPaths = ( ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\n\trm -rf ${DSTROOT}/usr/local\nfi\n"; + showEnvVarsInLog = 0; + }; + F9D050C811DD701A00FB0A29 /* configure archives */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + ); + name = "configure archives"; + outputPaths = ( + "$(DERIVED_SOURCES_DIR)/archives.txt", + ); 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"; + 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; }; /* End PBXShellScriptBuildPhase section */ @@ -874,7 +882,6 @@ F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */, F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */, F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */, - F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */, F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */, F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */, F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */, @@ -948,8 +955,6 @@ 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; @@ -960,8 +965,6 @@ 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; }; @@ -1010,7 +1013,7 @@ 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"; + INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/$(LOCAL)/bin"; LOCAL = "$(LOCAL_$(RC_TARGET_CONFIG))"; LOCAL_iPhone = local; OTHER_CPLUSPLUSFLAGS = ( @@ -1035,7 +1038,7 @@ GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; - INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/bin"; PRODUCT_NAME = dyld_shared_cache_util; }; name = Debug; @@ -1048,7 +1051,7 @@ 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"; + INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/bin"; PRODUCT_NAME = dyld_shared_cache_util; SKIP_INSTALL = NO; }; @@ -1065,7 +1068,7 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_INLINES_ARE_PRIVATE_EXTERN = YES; GCC_OPTIMIZATION_LEVEL = 0; - INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/lib"; MACH_O_TYPE = mh_bundle; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", @@ -1089,7 +1092,7 @@ DYLIB_CURRENT_VERSION = ""; EXECUTABLE_EXTENSION = bundle; GCC_INLINES_ARE_PRIVATE_EXTERN = YES; - INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/lib"; MACH_O_TYPE = mh_bundle; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", @@ -1106,30 +1109,14 @@ }; F9D8C7DE087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F97988FA187F706600EC2C8E /* dyld.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD_64_BIT)"; - BASE_ADDRESS_armv4t = 0x2fe00000; - BASE_ADDRESS_armv5 = 0x2fe00000; - BASE_ADDRESS_armv6 = 0x2fe00000; - 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; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; CLANG_CXX_LIBRARY = "libc++"; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; - 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; @@ -1145,7 +1132,6 @@ ./include, "./launch-cache", ); - INSTALL_PATH = /usr/lib; LD_GENERATE_MAP_FILE = YES; OTHER_CFLAGS = ""; OTHER_CPLUSPLUSFLAGS = ( @@ -1153,20 +1139,15 @@ "$(OTHER_CFLAGS)", ); OTHER_LDFLAGS = ( - "-seg1addr", - "$(BASE_ADDRESS_$(CURRENT_ARCH))", + "-Wl,-seg1addr,$(BASE_ADDRESS)", "@$(DERIVED_SOURCES_DIR)/archives.txt", "-nostdlib", "-Wl,-dylinker", "-Wl,-dylinker_install_name,/usr/lib/dyld", "-stdlib=libc++", - "$(ALIGNMENT_$(CURRENT_ARCH))", - "$(ENTRY_$(PLATFORM_NAME))", + "$(ALIGNMENT)", + "$(ENTRY)", ); - 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"; @@ -1179,29 +1160,12 @@ }; F9D8C7E0087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F97988FA187F706600EC2C8E /* dyld.xcconfig */; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; - BASE_ADDRESS_armv4t = 0x2fe00000; - BASE_ADDRESS_armv5 = 0x2fe00000; - BASE_ADDRESS_armv6 = 0x2fe00000; - BASE_ADDRESS_armv7 = 0x2fe00000; - BASE_ADDRESS_armv7f = 0x2fe00000; - BASE_ADDRESS_armv7k = 0x2fe00000; - BASE_ADDRESS_armv7s = 0x2fe00000; - BASE_ADDRESS_i386 = 0x8fe00000; - BASE_ADDRESS_ppc = 0x8fe00000; - BASE_ADDRESS_x86_64 = 0x7fff5fc00000; CLANG_CXX_LIBRARY = "libc++"; CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - 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; @@ -1214,7 +1178,6 @@ ./include, "./launch-cache", ); - INSTALL_PATH = /usr/lib; LD_GENERATE_MAP_FILE = YES; ORDER_FILE = "$(SRCROOT)/src/dyld.order"; "OTHER_CFLAGS[arch=armv6]" = "-mthumb"; @@ -1223,20 +1186,15 @@ "$(OTHER_CFLAGS)", ); OTHER_LDFLAGS = ( - "-seg1addr", - "$(BASE_ADDRESS_$(CURRENT_ARCH))", + "-Wl,-seg1addr,$(BASE_ADDRESS)", "@$(DERIVED_SOURCES_DIR)/archives.txt", "-nostdlib", "-Wl,-dylinker", "-Wl,-dylinker_install_name,/usr/lib/dyld", "-stdlib=libc++", - "$(ALIGNMENT_$(CURRENT_ARCH))", - "$(ENTRY_$(PLATFORM_NAME))", + "$(ALIGNMENT)", + "$(ENTRY)", ); - 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"; @@ -1250,34 +1208,27 @@ }; F9D8C7E2087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F97988FB187F707A00EC2C8E /* libdyld.xcconfig */; 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; GCC_OPTIMIZATION_LEVEL = 0; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; - 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"; + INSTALLHDRS_COPY_PHASE = YES; 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; + PRODUCT_NAME = dyld; + SKIP_INSTALL = NO; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -1288,18 +1239,14 @@ }; F9D8C7E4087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = F97988FB187F707A00EC2C8E /* libdyld.xcconfig */; buildSettings = { - ARCHS = ( - x86_64, - i386, - ); 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; @@ -1307,11 +1254,6 @@ GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; 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)", @@ -1321,14 +1263,11 @@ "$(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; + PRODUCT_NAME = dyld; SEPARATE_STRIP = YES; + SKIP_INSTALL = NO; STRIP_INSTALLED_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; WARNING_CFLAGS = ( @@ -1355,6 +1294,7 @@ }; F9D8C7EA087B087300E93EFB /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 3FC074DF188330C8005F11DD /* base.xcconfig */; buildSettings = { CLANG_CXX_LIBRARY = "compiler-default"; }; @@ -1362,6 +1302,7 @@ }; F9D8C7EC087B087300E93EFB /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 3FC074DF188330C8005F11DD /* base.xcconfig */; buildSettings = { CLANG_CXX_LIBRARY = "compiler-default"; }; @@ -1377,7 +1318,7 @@ GCC_MODEL_TUNING = G5; GCC_OPTIMIZATION_LEVEL = 0; GCC_SYMBOLS_PRIVATE_EXTERN = YES; - INSTALL_PATH = /usr/local/lib; + INSTALL_PATH_ACTUAL = /usr/local/lib; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", @@ -1404,7 +1345,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; INSTALLHDRS_COPY_PHASE = YES; - INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + INSTALL_PATH_ACTUAL = "$(INSTALL_LOCATION)/usr/local/lib"; OTHER_CPLUSPLUSFLAGS = ( "-stdlib=libc++", "$(OTHER_CFLAGS)", diff --git a/dyld_sim-entitlements.plist b/dyld_sim-entitlements.plist new file mode 100644 index 0000000..0509fb2 --- /dev/null +++ b/dyld_sim-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.private.dyld_sim + + + diff --git a/include/mach-o/dyld.h b/include/mach-o/dyld.h index 642ca42..ce41147 100644 --- a/include/mach-o/dyld.h +++ b/include/mach-o/dyld.h @@ -93,12 +93,6 @@ extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) __ -/* - * _dyld_moninit() is a private interface between dyld and libSystem. - */ -extern void _dyld_moninit(void (*monaddition)(char *lowpc, char *highpc)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); - - diff --git a/include/mach-o/dyld_priv.h b/include/mach-o/dyld_priv.h index 49d5775..24208db 100644 --- a/include/mach-o/dyld_priv.h +++ b/include/mach-o/dyld_priv.h @@ -40,12 +40,6 @@ extern "C" { // extern int _dyld_func_lookup(const char* dyld_func_name, void **address); - -// -// _dyld_moninit() is a private interface between libSystem.dylib and dyld -// -extern void _dyld_moninit(void (*monaddition)(char *lowpc, char *highpc)); - // // private interface between libSystem.dylib and dyld // @@ -185,6 +179,7 @@ extern const char* dyld_image_path_containing_address(const void* addr); #define DYLD_MACOSX_VERSION_10_7 0x000A0700 #define DYLD_MACOSX_VERSION_10_8 0x000A0800 #define DYLD_MACOSX_VERSION_10_9 0x000A0900 +#define DYLD_MACOSX_VERSION_10_10 0x000A0A00 #define DYLD_IOS_VERSION_2_0 0x00020000 #define DYLD_IOS_VERSION_2_1 0x00020100 @@ -201,6 +196,8 @@ extern const char* dyld_image_path_containing_address(const void* addr); #define DYLD_IOS_VERSION_6_0 0x00060000 #define DYLD_IOS_VERSION_6_1 0x00060100 #define DYLD_IOS_VERSION_7_0 0x00070000 +#define DYLD_IOS_VERSION_7_1 0x00070100 +#define DYLD_IOS_VERSION_8_0 0x00080000 // // This is finds the SDK version a binary was built against. @@ -240,15 +237,14 @@ extern uint32_t dyld_get_program_min_os_version(); -#if __IPHONE_OS_VERSION_MIN_REQUIRED // // Returns if any OS dylib has overridden its copy in the shared cache // // Exists in iPhoneOS 3.1 and later +// Exists in Mac OS X 10.10 and later extern bool dyld_shared_cache_some_image_overridden(); -#endif - + // // Returns if the process is setuid or is code signed with entitlements. @@ -264,6 +260,20 @@ extern bool dyld_process_is_restricted(); #define NSLINKMODULE_OPTION_CAN_UNLOAD 0x20 +// +// Update all bindings on specified image. +// Looks for uses of 'replacement' and changes it to 'replacee'. +// NOTE: this is less safe than using static interposing via DYLD_INSERT_LIBRARIES +// because the running program may have already copy the pointer values to other +// locations that dyld does not know about. +// +struct dyld_interpose_tuple { + const void* replacement; + const void* replacee; +}; +extern void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count); + + #if __cplusplus } diff --git a/launch-cache/Architectures.hpp b/launch-cache/Architectures.hpp index e26825d..fe3eea4 100644 --- a/launch-cache/Architectures.hpp +++ b/launch-cache/Architectures.hpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -48,6 +48,11 @@ struct arm }; +struct arm64 +{ + typedef Pointer64 P; + +}; diff --git a/launch-cache/FileAbstraction.hpp b/launch-cache/FileAbstraction.hpp index 8786e6a..1d73d74 100644 --- a/launch-cache/FileAbstraction.hpp +++ b/launch-cache/FileAbstraction.hpp @@ -127,7 +127,7 @@ public: typedef _E E; static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } - static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, (uint32_t)value); } // Round to a P-size boundary template diff --git a/launch-cache/MachOBinder.hpp b/launch-cache/MachOBinder.hpp index 6f37727..2b8a3ac 100644 --- a/launch-cache/MachOBinder.hpp +++ b/launch-cache/MachOBinder.hpp @@ -286,10 +286,12 @@ Binder::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress template <> uint8_t Binder::pointerRelocSize() { return 2; } template <> uint8_t Binder::pointerRelocSize() { return 3; } template <> uint8_t Binder::pointerRelocSize() { return 2; } +template <> uint8_t Binder::pointerRelocSize() { return 3; } template <> uint8_t Binder::pointerRelocType() { return GENERIC_RELOC_VANILLA; } template <> uint8_t Binder::pointerRelocType() { return X86_64_RELOC_UNSIGNED; } template <> uint8_t Binder::pointerRelocType() { return ARM_RELOC_VANILLA; } +template <> uint8_t Binder::pointerRelocType() { return ARM64_RELOC_UNSIGNED; } template @@ -1140,7 +1142,28 @@ typename A::P::uint_t Binder::findLazyPointerFor(const char* symbolName) } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } - + + if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking for re-export symbol\n", symbolName, this->getDylibID()); + for (typename std::vector::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) { + if ( strcmp(it->exportName, symbolName) != 0 ) + continue; + + 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; + return binder->findLazyPointerFor(it->importName); + } + + if ( log ) fprintf(stderr, "not found shared lazy pointer for %s in %s, checking re-export dylibs\n", symbolName, this->getDylibID()); + for (typename std::vector::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) { + if ( it->reExport ) { + pint_t result = it->binder->findLazyPointerFor(symbolName); + if ( result != 0 ) + return result; + } + } + if ( log ) fprintf(stderr, "NOT found shared lazy pointer for %s in %s\n", symbolName, this->getDylibID()); return 0; } @@ -1155,7 +1178,7 @@ void Binder::optimize() it->client->optimizeStub(it->symbolName, lpVMAddr); } else { - fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", it->symbolName, it->client->getDylibID()); + fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", it->symbolName, it->client->getDylibID()); } } diff --git a/launch-cache/MachOFileAbstraction.hpp b/launch-cache/MachOFileAbstraction.hpp index 6314caa..7a65370 100644 --- a/launch-cache/MachOFileAbstraction.hpp +++ b/launch-cache/MachOFileAbstraction.hpp @@ -61,6 +61,15 @@ struct uuid_command { #ifndef CPU_SUBTYPE_ARM_V7S #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) #endif +#ifndef CPU_SUBTYPE_ARM64_ALL + #define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#endif +#ifndef CPU_TYPE_ARM64 + #define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64)) +#endif + +#define ARM64_RELOC_UNSIGNED 0 // for pointers + #ifndef LC_LOAD_UPWARD_DYLIB #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ @@ -85,6 +94,9 @@ struct uuid_command { #define LC_DYLIB_CODE_SIGN_DRS 0x2B #endif +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif #include "FileAbstraction.hpp" diff --git a/launch-cache/MachOLayout.hpp b/launch-cache/MachOLayout.hpp index dbcbe76..4d90a1e 100644 --- a/launch-cache/MachOLayout.hpp +++ b/launch-cache/MachOLayout.hpp @@ -69,11 +69,11 @@ public: struct Segment { public: - Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, + Segment(uint64_t addr, uint64_t vmsize, uint64_t offset, uint64_t file_size, uint64_t align, uint32_t prot, const char* segName) : fOrigAddress(addr), fOrigSize(vmsize), fOrigFileOffset(offset), fOrigFileSize(file_size), fOrigPermissions(prot), - fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fPermissions(prot), - fNewAddress(0), fMappedAddress(NULL) { + fSize(vmsize), fFileOffset(offset), fFileSize(file_size), fAlignment(align), + fPermissions(prot), fNewAddress(0), fMappedAddress(NULL) { strlcpy(fOrigName, segName, 16); } @@ -85,6 +85,7 @@ public: bool readable() const { return fPermissions & VM_PROT_READ; } bool writable() const { return fPermissions & VM_PROT_WRITE; } bool executable() const { return fPermissions & VM_PROT_EXECUTE; } + uint64_t alignment() const { return fAlignment; } const char* name() const { return fOrigName; } uint64_t newAddress() const { return fNewAddress; } void* mappedAddress() const { return fMappedAddress; } @@ -105,6 +106,7 @@ public: uint64_t fSize; uint64_t fFileOffset; uint64_t fFileSize; + uint64_t fAlignment; uint32_t fPermissions; uint64_t fNewAddress; void* fMappedAddress; @@ -134,11 +136,13 @@ public: virtual bool hasMainExecutableLookupLinkage() const = 0; virtual bool isTwoLevelNamespace() const = 0; virtual bool hasDyldInfo() const = 0; + virtual bool hasMultipleReadWriteSegments() const = 0; virtual uint32_t getNameFileOffset() const = 0; virtual time_t getLastModTime() const = 0; virtual ino_t getInode() const = 0; virtual std::vector& getSegments() = 0; virtual const std::vector& getSegments() const = 0; + virtual const Segment* getSegment(const char* name) const = 0; virtual const std::vector& getLibraries() const = 0; virtual uint64_t getBaseAddress() const = 0; virtual uint64_t getVMSize() const = 0; @@ -180,11 +184,13 @@ public: virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; } virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); } virtual bool hasDyldInfo() const { return fHasDyldInfo; } + virtual bool hasMultipleReadWriteSegments() const { return fHasTooManyWritableSegments; } virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; } virtual time_t getLastModTime() const { return fMTime; } virtual ino_t getInode() const { return fInode; } virtual std::vector& getSegments() { return fSegments; } virtual const std::vector& getSegments() const { return fSegments; } + virtual const Segment* getSegment(const char* name) const; virtual const std::vector& getLibraries() const { return fLibraries; } virtual uint64_t getBaseAddress() const { return fLowSegment->address(); } virtual uint64_t getVMSize() const { return fVMSize; } @@ -203,6 +209,11 @@ private: typedef typename A::P::E E; typedef typename A::P::uint_t pint_t; + uint64_t segmentSize(const macho_segment_command* segCmd) const; + uint64_t segmentFileSize(const macho_segment_command* segCmd) const; + uint64_t segmentAlignment(const macho_segment_command* segCmd) const; + bool validReadWriteSeg(const Segment& seg) const; + static cpu_type_t arch(); const char* fPath; @@ -231,6 +242,7 @@ private: bool fMainExecutableLookupLinkage; bool fIsDylib; bool fHasDyldInfo; + bool fHasTooManyWritableSegments; mutable const uint8_t* fDyldInfoExports; uuid_t fUUID; }; @@ -282,14 +294,20 @@ const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const if ( layout->getArchPair().arch == ap.arch ) { switch ( ap.arch ) { case CPU_TYPE_ARM: - if ( layout->getArchPair().subtype == ap.subtype ) + case CPU_TYPE_X86_64: + if ( (layout->getArchPair().subtype & ~CPU_SUBTYPE_MASK) == (ap.subtype & ~CPU_SUBTYPE_MASK) ) return layout; break; - default: + default: return layout; } } } + // if requesting x86_64h and it did not exist, try x86_64 as a fallback + if ((ap.arch == CPU_TYPE_X86_64) && (ap.subtype == CPU_SUBTYPE_X86_64_H)) { + ap.subtype = CPU_SUBTYPE_X86_64_ALL; + return this->getSlice(ap); + } return NULL; } @@ -385,6 +403,9 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::set(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); break; + case CPU_TYPE_ARM64: + fLayouts.push_back(new MachOLayout(&p[fileOffset], fileOffset, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + break; default: throw "unknown slice in fat file"; } @@ -397,7 +418,7 @@ UniversalMachOLayout::UniversalMachOLayout(const char* path, const std::setmagic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { + if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) { if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } @@ -409,6 +430,10 @@ if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt3 if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); } + else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM64)) { + if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) ) + fLayouts.push_back(new MachOLayout(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid)); + } else { throw "unknown file format"; } @@ -425,11 +450,68 @@ if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt3 } +template +uint64_t MachOLayout::segmentSize(const macho_segment_command* segCmd) const +{ + // segments may have 16KB alignment padding at end, if so we can remove that in cache + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

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

)); + const macho_section

* const lastSection = §ionsStart[segCmd->nsects()-1]; + uint64_t endSectAddr = lastSection->addr() + lastSection->size(); + uint64_t endSectAddrPage = (endSectAddr + 4095) & (-4096); + if ( endSectAddrPage < (segCmd->vmaddr() + segCmd->vmsize()) ) { + uint64_t size = endSectAddrPage - segCmd->vmaddr(); + //if ( size != segCmd->vmsize() ) + // fprintf(stderr, "trim %s size=0x%08llX instead of 0x%08llX for %s\n", + // segCmd->segname(), size, segCmd->vmsize(), getFilePath()); + return size; + } + } + return segCmd->vmsize(); +} + +template +uint64_t MachOLayout::segmentAlignment(const macho_segment_command* segCmd) const +{ + int p2align = 12; + if ( segCmd->nsects() > 0 ) { + const macho_section

* const sectionsStart = (macho_section

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

)); + const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->align() > p2align ) + p2align = sect->align(); + } + } + return (1 << p2align); +} + +template +uint64_t MachOLayout::segmentFileSize(const macho_segment_command* segCmd) const +{ + // segments may have 16KB alignment padding at end, if so we can remove that in cache + if ( segCmd->nsects() > 0 ) { + uint64_t endOffset = segCmd->fileoff(); + const macho_section

* const sectionsStart = (macho_section

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

)); + const macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->offset() != 0 ) + endOffset = sect->offset() + sect->size(); + } + uint64_t size = (endOffset - segCmd->fileoff() + 4095) & (-4096); + //if ( size != segCmd->filesize() ) + // fprintf(stderr, "trim %s filesize=0x%08llX instead of 0x%08llX for %s\n", + // segCmd->segname(), size, segCmd->filesize(), getFilePath()); + return size; + } + return segCmd->filesize(); +} + + template MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid) : fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0), fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false), - fHasDyldInfo(false), fDyldInfoExports(NULL) + fHasDyldInfo(false), fHasTooManyWritableSegments(false), fDyldInfoExports(NULL) { fDylibID.name = NULL; fDylibID.currentVersion = 0; @@ -493,9 +575,9 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* break; case macho_segment_command

::CMD: { - macho_segment_command

* segCmd = (macho_segment_command

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

* segCmd = (macho_segment_command

*)cmd; + fSegments.push_back(Segment(segCmd->vmaddr(), segmentSize(segCmd), segCmd->fileoff(), + segmentFileSize(segCmd), segmentAlignment(segCmd), segCmd->initprot(), segCmd->segname())); } break; case LC_SYMTAB: @@ -543,6 +625,9 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* if ( (fLowWritableSegment == NULL) || (seg.address() < fLowWritableSegment->address()) ) fLowWritableSegment = &seg; fVMWritablSize += seg.size(); + if ( !validReadWriteSeg(seg) ) { + fHasTooManyWritableSegments = true; + } } else { if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) ) @@ -552,7 +637,7 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* } if ( (highSegment != NULL) && (fLowSegment != NULL) ) fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096); - + // scan undefines looking, for magic ordinals if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) { const macho_nlist

* symbolTable = (macho_nlist

*)((uint8_t*)machHeader + symbolTableCmd->symoff()); @@ -578,6 +663,19 @@ MachOLayout::MachOLayout(const void* machHeader, uint64_t offset, const char* template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_I386; } template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_X86_64; } template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_ARM; } +template <> cpu_type_t MachOLayout::arch() { return CPU_TYPE_ARM64; } + +template <> +bool MachOLayout::validReadWriteSeg(const Segment& seg) const +{ + return (strcmp(seg.name(), "__DATA") == 0) || (strcmp(seg.name(), "__OBJC") == 0); +} + +template +bool MachOLayout::validReadWriteSeg(const Segment& seg) const +{ + return (strcmp(seg.name(), "__DATA") == 0); +} template <> @@ -598,6 +696,18 @@ bool MachOLayout::isSplitSeg() const return false; } +template +const MachOLayoutAbstraction::Segment* MachOLayout::getSegment(const char* name) const +{ + for(std::vector::const_iterator it = fSegments.begin(); it != fSegments.end(); ++it) { + const Segment& seg = *it; + if ( strcmp(seg.name(), name) == 0 ) + return &seg; + } + return NULL; +} + + #endif // __MACHO_LAYOUT__ diff --git a/launch-cache/MachORebaser.hpp b/launch-cache/MachORebaser.hpp index 4493cc3..ac80cd0 100644 --- a/launch-cache/MachORebaser.hpp +++ b/launch-cache/MachORebaser.hpp @@ -93,6 +93,7 @@ private: void doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersInData); void adjustSegmentLoadCommand(macho_segment_command

* seg); pint_t getSlideForVMAddress(pint_t vmaddress); + pint_t maskedVMAddress(pint_t vmaddress); pint_t* mappedAddressForVMAddress(pint_t vmaddress); pint_t* mappedAddressForRelocAddress(pint_t r_address); void adjustRelocBaseAddresses(); @@ -172,6 +173,7 @@ Rebaser::Rebaser(const MachOLayoutAbstraction& layout) template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_I386; } template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_X86_64; } template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM; } +template <> cpu_type_t Rebaser::getArchitecture() const { return CPU_TYPE_ARM64; } template bool Rebaser::unequalSlides() const @@ -301,33 +303,46 @@ void Rebaser::adjustLoadCommands() } } +template <> +uint64_t Rebaser::maskedVMAddress(pint_t vmaddress) +{ + return (vmaddress & 0x0FFFFFFFFFFFFFFF); +} + +template +typename A::P::uint_t Rebaser::maskedVMAddress(pint_t vmaddress) +{ + return vmaddress; +} template typename A::P::uint_t Rebaser::getSlideForVMAddress(pint_t vmaddress) { + pint_t vmaddr = this->maskedVMAddress(vmaddress); const std::vector& segments = fLayout.getSegments(); for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { const MachOLayoutAbstraction::Segment& seg = *it; - if ( (seg.address() <= vmaddress) && (seg.size() != 0) && ((vmaddress < (seg.address()+seg.size())) || (seg.address() == vmaddress)) ) { + if ( (seg.address() <= vmaddr) && (seg.size() != 0) && ((vmaddr < (seg.address()+seg.size())) || (seg.address() == vmaddr)) ) { return seg.newAddress() - seg.address(); } } - throwf("vm address 0x%08llX not found", (uint64_t)vmaddress); + throwf("vm address 0x%08llX not found", (uint64_t)vmaddr); } template typename A::P::uint_t* Rebaser::mappedAddressForVMAddress(pint_t vmaddress) { + pint_t vmaddr = this->maskedVMAddress(vmaddress); const std::vector& segments = fLayout.getSegments(); for(std::vector::const_iterator it = segments.begin(); it != segments.end(); ++it) { const MachOLayoutAbstraction::Segment& seg = *it; - if ( (seg.address() <= vmaddress) && (vmaddress < (seg.address()+seg.size())) ) { - return (pint_t*)((vmaddress - seg.address()) + (uint8_t*)seg.mappedAddress()); + if ( (seg.address() <= vmaddr) && (vmaddr < (seg.address()+seg.size())) ) { + return (pint_t*)((vmaddr - seg.address()) + (uint8_t*)seg.mappedAddress()); } } - throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddress); + throwf("mappedAddressForVMAddress(0x%08llX) not found", (uint64_t)vmaddr); } template @@ -630,7 +645,17 @@ void Rebaser::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToData A::P::E::set32(*p, newInstruction); } break; - case 3: // used only for ppc, an instruction that sets the hi16 of a register + case 3: // used for arm64 ADRP + p = (uint32_t*)mappedAddressForVMAddress(address); + instruction = A::P::E::get32(*p); + if ( (instruction & 0x9F000000) == 0x90000000 ) { + // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9); + value64 += codeToDataDelta; + instruction = (instruction & 0x9F00001F) | ((value64 << 17) & 0x60000000) | ((value64 >> 9) & 0x00FFFFE0); + A::P::E::set32(*p, instruction); + } + break; default: throwf("invalid kind=%d in split seg info", kind); } @@ -710,8 +735,11 @@ void Rebaser::adjustCode() const MachOLayoutAbstraction::Segment& dataSeg = *it; if ( strcmp(dataSeg.name(), "__IMPORT") == 0 ) codeToImportDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); - else if ( dataSeg.writable() ) + else if ( dataSeg.writable() ) { + if ( (strcmp(dataSeg.name(), "__DATA") != 0) && (strcmp(dataSeg.name(), "__OBJC") != 0) ) + throwf("only one rw segment named '__DATA' can be used in dylibs placed in the dyld shared cache (%s)", fLayout.getFilePath()); codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address()); + } } // decompress and call doCodeUpdate() on each address for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { diff --git a/launch-cache/MachOTrie.hpp b/launch-cache/MachOTrie.hpp index 05a8e07..7720b11 100644 --- a/launch-cache/MachOTrie.hpp +++ b/launch-cache/MachOTrie.hpp @@ -25,6 +25,7 @@ #define __MACH_O_TRIE__ #include +#include #include "MachOFileAbstraction.hpp" @@ -245,12 +246,19 @@ inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { int bit = 0; do { if (p == end) +#if __EXCEPTIONS throw "malformed uleb128 extends beyond trie"; - +#else + return result; +#endif uint64_t slice = *p & 0x7f; if (bit >= 64 || slice << bit >> bit != slice) +#if __EXCEPTIONS throw "uleb128 too big for 64-bits"; +#else + return result; +#endif else { result |= (slice << bit); bit += 7; @@ -321,7 +329,11 @@ static inline void processExportNode(const uint8_t* const start, const uint8_t* std::vector& output) { if ( p >= end ) +#if __EXCEPTIONS throw "malformed trie, node past end"; +#else + return; +#endif const uint8_t terminalSize = read_uleb128(p, end); const uint8_t* children = p + terminalSize; if ( terminalSize != 0 ) { diff --git a/launch-cache/ObjCModernAbstraction.hpp b/launch-cache/ObjCModernAbstraction.hpp index 41419a8..e41749d 100644 --- a/launch-cache/ObjCModernAbstraction.hpp +++ b/launch-cache/ObjCModernAbstraction.hpp @@ -177,8 +177,6 @@ class objc_method_list_t { uint32_t count; objc_method_t first; - // use newMethodList instead - void* operator new (size_t) { return NULL; } void* operator new (size_t, void* buf) { return buf; } public: @@ -238,12 +236,46 @@ public: uint32_t newEntsize = sizeof(objc_method_t)) : entsize(newEntsize), count(newCount) { } + +private: + // use newMethodList instead + void* operator new (size_t); +}; + + +// Ivar offset variables are 64-bit on x86_64 and 32-bit everywhere else. + +template +class objc_ivar_offset_t { + typedef typename A::P::uint_t pint_t; + typename A::P::uint_t ptr; // uint32_t * + + uint32_t& offset(SharedCache *cache) const { return *(uint32_t *)cache->mappedAddressForVMAddress(A::P::getP(ptr)); } + +public: + bool hasOffset() const { return A::P::getP(ptr) != 0; } + pint_t getOffset(SharedCache *cache) const { return A::P::E::get32(offset(cache)); } + void setOffset(SharedCache *cache, pint_t newOffset) { A::P::E::set32(offset(cache), newOffset); } +}; + +template <> +class objc_ivar_offset_t { + typedef x86_64 A; + typedef typename A::P::uint_t pint_t; + typename A::P::uint_t ptr; // uint64_t * + + uint64_t& offset(SharedCache *cache) const { return *(uint64_t *)cache->mappedAddressForVMAddress(A::P::getP(ptr)); } + +public: + bool hasOffset() const { return A::P::getP(ptr) != 0; } + pint_t getOffset(SharedCache *cache) const { return A::P::E::get64(offset(cache)); } + void setOffset(SharedCache *cache, pint_t newOffset) { A::P::E::set64(offset(cache), newOffset); } }; template class objc_ivar_t { typedef typename A::P::uint_t pint_t; - typename A::P::uint_t offset; // A::P * + objc_ivar_offset_t offset; // uint32_t * (uint64_t * on x86_64) typename A::P::uint_t name; // const char * typename A::P::uint_t type; // const char * uint32_t alignment; @@ -252,9 +284,9 @@ class objc_ivar_t { public: const char * getName(SharedCache *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); } - bool hasOffset() const { return A::P::getP(offset) != 0; } - pint_t getOffset(SharedCache *cache) const { return A::P::getP(*(pint_t * const)cache->mappedAddressForVMAddress(A::P::getP(offset))); } - void setOffset(SharedCache *cache, pint_t newOffset) { A::P::setP(*(pint_t *)cache->mappedAddressForVMAddress(A::P::getP(offset)), newOffset); } + bool hasOffset() const { return offset.hasOffset(); } + pint_t getOffset(SharedCache *cache) const { return offset.getOffset(cache); } + void setOffset(SharedCache *cache, pint_t newOffset) { offset.setOffset(cache, newOffset); } uint32_t getAlignment() { @@ -270,8 +302,6 @@ class objc_ivar_list_t { uint32_t count; objc_ivar_t first; - // use newIvarList instead - void* operator new (size_t) { return NULL; } void* operator new (size_t, void* buf) { return buf; } public: @@ -310,7 +340,9 @@ public: uint32_t newEntsize = sizeof(objc_ivar_t)) : entsize(newEntsize), count(newCount) { } - +private: + // use newIvarList instead + void* operator new (size_t); }; @@ -334,8 +366,6 @@ class objc_property_list_t { uint32_t count; objc_property_t first; - // use newPropertyList instead - void* operator new (size_t) { return NULL; } void* operator new (size_t, void* buf) { return buf; } public: @@ -391,7 +421,9 @@ public: uint32_t newEntsize = sizeof(objc_property_t)) : entsize(newEntsize), count(newCount) { } - +private: + // use newPropertyList instead + void* operator new (size_t); }; template @@ -422,8 +454,6 @@ class objc_protocol_list_t { pint_t count; pint_t list[0]; - // use newProtocolList instead - void* operator new (size_t) { return NULL; } void* operator new (size_t, void* buf) { return buf; } public: @@ -470,7 +500,9 @@ public: } objc_protocol_list_t(uint32_t newCount) : count(newCount) { } - +private: + // use newProtocolList instead + void* operator new (size_t); }; diff --git a/launch-cache/dsc_extractor.cpp b/launch-cache/dsc_extractor.cpp index 4f68041..b1fb172 100644 --- a/launch-cache/dsc_extractor.cpp +++ b/launch-cache/dsc_extractor.cpp @@ -45,8 +45,10 @@ #include "dsc_iterator.h" #include "dsc_extractor.h" +#include "MachOTrie.hpp" #include +#include #include #include #include @@ -76,9 +78,27 @@ public: }; typedef std::unordered_map, CStringHash, CStringEquals> NameToSegments; +// Filter to find individual symbol re-exports in trie +class NotReExportSymbol { +public: + NotReExportSymbol(const std::set &rd) :_reexportDeps(rd) {} + bool operator()(const mach_o::trie::Entry &entry) const { + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + return true; + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 ) + return true; + // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export + if ( _reexportDeps.count(entry.other) != 0 ) + return true; + return false; + } +private: + const std::set &_reexportDeps; +}; + template -int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize) +int optimize_linkedit(macho_header* mh, uint64_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize) { typedef typename A::P P; typedef typename A::P::E E; @@ -97,8 +117,14 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach macho_dysymtab_command

* dynamicSymTab = NULL; macho_linkedit_data_command

* functionStarts = NULL; macho_linkedit_data_command

* dataInCode = NULL; + uint32_t exportsTrieOffset = 0; + uint32_t exportsTrieSize = 0; + std::set reexportDeps; + int depIndex = 0; for (uint32_t i = 0; i < cmd_count; ++i) { - if ( cmd->cmd() == macho_segment_command

::CMD ) { + switch ( cmd->cmd() ) { + case macho_segment_command

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

* segCmd = (macho_segment_command

*)cmd; segCmd->set_fileoff(cumulativeFileSize); @@ -106,16 +132,20 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { if ( sect->offset() != 0 ) - sect->set_offset(cumulativeFileSize+sect->addr()-segCmd->vmaddr()); + sect->set_offset((uint32_t)(cumulativeFileSize+sect->addr()-segCmd->vmaddr())); } if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { linkEditSegCmd = segCmd; } cumulativeFileSize += segCmd->filesize(); - } - else if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) { + } + break; + case LC_DYLD_INFO_ONLY: + { // zero out all dyld info macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

*)cmd; + exportsTrieOffset = dyldInfo->export_off(); + exportsTrieSize = dyldInfo->export_size(); dyldInfo->set_rebase_off(0); dyldInfo->set_rebase_size(0); dyldInfo->set_bind_off(0); @@ -126,18 +156,29 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach dyldInfo->set_lazy_bind_size(0); dyldInfo->set_export_off(0); dyldInfo->set_export_size(0); - } - else if ( cmd->cmd() == LC_SYMTAB ) { + } + break; + case LC_SYMTAB: symtab = (macho_symtab_command

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

*)cmd; - } - else if ( cmd->cmd() == LC_FUNCTION_STARTS ) { + break; + case LC_FUNCTION_STARTS: functionStarts = (macho_linkedit_data_command

*)cmd; - } - else if ( cmd->cmd() == LC_DATA_IN_CODE ) { + break; + case LC_DATA_IN_CODE: dataInCode = (macho_linkedit_data_command

*)cmd; + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + ++depIndex; + if ( cmd->cmd() == LC_REEXPORT_DYLIB ) { + reexportDeps.insert(depIndex); + } + break; } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); } @@ -156,26 +197,35 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach return -1; } - const uint32_t newFunctionStartsOffset = linkEditSegCmd->fileoff(); + const uint64_t newFunctionStartsOffset = linkEditSegCmd->fileoff(); uint32_t functionStartsSize = 0; if ( functionStarts != NULL ) { // copy function starts from original cache file to new mapped dylib file functionStartsSize = functionStarts->datasize(); memcpy((char*)mh + newFunctionStartsOffset, (char*)mapped_cache + functionStarts->dataoff(), functionStartsSize); } - const uint32_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + const uint64_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align uint32_t dataInCodeSize = 0; if ( dataInCode != NULL ) { // copy data-in-code info from original cache file to new mapped dylib file dataInCodeSize = dataInCode->datasize(); memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize); } - + + std::vector exports; + if ( exportsTrieSize != 0 ) { + const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset; + const uint8_t* exportsEnd = &exportsStart[exportsTrieSize]; + mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); + exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end()); + } + // look for local symbol info in unmapped part of shared cache dyldCacheHeader* header = (dyldCacheHeader*)mapped_cache; macho_nlist

* localNlists = NULL; uint32_t localNlistCount = 0; const char* localStrings = NULL; + const char* localStringsEnd = NULL; if ( header->mappingOffset() > offsetof(dyld_cache_header,localSymbolsSize) ) { dyldCacheLocalSymbolsInfo* localInfo = (dyldCacheLocalSymbolsInfo*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset()); dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)(((uint8_t*)mapped_cache) + header->localSymbolsOffset() + localInfo->entriesOffset()); @@ -187,11 +237,11 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach localNlistCount = entries[i].nlistCount(); localNlists = &allLocalNlists[localNlistStart]; localStrings = ((char*)localInfo) + localInfo->stringsOffset(); + localStringsEnd = &localStrings[localInfo->stringsSize()]; 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()]; @@ -206,14 +256,18 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach } } + // add room for N_INDR symbols for re-exported symbols + newSymCount += exports.size(); + // copy symbol entries and strings from original cache file to new mapped dylib file - const uint32_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align - const uint32_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist

); - const uint32_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t); + const uint64_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + const uint64_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist

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

* const newSymTabStart = (macho_nlist

*)(((uint8_t*)mh) + newSymTabOffset); char* const newStringPoolStart = (char*)mh + newStringPoolOffset; const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff()); const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff(); + const char* mergedStringPoolEnd = &mergedStringPoolStart[symtab->strsize()]; macho_nlist

* t = newSymTabStart; int poolOffset = 0; uint32_t symbolsCopied = 0; @@ -224,8 +278,28 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach continue; *t = *s; t->set_n_strx(poolOffset); - strcpy(&newStringPoolStart[poolOffset], &mergedStringPoolStart[s->n_strx()]); - poolOffset += (strlen(&newStringPoolStart[poolOffset]) + 1); + const char* symName = &mergedStringPoolStart[s->n_strx()]; + if ( symName > mergedStringPoolEnd ) + symName = ""; + strcpy(&newStringPoolStart[poolOffset], symName); + poolOffset += (strlen(symName) + 1); + ++t; + ++symbolsCopied; + } + // recreate N_INDR symbols in extracted dylibs for debugger + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + strcpy(&newStringPoolStart[poolOffset], it->name); + t->set_n_strx(poolOffset); + poolOffset += (strlen(it->name) + 1); + t->set_n_type(N_INDR | N_EXT); + t->set_n_sect(0); + t->set_n_desc(0); + const char* importName = it->importName; + if ( *importName == '\0' ) + importName = it->name; + strcpy(&newStringPoolStart[poolOffset], importName); + t->set_n_value(poolOffset); + poolOffset += (strlen(importName) + 1); ++t; ++symbolsCopied; } @@ -236,6 +310,8 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach // copy local symbols for (uint32_t i=0; i < localNlistCount; ++i) { const char* localName = &localStrings[localNlists[i].n_strx()]; + if ( localName > localStringsEnd ) + localName = ""; *t = localNlists[i]; t->set_n_strx(poolOffset); strcpy(&newStringPoolStart[poolOffset], localName); @@ -259,22 +335,22 @@ int optimize_linkedit(macho_header* mh, uint32_t textOffsetInCach // update load commands if ( functionStarts != NULL ) { - functionStarts->set_dataoff(newFunctionStartsOffset); + functionStarts->set_dataoff((uint32_t)newFunctionStartsOffset); functionStarts->set_datasize(functionStartsSize); } if ( dataInCode != NULL ) { - dataInCode->set_dataoff(newDataInCodeOffset); + dataInCode->set_dataoff((uint32_t)newDataInCodeOffset); dataInCode->set_datasize(dataInCodeSize); } symtab->set_nsyms(symbolsCopied); - symtab->set_symoff(newSymTabOffset); - symtab->set_stroff(newStringPoolOffset); + symtab->set_symoff((uint32_t)newSymTabOffset); + symtab->set_stroff((uint32_t)newStringPoolOffset); symtab->set_strsize(poolOffset); dynamicSymTab->set_extreloff(0); dynamicSymTab->set_nextrel(0); dynamicSymTab->set_locreloff(0); dynamicSymTab->set_nlocrel(0); - dynamicSymTab->set_indirectsymoff(newIndSymTabOffset); + dynamicSymTab->set_indirectsymoff((uint32_t)newIndSymTabOffset); linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff()); linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) ); @@ -348,8 +424,8 @@ size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, c FA->align = OSSwapHostToBigInt32(12); // Write regular segments into the buffer - uint32_t totalSize = 0; - uint32_t textOffsetInCache = 0; + uint64_t totalSize = 0; + uint64_t textOffsetInCache = 0; for( std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { if(strcmp(it->segName, "__TEXT") == 0 ) { @@ -364,7 +440,7 @@ size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, c if( afa->cputype == FA->cputype && afa->cpusubtype == FA->cpusubtype) { - fprintf(stderr, "arch already exists in fat dylib\n"); + //fprintf(stderr, "arch already exists in fat dylib\n"); dylib_data.resize(offsetInFatFile); return offsetInFatFile; } @@ -422,6 +498,8 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path dylib_create_func = dylib_maker; else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 ) dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 ) + dylib_create_func = dylib_maker; else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 ) dylib_create_func = dylib_maker; else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 ) @@ -430,6 +508,8 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path dylib_create_func = dylib_maker; else if ( strncmp((char*)mapped_cache, "dyld_v1 armv7", 14) == 0 ) dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64") == 0 ) + dylib_create_func = dylib_maker; else { fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); munmap(mapped_cache, statbuf.st_size); @@ -438,7 +518,7 @@ 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; - __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) { + __block int result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)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)); }); @@ -454,11 +534,10 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_queue_t writer_queue = dispatch_queue_create("dyld writer queue", 0); - __block int cumulativeResult = 0; __block unsigned count = 0; for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) { - dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); dispatch_group_async(group, process_queue, ^{ char dylib_path[PATH_MAX]; @@ -474,7 +553,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644); if ( fd == -1 ) { fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno); - cumulativeResult = -1; + result = -1; return; } @@ -482,7 +561,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path if (fstat(fd, &statbuf)) { fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno); close(fd); - cumulativeResult = -1; + result = -1; return; } @@ -490,21 +569,21 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path if(pread(fd, &vec->front(), vec->size(), 0) != (long)vec->size()) { fprintf(stderr, "can't read dylib file %s, errnor=%d\n", dylib_path, errno); close(fd); - cumulativeResult = -1; + result = -1; return; } const size_t offset = dylib_create_func(mapped_cache, *vec, it->second); dispatch_group_async(group, writer_queue, ^{ - progress(count++, map.size()); + progress(count++, (unsigned)map.size()); if(offset != vec->size()) { //Write out the first page, and everything after offset if( pwrite(fd, &vec->front(), 4096, 0) == -1 || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) { fprintf(stderr, "error writing, errnor=%d\n", errno); - cumulativeResult = -1; + result = -1; } } @@ -520,7 +599,7 @@ int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path dispatch_release(writer_queue); munmap(mapped_cache, statbuf.st_size); - return cumulativeResult; + return result; } diff --git a/launch-cache/dsc_iterator.cpp b/launch-cache/dsc_iterator.cpp index d57be70..477894b 100644 --- a/launch-cache/dsc_iterator.cpp +++ b/launch-cache/dsc_iterator.cpp @@ -35,7 +35,6 @@ #include "CacheFileAbstraction.hpp" - namespace dyld { @@ -47,7 +46,7 @@ namespace dyld { 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())) ) { - uint32_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); + uint64_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); const uint8_t* result = &cache[cacheOffset]; if ( result < cacheEnd ) return result; @@ -60,17 +59,19 @@ namespace dyld { // call the callback block on each segment in this image template - int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, const uint8_t* machHeader, + int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader, 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.version = 2; dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment dylibInfo.machHeader = machHeader; dylibInfo.path = dylibPath; + dylibInfo.inode = inode; + dylibInfo.modTime = modTime; const macho_header

* mh = (const macho_header

*)machHeader; const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

)); if ( (machHeader+ mh->sizeofcmds()) > cacheEnd ) @@ -154,6 +155,8 @@ namespace dyld { const uint8_t* firstSeg = NULL; for (uint32_t i=0; i < header->imagesCount(); ++i) { const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); + uint64_t inode = dylibs[i].inode(); + uint64_t modTime = dylibs[i].modTime(); if ( (const uint8_t*)dylibPath > cacheEnd ) return -1; const uint8_t* machHeader = mappedAddress(cache, cacheEnd, dylibs[i].address()); @@ -163,7 +166,7 @@ namespace dyld { return -1; if ( firstSeg == NULL ) firstSeg = machHeader; - int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, machHeader, callback); + int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, callback); if ( result != 0 ) return result; } @@ -184,6 +187,8 @@ extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t sha return dyld::walkImages(cache, shared_cache_size, callback); else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64h") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) return dyld::walkImages(cache, shared_cache_size, callback); else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) @@ -192,6 +197,8 @@ extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t sha return dyld::walkImages(cache, shared_cache_size, callback); else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); else return -1; } diff --git a/launch-cache/dsc_iterator.h b/launch-cache/dsc_iterator.h index b08eecc..6c75d0e 100644 --- a/launch-cache/dsc_iterator.h +++ b/launch-cache/dsc_iterator.h @@ -26,12 +26,15 @@ #include struct dyld_shared_cache_dylib_info { - uint32_t version; // initial version 1 + uint32_t version; // current version 2 + // following fields all exist in 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 + // following fields all exist in version 2 + uint64_t inode; // of dylib file or path hash + uint64_t modTime; // of dylib file }; typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info; diff --git a/launch-cache/dyld_shared_cache_util.cpp b/launch-cache/dyld_shared_cache_util.cpp index 9d4686a..be6aa65 100644 --- a/launch-cache/dyld_shared_cache_util.cpp +++ b/launch-cache/dyld_shared_cache_util.cpp @@ -33,8 +33,10 @@ #include #include #include +#include #include +#include #include "dsc_iterator.h" #include "dyld_cache_format.h" @@ -42,7 +44,6 @@ #include "MachOFileAbstraction.hpp" #include "CacheFileAbstraction.hpp" - enum Mode { modeNone, modeList, @@ -50,7 +51,8 @@ enum Mode { modeDependencies, modeSlideInfo, modeLinkEdit, - modeInfo + modeInfo, + modeSize }; struct Options { @@ -60,22 +62,48 @@ struct Options { bool printUUIDs; bool printVMAddrs; bool printDylibVersions; + bool printInodes; +}; + +struct TextInfo { + uint64_t textSize; + const char* path; +}; + +struct TextInfoSorter { + bool operator()(const TextInfo& left, const TextInfo& right) { + return (left.textSize > right.textSize); + } }; struct Results { std::map pageToContent; uint64_t linkeditBase; bool dependentTargetFound; + std::vector textSegments; }; - - void usage() { fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit | -map [ shared-cache-file ] | -slide_info | -info\n"); } +#if __x86_64__ +static bool isHaswell() +{ + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + if ( result != KERN_SUCCESS ) + return false; + return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H ); +} +#endif + /* * Get the path to the native shared cache for this host */ @@ -83,19 +111,24 @@ static const char* default_shared_cache_path() { #if __i386__ return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386"; #elif __x86_64__ - return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64"; + if ( isHaswell() ) + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64h"; + else + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64"; #elif __ARM_ARCH_5TEJ__ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5"; #elif __ARM_ARCH_6K__ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6"; +#elif __ARM_ARCH_7K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k"; #elif __ARM_ARCH_7A__ 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"; +#elif __arm64__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64"; #else #error unsupported architecture #endif @@ -173,6 +206,8 @@ void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared if ( options.printVMAddrs ) printf("0x%08llX ", segInfo->address); + if ( options.printInodes ) + printf("0x%08llX 0x%08llX ", dylibInfo->inode, dylibInfo->modTime); if ( options.printUUIDs ) { if ( dylibInfo->uuid != NULL ) { const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;; @@ -192,6 +227,23 @@ void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared } +template +void collect_size(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 ( dylibInfo->isAlias ) + return; + + TextInfo info; + info.textSize = segInfo->fileSize; + info.path = dylibInfo->path; + results.textSegments.push_back(info); + size_t size = segInfo->fileSize; +} + + static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results) @@ -279,7 +331,7 @@ void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_ static void checkMode(Mode mode) { if ( mode != modeNone ) { - fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, or -map\n"); + fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, or -size\n"); usage(); exit(1); } @@ -294,6 +346,7 @@ int main (int argc, const char* argv[]) { options.printUUIDs = false; options.printVMAddrs = false; options.printDylibVersions = false; + options.printInodes = false; options.dependentsOfPath = NULL; for (uint32_t i = 1; i < argc; i++) { @@ -328,9 +381,16 @@ int main (int argc, const char* argv[]) { else if (strcmp(opt, "-map") == 0) { checkMode(options.mode); options.mode = modeMap; + } + else if (strcmp(opt, "-size") == 0) { + checkMode(options.mode); + options.mode = modeSize; } else if (strcmp(opt, "-uuid") == 0) { options.printUUIDs = true; + } + else if (strcmp(opt, "-inode") == 0) { + options.printInodes = true; } else if (strcmp(opt, "-versions") == 0) { options.printDylibVersions = true; @@ -430,11 +490,16 @@ int main (int argc, const char* argv[]) { 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)); + printf(" __TEXT %3lluMB, 0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size()); else if ( mappings[i]. init_prot() & VM_PROT_WRITE ) - printf(" __DATA %lluMB\n", mappings[i].size()/(1024*1024)); + printf(" __DATA %3lluMB, 0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size()); else if ( mappings[i].init_prot() & VM_PROT_READ ) - printf(" __LINKEDIT %lluMB\n", mappings[i].size()/(1024*1024)); + printf(" __LINKEDIT %3lluMB, 0x%08llX -> 0x%08llX\n", mappings[i].size()/(1024*1024), mappings[i].address(), mappings[i].address() + mappings[i].size()); + } + if ( header->codeSignatureOffset() != 0 ) { + uint64_t size = statbuf.st_size - header->codeSignatureOffset(); + uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size(); + printf(" code sign %3lluMB, 0x%08llX -> 0x%08llX\n", size/(1024*1024), csAddr, csAddr + size); } } else { @@ -453,13 +518,17 @@ int main (int argc, const char* argv[]) { case modeLinkEdit: callback = process_linkedit; break; + case modeSize: + callback = collect_size; + break; case modeNone: case modeInfo: case modeSlideInfo: break; } } - else if ( strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0 ) { + else if ( (strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0) + || (strcmp((char*)options.mappedCache, "dyld_v1 x86_64h") == 0) ) { switch ( options.mode ) { case modeList: callback = print_list; @@ -473,6 +542,9 @@ int main (int argc, const char* argv[]) { case modeLinkEdit: callback = process_linkedit; break; + case modeSize: + callback = collect_size; + break; case modeNone: case modeInfo: case modeSlideInfo: @@ -494,6 +566,32 @@ int main (int argc, const char* argv[]) { case modeLinkEdit: callback = process_linkedit; break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + break; + } + } + else if ( strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 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 modeSize: + callback = collect_size; + break; case modeNone: case modeInfo: case modeSlideInfo: @@ -504,10 +602,10 @@ int main (int argc, const char* argv[]) { fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); exit(1); } - + __block Results results; results.dependentTargetFound = false; - int iterateResult = dyld_shared_cache_iterate(options.mappedCache, statbuf.st_size, + int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) { (callback)(dylibInfo, segInfo, options, results); }); @@ -522,6 +620,12 @@ int main (int argc, const char* argv[]) { printf("0x%08X %s\n", it->first, it->second); } } + else if ( options.mode == modeSize ) { + std::sort(results.textSegments.begin(), results.textSegments.end(), TextInfoSorter()); + for (std::vector::iterator it = results.textSegments.begin(); it != results.textSegments.end(); ++it) { + printf(" 0x%08llX %s\n", it->textSize, it->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); diff --git a/launch-cache/update_dyld_shared_cache.cpp b/launch-cache/update_dyld_shared_cache.cpp index 73cb114..cc6dc0c 100644 --- a/launch-cache/update_dyld_shared_cache.cpp +++ b/launch-cache/update_dyld_shared_cache.cpp @@ -128,7 +128,7 @@ public: static void findSharedDylibs(ArchPair ap); static ArchGraph* graphForArchPair(ArchPair ap) { return fgPerArchGraph[ap]; } static void setFileSystemRoot(const char* root) { fgFileSystemRoot = root; } - static void setFileSystemOverlay(const char* overlay) { fgFileSystemOverlay = overlay; } + static void setFileSystemOverlay(const std::vector& overlays); static const char* archName(ArchPair ap); ArchPair getArchPair() { return fArchPair; } @@ -170,7 +170,7 @@ private: static std::map fgPerArchGraph; static const char* fgFileSystemRoot; - static const char* fgFileSystemOverlay; + static std::vector fgFileSystemOverlays; ArchPair fArchPair; std::set fRoots; @@ -180,7 +180,7 @@ private: }; std::map ArchGraph::fgPerArchGraph; const char* ArchGraph::fgFileSystemRoot = ""; -const char* ArchGraph::fgFileSystemOverlay = ""; +std::vector ArchGraph::fgFileSystemOverlays; void ArchGraph::addArchPair(ArchPair ap) { @@ -188,18 +188,26 @@ void ArchGraph::addArchPair(ArchPair ap) fgPerArchGraph[ap] = new ArchGraph(ap); } +void ArchGraph::setFileSystemOverlay(const std::vector& overlays) +{ + for (std::vector::const_iterator it=overlays.begin(); it != overlays.end(); ++it) + fgFileSystemOverlays.push_back(*it); +} + void ArchGraph::addRoot(const char* vpath, const std::set& onlyArchs) { //fprintf(stderr, "addRoot(%s)\n", vpath); char completePath[MAXPATHLEN]; const char* path = NULL; // check -overlay path first - if ( fgFileSystemOverlay[0] != '\0' ) { - strcpy(completePath, fgFileSystemOverlay); + for (std::vector::const_iterator it=fgFileSystemOverlays.begin(); it != fgFileSystemOverlays.end(); ++it) { + strcpy(completePath, *it); strcat(completePath, vpath); // assumes vpath starts with '/' struct stat stat_buf; - if ( stat(completePath, &stat_buf) == 0 ) + if ( stat(completePath, &stat_buf) == 0 ) { path = completePath; + break; + } } // if not found in overlay, check for -root if ( (path == NULL) && (fgFileSystemRoot[0] != '\0') ) { @@ -255,41 +263,42 @@ ArchGraph::DependencyNode* ArchGraph::getNodeForVirtualPath(const char* vpath) { //fprintf(stderr, "getNodeForVirtualPath(%s)\n", vpath); char completePath[MAXPATHLEN]; - if ( fgFileSystemOverlay[0] != '\0' ) { + for (std::vector::const_iterator it=fgFileSystemOverlays.begin(); it != fgFileSystemOverlays.end(); ++it) { + const char* overlayPath = *it; // using -overlay means if /overlay/path/dylib exists use it, otherwise use /path/dylib - strcpy(completePath, fgFileSystemOverlay); + strcpy(completePath, overlayPath); strcat(completePath, vpath); // assumes vpath starts with '/' struct stat stat_buf; - if ( stat(completePath, &stat_buf) == 0 ) + if ( stat(completePath, &stat_buf) == 0 ) { return this->getNode(completePath); - else { - // support when install name is a symlink - const char* pathToSymlink = vpath; - if ( fgFileSystemRoot[0] != '\0' ) { - strcpy(completePath, fgFileSystemRoot); - strcat(completePath, vpath); - pathToSymlink = completePath; - } - if ( (lstat(pathToSymlink, &stat_buf) == 0) && S_ISLNK(stat_buf.st_mode) ) { - // requested path did not exist in /overlay, but leaf of path is a symlink in / - char pathInSymLink[MAXPATHLEN]; - size_t res = readlink(pathToSymlink, pathInSymLink, sizeof(pathInSymLink)); - if ( res != -1 ) { - pathInSymLink[res] = '\0'; - if ( pathInSymLink[0] != '/' ) { - char symFullPath[MAXPATHLEN]; - strcpy(symFullPath, vpath); - char* lastSlash = strrchr(symFullPath, '/'); - if ( lastSlash != NULL ) { - strcpy(lastSlash+1, pathInSymLink); - // (re)try looking for what symlink points to, but in /overlay - return this->getNodeForVirtualPath(symFullPath); - } - } - } + } + // support when install name is a symlink + const char* pathToSymlink = vpath; + if ( fgFileSystemRoot[0] != '\0' ) { + strcpy(completePath, fgFileSystemRoot); + strcat(completePath, vpath); + pathToSymlink = completePath; + } + if ( (lstat(pathToSymlink, &stat_buf) == 0) && S_ISLNK(stat_buf.st_mode) ) { + // requested path did not exist in /overlay, but leaf of path is a symlink in / + char pathInSymLink[MAXPATHLEN]; + size_t res = readlink(pathToSymlink, pathInSymLink, sizeof(pathInSymLink)); + if ( res != -1 ) { + pathInSymLink[res] = '\0'; + if ( pathInSymLink[0] != '/' ) { + char symFullPath[MAXPATHLEN]; + strcpy(symFullPath, vpath); + char* lastSlash = strrchr(symFullPath, '/'); + if ( lastSlash != NULL ) { + strcpy(lastSlash+1, pathInSymLink); + // (re)try looking for what symlink points to, but in /overlay + return this->getNodeForVirtualPath(symFullPath); + } + } } } } + if ( fgFileSystemRoot[0] != '\0' ) { // using -root means always use /rootpath/usr/lib strcpy(completePath, fgFileSystemRoot); @@ -371,8 +380,12 @@ ArchGraph::DependencyNode* ArchGraph::getNode(const char* path) aliasPath = &realPath[strlen(fgFileSystemRoot)]; } // Too many aliases in -overlay mode - if ( (fgFileSystemOverlay != NULL) && (fgFileSystemOverlay[0] != '\0') && (strncmp(realPath, fgFileSystemOverlay, strlen(fgFileSystemOverlay)) == 0) ) { - aliasPath = &realPath[strlen(fgFileSystemOverlay)]; + for (std::vector::const_iterator it=fgFileSystemOverlays.begin(); it != fgFileSystemOverlays.end(); ++it) { + const char* overlayPath = *it; + if ( strncmp(realPath, overlayPath, strlen(overlayPath)) == 0 ) { + aliasPath = &realPath[strlen(overlayPath)]; + break; + } } if ( fAliasesMap.find(aliasPath) == fAliasesMap.end() ) { if ( strcmp(aliasPath, node->getLayout()->getID().name) != 0 ) { @@ -522,7 +535,12 @@ const char* ArchGraph::archName(ArchPair ap) case CPU_TYPE_I386: return "i386"; case CPU_TYPE_X86_64: - return "x86_64"; + switch ( ap.subtype ) { + case CPU_SUBTYPE_X86_64_H: + return "x86_64h"; + default: + return "x86_64"; + } case CPU_TYPE_ARM: switch ( ap.subtype ) { case CPU_SUBTYPE_ARM_V4T: @@ -544,6 +562,8 @@ const char* ArchGraph::archName(ArchPair ap) default: return "arm"; } + case CPU_TYPE_ARM64: + return "arm64"; default: return "unknown"; } @@ -553,18 +573,18 @@ bool ArchGraph::sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char { if ( ! layout->isTwoLevelNamespace() ) asprintf(msg, "can't put %s in shared cache because it was built -flat_namespace", layout->getID().name); + else if ( ! layout->inSharableLocation() ) + asprintf(msg, "can't put %s in shared cache because its -install_name is not in /usr/lib or /System/Library", layout->getID().name); else if ( ! layout->hasSplitSegInfo() ) asprintf(msg, "can't put %s in shared cache because it was not built for %s or later", layout->getID().name, (iPhoneOS ? "iPhoneOS 3.1" : "MacOSX 10.5")); else if ( ! layout->isRootOwned() ) asprintf(msg, "can't put %s in shared cache because it is not owned by root", layout->getID().name); - else if ( ! layout->inSharableLocation() ) - asprintf(msg, "can't put %s in shared cache because it is not in /usr/lib or /System/Library", layout->getID().name); else if ( layout->hasDynamicLookupLinkage() ) asprintf(msg, "can't put %s in shared cache because it was built with '-undefined dynamic_lookup'", layout->getID().name); else if ( layout->hasMainExecutableLookupLinkage() ) asprintf(msg, "can't put %s in shared cache because it was built with '-bundle_loader'", layout->getID().name); - //else if ( ! layout->hasDyldInfo() ) - // asprintf(msg, "can't put %s in shared cache because it was built for older OS", layout->getID().name); + else if ( layout->hasMultipleReadWriteSegments() ) + asprintf(msg, "can't put %s in shared cache because it has multiple r/w segments", layout->getID().name); else return true; return false; @@ -638,6 +658,7 @@ public: uint32_t add(const char* str); uint32_t addUnique(const char* str); const char* stringAtIndex(uint32_t) const; + private: typedef std::unordered_map StringToOffset; @@ -649,7 +670,7 @@ private: StringPool::StringPool() - : fBufferUsed(0), fBufferAllocated(48*1024*1024) + : fBufferUsed(0), fBufferAllocated(64*1024*1024) { fBuffer = (char*)malloc(fBufferAllocated); } @@ -708,7 +729,7 @@ template class SharedCache { public: - SharedCache(ArchGraph* graph, const char* rootPath, const char* overlayPath, const char* cacheDir, bool explicitCacheDir, + SharedCache(ArchGraph* graph, const char* rootPath, const std::vector& overlayPaths, 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, bool dontMapLocalSymbols); @@ -742,8 +763,11 @@ private: 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 pathHash(const char*); static uint64_t pageAlign(uint64_t addr); + static uint64_t regionAlign(uint64_t addr); + static uint64_t pageAlign4KB(uint64_t addr); void assignNewBaseAddresses(bool verify); struct LayoutInfo { @@ -927,40 +951,54 @@ public: 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 <> cpu_type_t SharedCache::arch() { return CPU_TYPE_ARM64; } 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::sharedRegionStartAddress() { return ARM64_SHARED_REGION_START; } 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::sharedRegionSize() { return ARM64_SHARED_REGION_SIZE; } 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::sharedRegionStartWritableAddress(uint64_t exEnd) { return exEnd; } 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 <> uint64_t SharedCache::sharedRegionStartReadOnlyAddress(uint64_t wrEnd, uint64_t exEnd) { return (wrEnd + 16383) & (-16384); } template <> const char* SharedCache::archName() { return "i386"; } template <> const char* SharedCache::archName() { return "x86_64"; } template <> const char* SharedCache::archName() { return "arm"; } +template <> const char* SharedCache::archName() { return "arm64"; } template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName) { return archName; } template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName){ return archName; } template <> const char* SharedCache::cacheFileSuffix(bool, const char* archName) { return archName; } - +template <> 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 <> uint64_t SharedCache::pageAlign(uint64_t addr) { return ( (addr + 16383) & (-16384) ); } +template <> uint64_t SharedCache::regionAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } +template <> uint64_t SharedCache::regionAlign(uint64_t addr) { return ( (addr + 4095) & (-4096) ); } +template <> uint64_t SharedCache::regionAlign(uint64_t addr) { return ( (addr + 16383) & (-16384) ); } +template <> uint64_t SharedCache::regionAlign(uint64_t addr) { return ( (addr + 16383) & (-16384) ); } + + +template +uint64_t SharedCache::pageAlign4KB(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) +SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const std::vector& overlayPaths, const char* cacheDir, bool explicitCacheDir, bool alphaSort, bool verify, bool optimize, uint64_t dyldBaseAddress) : fArchGraph(graph), fVerify(verify), fExistingIsNotUpToDate(true), fCacheFileInFinalLocation(rootPath[0] == '\0'), fCacheFilePath(NULL), fExistingCacheForVerification(NULL), fDyldBaseAddress(dyldBaseAddress), @@ -986,8 +1024,12 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* LayoutInfo temp; temp.layout = lib; temp.info.address = 0; - temp.info.modTime = lib->getLastModTime(); temp.info.inode = lib->getInode(); + temp.info.modTime = lib->getLastModTime(); + if ( iPhoneOS ) { + temp.info.inode = pathHash(lib->getID().name); + temp.info.modTime = 0; + } temp.info.pathFileOffset = lib->getNameFileOffset(); // for now this is the offset within the dylib for(ArchGraph::StringToString::iterator ait = aliases.begin(); ait != aliases.end(); ++ait) { if ( strcmp(ait->second, lib->getID().name) == 0 ) { @@ -1009,8 +1051,9 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* if ( explicitCacheDir ) { fCacheFilePath = strdup(cachePathCanonical); } - else if ( overlayPath[0] != '\0' ) { - strcpy(cachePath, overlayPath); + else if ( overlayPaths.size() == 1 ) { + // if no -cache_dir and exactly on -overlay, write cache file into that overlay dir + strcpy(cachePath, overlayPaths[0]); strcat(cachePath, "/"); strcat(cachePath, cachePathCanonical); fCacheFilePath = strdup(cachePath); @@ -1024,8 +1067,8 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* else { fCacheFilePath = strdup(cachePathCanonical); } - if ( overlayPath[0] != '\0' ) { - // in overlay mode if there already is a cache file in the overlay + if ( overlayPaths.size() == 1 ) { + // in overlay mode if there already is a cache file in the overlay, // check if it is up to date. struct stat stat_buf; if ( stat(fCacheFilePath, &stat_buf) == 0 ) { @@ -1082,6 +1125,10 @@ SharedCache::SharedCache(ArchGraph* graph, const char* rootPath, const char* temp.aliases.clear(); temp.aliases.push_back(aliasPath); temp.info.pathFileOffset = fHeaderSize; + if ( iPhoneOS ) { + temp.info.inode = pathHash(aliasPath); + temp.info.modTime = 0; + } fDylibAliases.push_back(temp); fHeaderSize += strlen(aliasPath)+1; } @@ -1119,6 +1166,15 @@ uint64_t SharedCache::getWritableSegmentNewAddress(uint64_t proposedNewAddres return proposedNewAddress; } +template +uint64_t SharedCache::pathHash(const char* path) +{ + uint64_t sum = 0; + for (const char* s=path; *s != '\0'; ++s) + sum += sum*4 + *s; + return sum; +} + template void SharedCache::assignNewBaseAddresses(bool verify) @@ -1132,6 +1188,8 @@ void SharedCache::assignNewBaseAddresses(bool verify) MachOLayoutAbstraction::Segment& seg = segs[i]; seg.reset(); if ( seg.executable() && !seg.writable() ) { + // Some dylib require extra alignment + currentExecuteAddress = (currentExecuteAddress + seg.alignment() - 1) & (-seg.alignment()); // __TEXT segment if ( it->info.address == 0 ) it->info.address = currentExecuteAddress; @@ -1140,6 +1198,8 @@ void SharedCache::assignNewBaseAddresses(bool verify) } } } + // align __TEXT region + currentExecuteAddress = regionAlign(currentExecuteAddress); // layout DATA for dylibs const uint64_t startWritableAddress = sharedRegionStartWritableAddress(currentExecuteAddress); @@ -1154,10 +1214,13 @@ void SharedCache::assignNewBaseAddresses(bool verify) throw "found writable and executable segment"; // __DATA segment seg.setNewAddress(currentWritableAddress); - currentWritableAddress = pageAlign(seg.newAddress() + seg.size()); + // always 4KB align data pages to allow padding to be removed + currentWritableAddress = pageAlign4KB(seg.newAddress() + seg.size()); } } } + // align __DATA region + currentWritableAddress = regionAlign(currentWritableAddress); // layout all read-only (but not LINKEDIT) segments const uint64_t startReadOnlyAddress = sharedRegionStartReadOnlyAddress(currentWritableAddress, currentExecuteAddress); @@ -1175,6 +1238,7 @@ void SharedCache::assignNewBaseAddresses(bool verify) } // layout all LINKEDIT segments at end of all read-only segments + currentReadOnlyAddress = regionAlign(currentReadOnlyAddress); // fLinkEditsStartAddress = currentReadOnlyAddress; fFirstLinkEditSegment = NULL; for(typename std::vector::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) { @@ -1283,7 +1347,7 @@ bool SharedCache::notUpToDate(const void* cache, unsigned int aliasCount) strcpy(&temp[15-strlen(archPairName)], archPairName); if ( strcmp(header->magic(), temp) != 0 ) { if ( fVerify ) { - fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file has invalid header\n", getpid(), archName()); + fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file has invalid header\n", getpid(), archPairName); return false; } else { @@ -1294,11 +1358,11 @@ bool SharedCache::notUpToDate(const void* cache, unsigned int aliasCount) // not valid if count of images does not match current images needed if ( header->imagesCount() != (fDylibs.size()+aliasCount) ) { if ( fVerify ) { - fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file contains a different set of dylibs\n", getpid(), archName()); + fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file contains a different set of dylibs\n", getpid(), archPairName); return false; } else { - fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because current cache file contains a different set of dylibs\n", getpid(), archName()); + fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because current cache file contains a different set of dylibs\n", getpid(), archPairName); return true; } } @@ -1317,7 +1381,7 @@ bool SharedCache::notUpToDate(const void* cache, unsigned int aliasCount) if ( fVerify ) { if ( cacheEntry->pathFileOffset() > textSize ) { throwf("update_dyld_shared_cache[%u]: for arch=%s, image entries corrupt, bad path offset in %s\n", - getpid(), archName(), it->layout->getID().name); + getpid(), archPairName, it->layout->getID().name); } // in -verify mode, just match by path and warn if file looks different if ( strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0 ) { @@ -1325,7 +1389,7 @@ bool SharedCache::notUpToDate(const void* cache, unsigned int aliasCount) sortingMap[it->layout] = cacheEntry-imagesStart; if ( (cacheEntry->inode() != it->info.inode) || (cacheEntry->modTime() != it->info.modTime) ) { fprintf(stderr, "update_dyld_shared_cache[%u] warning: for arch=%s, %s has changed since cache was built\n", - getpid(), archName(), it->layout->getID().name); + getpid(), archPairName, it->layout->getID().name); } break; } @@ -1346,10 +1410,10 @@ bool SharedCache::notUpToDate(const void* cache, unsigned int aliasCount) } if ( !found ) { if ( fVerify ) { - throwf("update_dyld_shared_cache[%u] can't verify %s cache because %s is not in existing cache\n", getpid(), archName(), it->layout->getID().name); + throwf("update_dyld_shared_cache[%u] can't verify %s cache because %s is not in existing cache\n", getpid(), archPairName, it->layout->getID().name); } else { - fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because dylib at %s has changed\n", getpid(), archName(), it->layout->getID().name); + fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because dylib at %s has changed\n", getpid(), archPairName, it->layout->getID().name); return true; } } @@ -1762,7 +1826,7 @@ void LinkEditOptimizer::copyIndirectSymbolTable(uint32_t& offset) } template -void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset, +void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t leSize, uint32_t stringPoolOffset, uint32_t linkEditsFileOffset, bool keepSignatures) { // set LINKEDIT segment commmand to new merged LINKEDIT @@ -1774,16 +1838,31 @@ void LinkEditOptimizer::updateLoadCommands(uint64_t newVMAddress, uint64_t si macho_segment_command

* seg = (macho_segment_command

*)cmd; if ( strcmp(seg->segname(), "__LINKEDIT") == 0 ) { seg->set_vmaddr(newVMAddress); - seg->set_vmsize(size); - seg->set_filesize(size); + seg->set_vmsize(leSize); + seg->set_filesize(leSize); seg->set_fileoff(linkEditsFileOffset); } - // don't alter __TEXT until is fixed - else if ( strcmp(seg->segname(), "__TEXT") != 0 ) { - // update all other segments fileoff to be offset from start of cache file + else { pint_t oldFileOff = seg->fileoff(); - seg->set_fileoff(fSharedCache.cacheFileOffsetForVMAddress(seg->vmaddr())); + // don't alter __TEXT until is fixed + if ( strcmp(seg->segname(), "__TEXT") != 0 ) { + // update all other segments fileoff to be offset from start of cache file + seg->set_fileoff(fSharedCache.cacheFileOffsetForVMAddress(seg->vmaddr())); + } pint_t fileOffsetDelta = seg->fileoff() - oldFileOff; + const MachOLayoutAbstraction::Segment* layoutSeg = fLayout.getSegment(seg->segname()); + if ( layoutSeg != NULL ) { + //if ( seg->filesize() != layoutSeg->fileSize() ) { + // fprintf(stderr, "LC filesize=0x%08llX, trimmed seg file size=0x%08llX, seg=%s, path=%s\n", + // seg->filesize(), layoutSeg->fileSize(), seg->segname(), fLayout.getFilePath()); + //} + //if ( seg->vmsize() != layoutSeg->size() ) { + // fprintf(stderr, "LC vmsize=0x%08llX, trimmed seg size=0x%08llX, seg=%s, path=%s\n", + // seg->vmsize(), layoutSeg->size(), seg->segname(), fLayout.getFilePath()); + //} + seg->set_vmsize(layoutSeg->size()); + seg->set_filesize(layoutSeg->fileSize()); + } // update all sections in this segment macho_section

* const sectionsStart = (macho_section

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

)); macho_section

* const sectionsEnd = §ionsStart[seg->nsects()]; @@ -1986,7 +2065,7 @@ uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures, bool dontMapLocal // update load commands so that all dylibs shared different areas of the same LINKEDIT segment for(typename std::vector*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) { - (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalUnoptimizedSize, fOffsetOfOldStringPoolInCombinedLinkedit, linkEditsFileOffset, keepSignatures); + (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalOptimizedSize, fOffsetOfOldStringPoolInCombinedLinkedit, linkEditsFileOffset, keepSignatures); } //fprintf(stderr, "fLinkEditsTotalUnoptimizedSize=%llu, fLinkEditsTotalOptimizedSize=%u\n", fLinkEditsTotalUnoptimizedSize, fLinkEditsTotalOptimizedSize); @@ -2008,10 +2087,10 @@ uint8_t* SharedCache::optimizeLINKEDIT(bool keepSignatures, bool dontMapLocal seg.setFileOffset(linkEditsFileOffset); } } - } - + } + // return new end of cache - return (uint8_t*)fFirstLinkEditSegment->mappedAddress() + fLinkEditsTotalOptimizedSize; + return (uint8_t*)fFirstLinkEditSegment->mappedAddress() + regionAlign(fLinkEditsTotalOptimizedSize); } @@ -2402,7 +2481,7 @@ static bool adhoc_codesign_share_cache(const char* path) template <> bool SharedCache::addCacheSlideInfo(){ return true; } template <> bool SharedCache::addCacheSlideInfo() { return true; } template <> bool SharedCache::addCacheSlideInfo() { return false; } - +template <> bool SharedCache::addCacheSlideInfo() { return true; } template @@ -2510,8 +2589,10 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, struct stat stat_buf; if ( fstat(src, &stat_buf) == -1) throwf("can't stat open file %s, errno=%d", path, errno); - if ( (it->layout->getInode() != stat_buf.st_ino) || (it->layout->getLastModTime() != stat_buf.st_mtime) ) - throwf("file modified during cache creation: %s", path); + if ( (it->layout->getInode() != stat_buf.st_ino) ) + throwf("file inode changed from %llu to %llu during cache creation: %s", it->layout->getInode(), stat_buf.st_ino, path); + else if ( it->layout->getLastModTime() != stat_buf.st_mtime ) + throwf("file mtime changed from 0x%lX to 0x%lX during cache creation: %s", it->layout->getLastModTime(), stat_buf.st_mtime, path); if ( verbose ) fprintf(stderr, "update_dyld_shared_cache: copying %s to cache\n", it->layout->getFilePath()); @@ -2624,7 +2705,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, if ( optimize ) { //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024)); cacheFileSize = (this->optimizeLINKEDIT(keepSignatures, dontMapLocalSymbols) - inMemoryCache); - //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024)); + //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size 0x%08X %uMB\n", cacheFileSize, cacheFileSize/(1024*1024)); // update header to reduce mapping size dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&inMemoryCache[sizeof(dyldCacheHeader)]; @@ -2704,7 +2785,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, } slideInfo->set_entries_count(entry_count); - int slideInfoPageSize = pageAlign(slideInfo->entries_offset() + entry_count*entry_size); + int slideInfoPageSize = regionAlign(slideInfo->entries_offset() + entry_count*entry_size); cacheFileSize += slideInfoPageSize; // update mappings to increase RO size @@ -2725,19 +2806,6 @@ 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; @@ -2773,7 +2841,7 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, // update state fUnmappedLocalSymbolsSize = pageAlign(stringsOffset + stringsSize); - cacheFileSize = localSymbolsOffset + fUnmappedLocalSymbolsSize; + cacheFileSize = regionAlign(localSymbolsOffset + fUnmappedLocalSymbolsSize); // update header to show location of slidePointers dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; @@ -2782,6 +2850,21 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, cacheHeader->set_codeSignatureOffset(cacheFileSize); } + // 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()]; + // incorporate code signature size into overflow check + uint32_t estCodeSigSize = regionAlign(cacheFileSize/200); // guess 0.5% for code signature + for (int i=0; i < cacheHeader->mappingCount(); ++i) { + uint64_t endAddr = mappings[i].address() + mappings[i].size() + estCodeSigSize; + if ( endAddr > (sharedRegionStartAddress() + sharedRegionSize()) ) { + throwf("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regionsaddress space. Overflow amount: %lluKB\n", + getpid(), fArchGraph->archName(), (endAddr-(sharedRegionStartAddress() + sharedRegionSize()))/1024); + } + } + } + // compute UUID of whole cache uint8_t digest[16]; CC_MD5(inMemoryCache, cacheFileSize, digest); @@ -3016,6 +3099,11 @@ bool SharedCache::update(bool force, bool optimize, bool deleteExistingFirst, fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit, fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit+fSizeOfOldStringPoolInCombinedLinkedit); + dyldCacheHeader* cacheHeader = (dyldCacheHeader*)inMemoryCache; + if ( cacheHeader->slideInfoSize() != 0 ) { + fprintf(fmap, " linkedit %4lluKB kernel slide info\n", (cacheHeader->slideInfoSize())/1024); + } + fprintf(fmap, "unmapped -- %4uMB local symbol info\n", fUnmappedLocalSymbolsSize/(1024*1024)); uint64_t endMappingAddr = fMappings[2].sfm_address + fMappings[2].sfm_size; @@ -3126,16 +3214,7 @@ static void parsePathsFile(const char* filePath, std::vector& paths *last = '\0'; --last; } - // 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 ) { - // 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); - } + paths.push_back(symbolStart); symbolStart = NULL; state = lineStart; } @@ -3151,11 +3230,11 @@ static void parsePathsFile(const char* filePath, std::vector& paths -static void setSharedDylibs(const char* rootPath, const char* overlayPath, const std::set& onlyArchs, std::vector rootsPaths) +static void setSharedDylibs(const char* rootPath, const std::vector& overlayPaths, const std::set& onlyArchs, std::vector rootsPaths) { // set file system root ArchGraph::setFileSystemRoot(rootPath); - ArchGraph::setFileSystemOverlay(overlayPath); + ArchGraph::setFileSystemOverlay(overlayPaths); // initialize all architectures requested for(std::set::iterator a = onlyArchs.begin(); a != onlyArchs.end(); ++a) @@ -3171,7 +3250,7 @@ static void setSharedDylibs(const char* rootPath, const char* overlayPath, const } -static void scanForSharedDylibs(const char* rootPath, const char* overlayPath, const char* dirOfPathFiles, const std::set& onlyArchs) +static void scanForSharedDylibs(const char* rootPath, const std::vector& overlayPaths, const char* dirOfPathFiles, const std::set& onlyArchs) { char rootDirOfPathFiles[strlen(rootPath)+strlen(dirOfPathFiles)+2]; // in -root mode, look for roots in /rootpath/var/db/dyld @@ -3216,14 +3295,14 @@ static void scanForSharedDylibs(const char* rootPath, const char* overlayPath, c if ( rootsPaths.size() == 0 ) fprintf(stderr, "update_dyld_shared_cache: warning, no entries found in shared_region_roots\n"); - setSharedDylibs(rootPath, overlayPath, onlyArchs, rootsPaths); + setSharedDylibs(rootPath, overlayPaths, onlyArchs, rootsPaths); } -static void setSharedDylibs(const char* rootPath, const char* overlayPath, const char* pathsFile, const std::set& onlyArchs) +static void setSharedDylibs(const char* rootPath, const std::vector& overlayPaths, const char* pathsFile, const std::set& onlyArchs) { std::vector rootsPaths; parsePathsFile(pathsFile, rootsPaths); - setSharedDylibs(rootPath, overlayPath, onlyArchs, rootsPaths); + setSharedDylibs(rootPath, overlayPaths, onlyArchs, rootsPaths); } @@ -3270,7 +3349,7 @@ static void deleteOrphanTempCacheFiles() -static bool updateSharedeCacheFile(const char* rootPath, const char* overlayPath, const char* cacheDir, bool explicitCacheDir, const std::set& onlyArchs, +static bool updateSharedeCacheFile(const char* rootPath, const std::vector& overlayPaths, const char* cacheDir, bool explicitCacheDir, const std::set& onlyArchs, bool force, bool alphaSort, bool optimize, bool deleteExistingFirst, bool verify, bool keepSignatures, bool dontMapLocalSymbols) { bool didUpdate = false; @@ -3298,19 +3377,25 @@ static bool updateSharedeCacheFile(const char* rootPath, const char* overlayPath switch ( a->arch ) { case CPU_TYPE_I386: { - SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPath, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); 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); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); 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); + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); + didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols); + } + break; + case CPU_TYPE_ARM64: + { + SharedCache cache(ArchGraph::graphForArchPair(*a), rootPath, overlayPaths, cacheDir, explicitCacheDir, alphaSort, verify, optimize, dyldBaseAddress); didUpdate |= cache.update(force, optimize, deleteExistingFirst, index, archCount, keepSignatures, dontMapLocalSymbols); } break; @@ -3334,7 +3419,7 @@ int main(int argc, const char* argv[]) { std::set onlyArchs; const char* rootPath = ""; - const char* overlayPath = ""; + std::vector overlayPaths; const char* dylibListFile = NULL; bool force = false; bool alphaSort = false; @@ -3343,6 +3428,7 @@ int main(int argc, const char* argv[]) bool keepSignatures = false; bool explicitCacheDir = false; bool dontMapLocalSymbols = false; + bool relaunchForHaswell = false; const char* cacheDir = NULL; try { @@ -3389,9 +3475,10 @@ int main(int argc, const char* argv[]) throw "-root missing path argument"; } else if ( strcmp(arg, "-overlay") == 0 ) { - overlayPath = argv[++i]; - if ( overlayPath == NULL ) + const char* path = argv[++i]; + if ( path == NULL ) throw "-overlay missing path argument"; + overlayPaths.push_back(path); } else if ( strcmp(arg, "-cache_dir") == 0 ) { cacheDir = argv[++i]; @@ -3405,6 +3492,8 @@ int main(int argc, const char* argv[]) onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL)); else if ( strcmp(arch, "x86_64") == 0 ) onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL)); + else if ( strcmp(arch, "x86_64h") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H)); else if ( strcmp(arch, "armv4t") == 0 ) onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V4T)); else if ( strcmp(arch, "armv5") == 0 ) @@ -3419,12 +3508,15 @@ int main(int argc, const char* argv[]) 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 if ( strcmp(arch, "arm64") == 0 ) + onlyArchs.insert(ArchPair(CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL)); else throwf("unknown architecture %s", arch); } else if ( strcmp(arg, "-universal_boot") == 0 ) { onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL)); onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL)); + relaunchForHaswell = true; } else { usage(); @@ -3447,11 +3539,11 @@ int main(int argc, const char* argv[]) } // strip tailing slashes on -overlay - if ( overlayPath[0] != '\0' ) { + for (std::vector::iterator it=overlayPaths.begin(); it != overlayPaths.end(); ++it) { char realOverlayPath[MAXPATHLEN]; - if ( realpath(overlayPath, realOverlayPath) == NULL ) - throwf("realpath() failed on %s\n", overlayPath); - overlayPath = strdup(realOverlayPath); + if ( realpath(*it, realOverlayPath) == NULL ) + throwf("realpath() failed on %s\n", *it); + *it = strdup(realOverlayPath); } // set default location to write cache dir @@ -3470,8 +3562,20 @@ int main(int argc, const char* argv[]) #if __i386__ || __x86_64__ onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL)); // check system is capable of running 64-bit programs - if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available ) - onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL)); + if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available ) { + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + if ( result != KERN_SUCCESS ) + throw "host_info() failed"; + if ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H ) + onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H)); + else + onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_ALL)); + } #else #error unsupported architecture #endif @@ -3483,10 +3587,10 @@ int main(int argc, const char* argv[]) // build list of shared dylibs if ( dylibListFile != NULL ) - setSharedDylibs(rootPath, overlayPath, dylibListFile, onlyArchs); + setSharedDylibs(rootPath, overlayPaths, dylibListFile, onlyArchs); else - scanForSharedDylibs(rootPath, overlayPath, "/var/db/dyld/shared_region_roots/", onlyArchs); - bool didUpdate = updateSharedeCacheFile(rootPath, overlayPath, cacheDir, explicitCacheDir, onlyArchs, force, alphaSort, optimize, + scanForSharedDylibs(rootPath, overlayPaths, "/var/db/dyld/shared_region_roots/", onlyArchs); + bool didUpdate = updateSharedeCacheFile(rootPath, overlayPaths, cacheDir, explicitCacheDir, onlyArchs, force, alphaSort, optimize, false, verify, keepSignatures, dontMapLocalSymbols); if ( didUpdate && !iPhoneOS ) { @@ -3495,14 +3599,32 @@ int main(int argc, const char* argv[]) 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; + if ( !overlayPaths.empty() ) + nuggetRootPath = overlayPaths[0]; else if ( rootPath[0] != '\0' ) nuggetRootPath = rootPath; (*proc)(nuggetRootPath); } dlclose(handle); } + + if ( relaunchForHaswell ) { + char cmd[2048]; + strlcpy(cmd, argv[0], 2048); + strlcat(cmd, " -arch x86_64h", 2048); + if ( force ) + strlcat(cmd, " -force", 2048); + if ( verify ) + strlcat(cmd, " -verify", 2048); + if ( alphaSort ) + strlcat(cmd, " -sort_by_name", 2048); + if ( (rootPath != NULL) && (rootPath[0] != '\0') ) { + strlcat(cmd, " -root ", 2048); + strlcat(cmd, rootPath, 2048); + } + return system(cmd); + } + } catch (const char* msg) { fprintf(stderr, "update_dyld_shared_cache failed: %s\n", msg); diff --git a/libdyld.xcconfig b/libdyld.xcconfig new file mode 100644 index 0000000..649af26 --- /dev/null +++ b/libdyld.xcconfig @@ -0,0 +1,8 @@ + +LIBSYSTEM_LIBS[sdk=*simulator*] = -Wl,-upward-lsystem_sim_c -Wl,-upward-lSystem +LIBSYSTEM_LIBS[sdk=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[sdk=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 + +INSTALL_PATH_ACTUAL = /usr/lib/system + + diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 65820ed..5b297f3 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -351,6 +351,43 @@ void ImageLoader::applyInterposing(const LinkContext& context) this->recursiveApplyInterposing(context); } + +uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* inImage, const ImageLoader* onlyInImage) +{ + //dyld::log("interposedAddress(0x%08llX), tupleCount=%lu\n", (uint64_t)address, fgInterposingTuples.size()); + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + //dyld::log(" interposedAddress: replacee=0x%08llX, replacement=0x%08llX, neverImage=%p, onlyImage=%p, inImage=%p\n", + // (uint64_t)it->replacee, (uint64_t)it->replacement, it->neverImage, it->onlyImage, inImage); + // replace all references to 'replacee' with 'replacement' + if ( (address == it->replacee) && (inImage != it->neverImage) && ((it->onlyImage == NULL) || (inImage == it->onlyImage)) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld interposing: replace 0x%lX with 0x%lX\n", it->replacee, it->replacement); + } + return it->replacement; + } + } + return address; +} + +void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count) +{ + for(size_t i=0; i < count; ++i) { + ImageLoader::InterposeTuple tuple; + tuple.replacement = (uintptr_t)array[i].replacement; + tuple.neverImage = NULL; + tuple.onlyImage = this; + tuple.replacee = (uintptr_t)array[i].replacee; + // chain to any existing interpositions + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + if ( (it->replacee == tuple.replacee) && (it->onlyImage == this) ) { + tuple.replacee = it->replacement; + } + } + ImageLoader::fgInterposingTuples.push_back(tuple); + } +} + + 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(), fDlopenReferenceCount, fNeverUnload); @@ -423,13 +460,39 @@ bool ImageLoader::decrementDlopenReferenceCount() return false; } + +// upward dylib initializers can be run too soon +// To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs +// have their initialization postponed until after the recursion through downward dylibs +// has completed. +void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread, + InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images) +{ + uint32_t maxImageCount = context.imageCount(); + ImageLoader::UninitedUpwards upsBuffer[maxImageCount]; + ImageLoader::UninitedUpwards& ups = upsBuffer[0]; + ups.count = 0; + // Calling recursive init on all images in images list, building a new list of + // uninitialized upward dependencies. + for (uintptr_t i=0; i < images.count; ++i) { + images.images[i]->recursiveInitialization(context, thisThread, timingInfo, ups); + } + // If any upward dependencies remain, init them. + if ( ups.count > 0 ) + processInitializers(context, thisThread, timingInfo, ups); +} + + void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) { uint64_t t1 = mach_absolute_time(); - mach_port_t this_thread = mach_thread_self(); - this->recursiveInitialization(context, this_thread, timingInfo); + mach_port_t thisThread = mach_thread_self(); + ImageLoader::UninitedUpwards up; + up.count = 1; + up.images[0] = this; + processInitializers(context, thisThread, timingInfo, up); context.notifyBatch(dyld_image_state_initialized); - mach_port_deallocate(mach_task_self(), this_thread); + mach_port_deallocate(mach_task_self(), thisThread); uint64_t t2 = mach_absolute_time(); fgTotalInitTime += (t2 - t1); } @@ -518,7 +581,6 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli fState = dyld_image_state_dependents_mapped; // get list of libraries this image needs - //dyld::log("ImageLoader::recursiveLoadLibraries() %ld = %d*%ld\n", fLibrariesCount*sizeof(DependentLibrary), fLibrariesCount, sizeof(DependentLibrary)); DependentLibraryInfo libraryInfos[fLibraryCount]; this->doGetDependentLibraries(libraryInfos); @@ -601,8 +663,11 @@ void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool prefli (*context.setErrorStrings)(dyld_error_kind_dylib_wrong_arch, this->getPath(), requiredLibInfo.name, NULL); else (*context.setErrorStrings)(dyld_error_kind_dylib_missing, this->getPath(), requiredLibInfo.name, NULL); - dyld::throwf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getRealPath(), msg); + const char* newMsg = dyld::mkstringf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getRealPath(), msg); + free((void*)msg); // our free() will do nothing if msg is a string literal + throw newMsg; } + free((void*)msg); // our free() will do nothing if msg is a string literal // ok if weak library not found dependentLib = NULL; canUsePrelinkingInfo = false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero @@ -901,7 +966,8 @@ void ImageLoader::recursiveSpinUnLock() } -void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, InitializerTimingList& timingInfo) +void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, + InitializerTimingList& timingInfo, UninitedUpwards& uninitUps) { recursive_lock lock_info(this_thread); recursiveSpinLock(lock_info); @@ -911,17 +977,18 @@ void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_ // break cycles fState = dyld_image_state_dependents_initialized-1; try { - bool hasUpwards = false; // initialize lower level libraries first for(unsigned int i=0; i < libraryCount(); ++i) { ImageLoader* dependentImage = libImage(i); if ( dependentImage != NULL ) { - // don't try to initialize stuff "above" me - bool isUpward = libIsUpward(i); - if ( (dependentImage->fDepth >= fDepth) && !isUpward ) { - dependentImage->recursiveInitialization(context, this_thread, timingInfo); + // don't try to initialize stuff "above" me yet + if ( libIsUpward(i) ) { + uninitUps.images[uninitUps.count] = dependentImage; + uninitUps.count++; + } + else if ( dependentImage->fDepth >= fDepth ) { + dependentImage->recursiveInitialization(context, this_thread, timingInfo, uninitUps); } - hasUpwards |= isUpward; } } @@ -938,18 +1005,6 @@ 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) { - ImageLoader* dependentImage = libImage(i); - // ObjC CG hang - // only init upward lib here if lib is not downwardly referenced somewhere - if ( (dependentImage != NULL) && libIsUpward(i) && !dependentImage->isReferencedDownward() ) { - dependentImage->recursiveInitialization(context, this_thread, timingInfo); - } - } - } - // let anyone know we finished initializing this image fState = dyld_image_state_initialized; oldState = fState; @@ -984,16 +1039,16 @@ static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) } } if ( partTime < sUnitsPerSecond ) { - uint32_t milliSecondsTimesHundred = (partTime*100000)/sUnitsPerSecond; - uint32_t milliSeconds = milliSecondsTimesHundred/100; - uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t milliSecondsTimesHundred = (uint32_t)((partTime*100000)/sUnitsPerSecond); + uint32_t milliSeconds = (uint32_t)(milliSecondsTimesHundred/100); + uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); uint32_t percent = percentTimesTen/10; dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); } else { - uint32_t secondsTimeTen = (partTime*10)/sUnitsPerSecond; + uint32_t secondsTimeTen = (uint32_t)((partTime*10)/sUnitsPerSecond); uint32_t seconds = secondsTimeTen/10; - uint32_t percentTimesTen = (partTime*1000)/totalTime; + uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); uint32_t percent = percentTimesTen/10; dyld::log("%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); } diff --git a/src/ImageLoader.h b/src/ImageLoader.h index 4e00826..6f4c3bc 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -40,7 +40,11 @@ #include #include -#if __MAC_OS_X_VERSION_MIN_REQUIRED +#if __arm__ + #include +#endif + +#if __x86_64__ || __i386__ #include #else // work around until iOS has CrashReporterClient.h @@ -48,6 +52,15 @@ #define CRSetCrashLogMessage2(x) #endif +#ifndef SHARED_REGION_BASE_ARM64 + #define SHARED_REGION_BASE_ARM64 0x7FFF80000000LL +#endif + +#ifndef SHARED_REGION_SIZE_ARM64 + #define SHARED_REGION_SIZE_ARM64 0x10000000LL +#endif + + #define LOG_BINDINGS 0 #include "mach-o/dyld_images.h" @@ -62,6 +75,9 @@ #elif __arm__ #define SHARED_REGION_BASE SHARED_REGION_BASE_ARM #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM +#elif __arm64__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_ARM64 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM64 #endif #ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER @@ -84,14 +100,14 @@ #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 PREBOUND_IMAGE_SUPPORT __arm__ #define TEXT_RELOC_SUPPORT __i386__ - #define DYLD_SHARED_CACHE_SUPPORT __arm__ + #define DYLD_SHARED_CACHE_SUPPORT (__arm__ || __arm64__) #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 SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__) #define INITIAL_IMAGE_COUNT 256 #else #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 @@ -103,7 +119,7 @@ #define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__) #define SUPPORT_VERSIONED_PATHS 1 #define SUPPORT_CLASSIC_MACHO 1 - #define CORESYMBOLICATION_SUPPORT 1 + #define SUPPORT_ZERO_COST_EXCEPTIONS 1 #define INITIAL_IMAGE_COUNT 200 #endif @@ -149,6 +165,22 @@ extern "C" void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off #endif +#if __arm64__ + #define dyld_page_trunc(__addr) (__addr & (-16384)) + #define dyld_page_round(__addr) ((__addr + 16383) & (-16384)) + #define dyld_page_size 16384 +#elif __arm__ + #define dyld_page_trunc(__addr) trunc_page_kernel(__addr) + #define dyld_page_round(__addr) round_page_kernel(__addr) + #define dyld_page_size vm_kernel_page_size +#else + #define dyld_page_trunc(__addr) (__addr & (-4096)) + #define dyld_page_round(__addr) ((__addr + 4095) & (-4096)) + #define dyld_page_size 4096 +#endif + + + struct ProgramVars { const void* mh; @@ -246,6 +278,8 @@ public: ImageLoader* mainExecutable; const char* imageSuffix; const char** rootPaths; + const dyld_interpose_tuple* dynamicInterposeArray; + size_t dynamicInterposeCount; PrebindMode prebindUsage; SharedRegionMode sharedRegionMode; bool dyldLoadedAtSameAddressNeededBySharedCache; @@ -303,6 +337,12 @@ public: } images[1]; }; + struct UninitedUpwards + { + uintptr_t count; + ImageLoader* images[1]; + }; + // constructor is protected, but anyone can delete an image virtual ~ImageLoader(); @@ -483,8 +523,13 @@ public: // if image has a UUID, copy into parameter and return true virtual bool getUUID(uuid_t) const = 0; - - + + // dynamic interpose values onto this image + virtual void dynamicInterpose(const LinkContext& context) = 0; + + // record interposing for any late binding + void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count); + // // A segment is a chunk of an executable file that is mapped into memory. // @@ -504,7 +549,9 @@ public: virtual uintptr_t segActualEndAddress(unsigned int) const = 0; + // info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS virtual uint32_t sdkVersion() const = 0; + virtual uint32_t minOSVersion() const = 0; // if the image contains interposing functions, register them virtual void registerInterposing() = 0; @@ -571,7 +618,8 @@ public: struct InterposeTuple { uintptr_t replacement; - ImageLoader* replacementImage; // don't apply replacement to this image + ImageLoader* neverImage; // don't apply replacement to this image + ImageLoader* onlyImage; // only apply replacement to this image uintptr_t replacee; }; @@ -627,7 +675,8 @@ protected: 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&); + void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, + ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&); // fill in information about dependent libraries (array length is fLibraryCount) virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0; @@ -679,6 +728,8 @@ protected: void setFileInfo(dev_t device, ino_t inode, time_t modDate); + static uintptr_t interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* notInImage, const ImageLoader* onlyInImage=NULL); + static uintptr_t fgNextPIEDylibAddress; static uint32_t fgImagesWithUsedPrebinding; static uint32_t fgImagesUsedFromSharedCache; @@ -721,6 +772,8 @@ private: const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart, const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const; + void processInitializers(const LinkContext& context, mach_port_t this_thread, + InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& ups); recursive_lock* fInitializerRecursiveLock; diff --git a/src/ImageLoaderMachO.cpp b/src/ImageLoaderMachO.cpp index 1f636cb..53b0ddd 100644 --- a/src/ImageLoaderMachO.cpp +++ b/src/ImageLoaderMachO.cpp @@ -109,7 +109,7 @@ ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, uns // ignore zero-sized segments if ( segCmd->vmsize != 0 ) { // record offset of load command - segOffsets[segIndex++] = (uint8_t*)segCmd - fMachOData; + segOffsets[segIndex++] = (uint32_t)((uint8_t*)segCmd - fMachOData); } } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); @@ -138,6 +138,16 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds); const struct load_command* cmd = startCmds; for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize; + if ( cmdLength < 8 ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s", + i, cmdLength, path); + } + const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( (nextCmd > endCmds) || (nextCmd < cmd) ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", + i, cmdLength, mh->sizeofcmds, path); + } switch (cmd->cmd) { case LC_DYLD_INFO: case LC_DYLD_INFO_ONLY: @@ -160,6 +170,10 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat loadCommandSegmentIndex = i; loadCommandSegmentVMStart = segCmd->vmaddr; loadCommandSegmentVMEnd = segCmd->vmaddr + segCmd->vmsize; + if ( (intptr_t)(segCmd->vmsize) < 0) + dyld::throwf("malformed mach-o image: segment load command %s size too large", segCmd->segname); + if ( loadCommandSegmentVMEnd < loadCommandSegmentVMStart ) + dyld::throwf("malformed mach-o image: segment load command %s wraps around address space", segCmd->segname); } break; case LC_SEGMENT_COMMAND_WRONG: @@ -175,12 +189,7 @@ void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* pat *codeSigCmd = (struct linkedit_data_command*)cmd; // only support one LC_CODE_SIGNATURE per image break; } - uint32_t cmdLength = cmd->cmdsize; - cmd = (const struct load_command*)(((char*)cmd)+cmdLength); - if ( (cmd > endCmds) || (cmd < startCmds) ) { - dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", - i, cmdLength, mh->sizeofcmds, path); - } + cmd = nextCmd; } if ( context.codeSigningEnforced && !foundLoadCommandSegment ) @@ -337,7 +346,7 @@ void ImageLoaderMachO::parseLoadCmds() fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); #if TEXT_RELOC_SUPPORT // __TEXT segment always starts at beginning of file and contains mach_header and load commands - if ( strcmp(segName(i),"__TEXT") == 0 ) { + if ( segExecutable(i) ) { if ( segHasRebaseFixUps(i) && (fSlide != 0) ) fTextSegmentRebases = true; if ( segHasBindFixUps(i) ) @@ -419,9 +428,9 @@ void ImageLoaderMachO::parseLoadCmds() else if ( type == S_DTRACE_DOF ) fHasDOFSections = true; else if ( isTextSeg && (strcmp(sect->sectname, "__eh_frame") == 0) ) - fEHFrameSectionOffset = (uint8_t*)sect - fMachOData; + fEHFrameSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) ) - fUnwindInfoSectionOffset = (uint8_t*)sect - fMachOData;; + fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); } } break; @@ -430,7 +439,7 @@ void ImageLoaderMachO::parseLoadCmds() break; case LC_ID_DYLIB: { - fDylibIDOffset = (uint8_t*)cmd - fMachOData; + fDylibIDOffset = (uint32_t)((uint8_t*)cmd - fMachOData); } break; case LC_RPATH: @@ -582,6 +591,7 @@ uintptr_t ImageLoaderMachO::segActualEndAddress(unsigned int segIndex) const bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const { +#if TEXT_RELOC_SUPPORT // scan sections for fix-up bit const macho_segment_command* segCmd = segLoadCommand(segIndex); const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); @@ -590,11 +600,13 @@ bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const if ( (sect->flags & S_ATTR_LOC_RELOC) != 0 ) return true; } +#endif return false; } bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const { +#if TEXT_RELOC_SUPPORT // scan sections for fix-up bit const macho_segment_command* segCmd = segLoadCommand(segIndex); const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); @@ -603,6 +615,7 @@ bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const if ( (sect->flags & S_ATTR_EXT_RELOC) != 0 ) return true; } +#endif return false; } @@ -652,7 +665,7 @@ void ImageLoaderMachO::preFetchDATA(int fd, uint64_t offsetInFat, const LinkCont // prefetch writable segment that have mmap'ed regions radvisory advice; advice.ra_offset = offsetInFat + segFileOffset(i); - advice.ra_count = segFileSize(i); + advice.ra_count = (int)segFileSize(i); // limit prefetch to 1MB (256 pages) if ( advice.ra_count > 1024*1024 ) advice.ra_count = 1024*1024; @@ -830,14 +843,16 @@ void ImageLoaderMachO::registerInterposing() for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { if ( ((sect->flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sect->sectname, "__interpose") == 0) && (strcmp(seg->segname, "__DATA") == 0)) ) { const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide); - const unsigned int count = sect->size / sizeof(InterposeData); - for (uint32_t i=0; i < count; ++i) { + const size_t count = sect->size / sizeof(InterposeData); + for (size_t i=0; i < count; ++i) { ImageLoader::InterposeTuple tuple; tuple.replacement = interposeArray[i].replacement; - tuple.replacementImage = this; + tuple.neverImage = this; + tuple.onlyImage = NULL; tuple.replacee = interposeArray[i].replacee; // verify that replacement is in this image if ( this->containsAddress((void*)tuple.replacement) ) { + // chain to any existing interpositions for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { if ( it->replacee == tuple.replacee ) { tuple.replacee = it->replacement; @@ -873,6 +888,30 @@ uint32_t ImageLoaderMachO::sdkVersion() const return 0; } +uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct version_min_command* versCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + versCmd = (version_min_command*)cmd; + return versCmd->version; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +uint32_t ImageLoaderMachO::minOSVersion() const +{ + return ImageLoaderMachO::minOSVersion(machHeader()); +} + + void* ImageLoaderMachO::getThreadPC() const { const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; @@ -912,6 +951,9 @@ void* ImageLoaderMachO::getMain() const #elif __arm__ const arm_thread_state_t* registers = (arm_thread_state_t*)(((char*)cmd) + 16); void* entry = (void*)(registers->__pc + fSlide); + #elif __arm64__ + const arm_thread_state64_t* registers = (arm_thread_state64_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->__pc + fSlide); #else #warning need processor specific code #endif @@ -1034,7 +1076,7 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorpath.offset; - if ( strncmp(path, "@loader_path/", 13) == 0 ) { + if ( (strncmp(path, "@loader_path", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) { if ( context.processIsRestricted && (context.mainExecutable == this) ) { dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath()); break; @@ -1044,14 +1086,13 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorgetPath()); break; @@ -1061,11 +1102,10 @@ void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vectorncmds; @@ -1184,24 +1225,21 @@ void ImageLoaderMachO::doRebase(const LinkContext& context) #if TEXT_RELOC_SUPPORT void ImageLoaderMachO::makeTextSegmentWritable(const LinkContext& context, bool writeable) { - int textSegmentIndex = 0; for(unsigned int i=0; i < fSegmentsCount; ++i) { - if ( strcmp(segName(i), "__TEXT") == 0 ) { - textSegmentIndex = i; - break; + if ( segExecutable(i) ) { + if ( writeable ) { + segMakeWritable(i, context); + } + else { + #if !__i386__ && !__x86_64__ + // some processors require range to be invalidated before it is made executable + sys_icache_invalidate((void*)segActualLoadAddress(i), segSize(textSegmentIndex)); + #endif + segProtect(i, context); + } } } - if ( writeable ) { - segMakeWritable(textSegmentIndex, context); - } - else { - #if !__i386__ && !__x86_64__ - // some processors require range to be invalidated before it is made executable - sys_icache_invalidate((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); - #endif - segProtect(textSegmentIndex, context); - } } #endif @@ -1242,16 +1280,7 @@ uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoade { uintptr_t result = exportedSymbolAddress(context, sym, requestor, runResolver); // check for interposing overrides - for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { - // replace all references to 'replacee' with 'replacement' - if ( (result == it->replacee) && (requestor != it->replacementImage) ) { - if ( context.verboseInterposing ) { - dyld::log("dyld interposing: replace 0x%lX with 0x%lX in %s\n", - it->replacee, it->replacement, this->getPath()); - } - result = it->replacement; - } - } + result = interposedAddress(context, result, requestor); return result; } @@ -1390,11 +1419,13 @@ bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segme void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol, - const char* referencedFrom, const char* expectedIn) + const char* referencedFrom, const char* fromVersMismatch, + const char* expectedIn) { // record values for possible use by CrashReporter or Finder (*context.setErrorStrings)(dyld_error_kind_symbol_missing, referencedFrom, expectedIn, symbol); - dyld::throwf("Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n", symbol, referencedFrom, expectedIn); + dyld::throwf("Symbol not found: %s\n Referenced from: %s%s\n Expected in: %s\n", + symbol, referencedFrom, fromVersMismatch, expectedIn); } const mach_header* ImageLoaderMachO::machHeader() const @@ -1462,7 +1493,7 @@ uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t l break; case BIND_TYPE_TEXT_PCREL32: loc32 = (uint32_t*)locationToFix; - value32 = (uint32_t)newValue - (((uintptr_t)locationToFix) + 4); + value32 = (uint32_t)(newValue - (((uintptr_t)locationToFix) + 4)); if ( *loc32 != value32 ) *loc32 = value32; break; @@ -1488,7 +1519,7 @@ uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t l #endif struct DATAdyld { - void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper_interface + void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup // the following only exist in main executables built for 10.5 or later ProgramVars vars; @@ -1496,7 +1527,6 @@ struct DATAdyld { // These are defined in dyldStartup.s extern "C" void stub_binding_helper(); -extern "C" bool dyld_func_lookup(const char* name, uintptr_t* address); void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) @@ -1519,13 +1549,15 @@ void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { if ( strcmp(sect->sectname, "__dyld" ) == 0 ) { struct DATAdyld* dd = (struct DATAdyld*)(sect->addr + fSlide); + #if !__arm64__ if ( sect->size > offsetof(DATAdyld, dyldLazyBinder) ) { if ( dd->dyldLazyBinder != (void*)&stub_binding_helper ) dd->dyldLazyBinder = (void*)&stub_binding_helper; } + #endif // !__arm64__ if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) { - if ( dd->dyldFuncLookup != (void*)&dyld_func_lookup ) - dd->dyldFuncLookup = (void*)&dyld_func_lookup; + if ( dd->dyldFuncLookup != (void*)&_dyld_func_lookup ) + dd->dyldFuncLookup = (void*)&_dyld_func_lookup; } if ( mh->filetype == MH_EXECUTE ) { // there are two ways to get the program variables @@ -1670,8 +1702,8 @@ void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) const uint8_t type = sect->flags & SECTION_TYPE; if ( type == S_MOD_INIT_FUNC_POINTERS ) { Initializer* inits = (Initializer*)(sect->addr + fSlide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=0; i < count; ++i) { + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t i=0; i < count; ++i) { Initializer func = inits[i]; // verify initializers are in image if ( ! this->containsAddress((void*)func) ) { @@ -1766,8 +1798,8 @@ void ImageLoaderMachO::doTermination(const LinkContext& context) const uint8_t type = sect->flags & SECTION_TYPE; if ( type == S_MOD_TERM_FUNC_POINTERS ) { Terminator* terms = (Terminator*)(sect->addr + fSlide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=count; i > 0; --i) { + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t i=count; i > 0; --i) { Terminator func = terms[i-1]; // verify terminators are in image if ( ! this->containsAddress((void*)func) ) { @@ -1808,7 +1840,13 @@ intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) uintptr_t highAddr = 0; for(unsigned int i=0, e=segmentCount(); i < e; ++i) { const uintptr_t segLow = segPreferredLoadAddress(i); - const uintptr_t segHigh = (segLow + segSize(i) + 4095) & -4096; + const uintptr_t segHigh = dyld_page_round(segLow + segSize(i)); + if ( segLow < highAddr ) { + if ( dyld_page_size > 4096 ) + dyld::throwf("can't map segments into 16KB pages"); + else + dyld::throwf("overlapping segments"); + } if ( segLow < lowAddr ) lowAddr = segLow; if ( segHigh > highAddr ) @@ -1845,7 +1883,7 @@ uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoad // in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either if ( fgNextPIEDylibAddress != 0 ) { // add small (0-3 pages) random padding between dylibs - addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*4096; + addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*dyld_page_size; //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard)); kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); if ( r == KERN_SUCCESS ) { @@ -1877,8 +1915,12 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF { // find address range for image intptr_t slide = this->assignSegmentAddresses(context); - if ( context.verboseMapping ) - dyld::log("dyld: Mapping %s\n", this->getPath()); + if ( context.verboseMapping ) { + if ( offsetInFat != 0 ) + dyld::log("dyld: Mapping %s (slice offset=%llu)\n", this->getPath(), (unsigned long long)offsetInFat); + else + dyld::log("dyld: Mapping %s\n", this->getPath()); + } // map in all segments for(unsigned int i=0, e=segmentCount(); i < e; ++i) { vm_offset_t fileOffset = segFileOffset(i) + offsetInFat; @@ -1888,7 +1930,6 @@ void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInF if ( !segUnaccessible(i) ) { // If has text-relocs, don't set x-bit initially. // Instead set it later after text-relocs have been done. - // The iPhone OS does not like it when you make executable code writable. if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) ) protection |= PROT_EXEC; if ( segReadable(i) ) diff --git a/src/ImageLoaderMachO.h b/src/ImageLoaderMachO.h index 6d5c744..4505d63 100644 --- a/src/ImageLoaderMachO.h +++ b/src/ImageLoaderMachO.h @@ -101,9 +101,11 @@ public: virtual uintptr_t segActualEndAddress(unsigned int) const; virtual void registerInterposing(); virtual uint32_t sdkVersion() const; + virtual uint32_t minOSVersion() const; static void printStatistics(unsigned int imageCount, const InitializerTimingList&); + static uint32_t minOSVersion(const mach_header*); protected: ImageLoaderMachO(const ImageLoaderMachO&); @@ -176,7 +178,8 @@ protected: void mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context); void UnmapSegments(); void __attribute__((noreturn)) throwSymbolNotFound(const LinkContext& context, const char* symbol, - const char* referencedFrom, const char* expectedIn); + const char* referencedFrom, const char* fromVersMismatch, + const char* expectedIn); void doImageInit(const LinkContext& context); void doModInitFunctions(const LinkContext& context); void setupLazyPointerHandler(const LinkContext& context); diff --git a/src/ImageLoaderMachOClassic.cpp b/src/ImageLoaderMachOClassic.cpp index 3eed9da..8190f1d 100644 --- a/src/ImageLoaderMachOClassic.cpp +++ b/src/ImageLoaderMachOClassic.cpp @@ -55,7 +55,7 @@ #include "mach-o/dyld_images.h" // in dyldStartup.s -extern "C" void fast_stub_binding_helper_interface(); +extern "C" void stub_binding_helper_i386_old(); #if __x86_64__ @@ -352,11 +352,11 @@ void ImageLoaderMachOClassic::prefetchLINKEDIT(const LinkContext& context) end += fSymbolTable[fDynamicInfo->iextdefsym+fDynamicInfo->nextdefsym-1].n_un.n_strx; // round to whole pages - start = start & (-4096); - end = (end + 4095) & (-4096); + start = dyld_page_trunc(start); + end = dyld_page_round(end); // skip if there is only one page - if ( (end-start) > 4096 ) { + if ( (end-start) > dyld_page_size ) { madvise((void*)start, end-start, MADV_WILLNEED); fgTotalBytesPreFetched += (end-start); if ( context.verboseMapping ) { @@ -619,7 +619,7 @@ bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const Im const char* lastSlash = strrchr(childInstallPath, '/'); if ( lastSlash != NULL ) { const char* firstDot = strchr(lastSlash, '.'); - int len; + size_t len; if ( firstDot == NULL ) len = strlen(lastSlash); else @@ -1071,7 +1071,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, // if reference is weak_import, then it is ok, just return 0 return 0; } - throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace"); + throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); } else { // symbol requires searching images with coalesced symbols (not done during prebinding) @@ -1116,7 +1116,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, if ( context.flatExportFinder(symbolName, &sym, foundIn) ) return (*foundIn)->getExportedSymbolAddress(sym, context, this); - throwSymbolNotFound(context, symbolName, this->getPath(), "dynamic lookup"); + throwSymbolNotFound(context, symbolName, this->getPath(), "", "dynamic lookup"); } else if ( ord <= libraryCount() ) { target = libImage(ord-1); @@ -1150,7 +1150,7 @@ uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, } // nowhere to be found - throwSymbolNotFound(context, symbolName, this->getPath(), target->getPath()); + throwSymbolNotFound(context, symbolName, this->getPath(), "", target->getPath()); } } @@ -1402,11 +1402,11 @@ uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, cons const uint8_t type = sect->flags & SECTION_TYPE; uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; if ( type == S_LAZY_SYMBOL_POINTERS ) { - const uint32_t pointerCount = sect->size / sizeof(uintptr_t); + const size_t pointerCount = sect->size / sizeof(uintptr_t); uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { const uint32_t indirectTableOffset = sect->reserved1; - const uint32_t lazyIndex = lazyPointer - symbolPointers; + const size_t lazyIndex = lazyPointer - symbolPointers; symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; } } @@ -1519,7 +1519,7 @@ uintptr_t ImageLoaderMachOClassic::getAddressCoalIterator(CoalIterator& it, cons symbol_index = toc[it.curIndex-1].symbol_index; } else { - symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1; + symbol_index = fDynamicInfo->iextdefsym + (uint32_t)it.curIndex - 1; } const struct macho_nlist* sym = &fSymbolTable[symbol_index]; //dyld::log("getAddressCoalIterator() => 0x%llX, %s symbol_index=%d, in %s\n", (uint64_t)(sym->n_value + fSlide), &fStrings[sym->n_un.n_strx], symbol_index, this->getPath()); @@ -1554,7 +1554,7 @@ void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t symbol_index = toc[it.curIndex-1].symbol_index; } else { - symbol_index = fDynamicInfo->iextdefsym+it.curIndex-1; + symbol_index = fDynamicInfo->iextdefsym + (uint32_t)it.curIndex - 1; } // if this image's copy of the symbol is not a weak definition nor a weak reference then nothing to coalesce here @@ -1664,11 +1664,11 @@ void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t case S_NON_LAZY_SYMBOL_POINTERS: case S_LAZY_SYMBOL_POINTERS: { - uint32_t elementCount = sect->size / elementSize; + size_t elementCount = sect->size / elementSize; const uint32_t indirectTableOffset = sect->reserved1; uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); //dyld::log(" scanning section %s of %s starting at %p\n", sect->sectname, this->getShortName(), ptrToBind); - for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + for (size_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { if ( indirectTable[indirectTableOffset + j] == symbol_index ) { //dyld::log(" found symbol index match at %d/%d, ptrToBind=%p\n", j, elementCount, ptrToBind); // update pointer @@ -1711,7 +1711,7 @@ void ImageLoaderMachOClassic::bindIndirectSymbolPointers(const LinkContext& cont bool isLazySymbol = false; const uint8_t type = sect->flags & SECTION_TYPE; uint32_t elementSize = sizeof(uintptr_t); - uint32_t elementCount = sect->size / elementSize; + size_t elementCount = sect->size / elementSize; if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { if ( ! bindNonLazys ) continue; @@ -1739,7 +1739,7 @@ void ImageLoaderMachOClassic::bindIndirectSymbolPointers(const LinkContext& cont } const uint32_t indirectTableOffset = sect->reserved1; uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); - for (uint32_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + for (size_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { #if LINKEDIT_USAGE_DEBUG noteAccessedLinkEditAddress(&indirectTable[indirectTableOffset + j]); #endif @@ -1832,7 +1832,7 @@ void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context) const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); uint8_t* end = start + sect->size; - uintptr_t dyldHandler = (uintptr_t)&fast_stub_binding_helper_interface; + uintptr_t dyldHandler = (uintptr_t)&stub_binding_helper_i386_old; uint32_t entryIndex = 0; for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) { bool installLazyHandler = true; @@ -1950,19 +1950,12 @@ void ImageLoaderMachOClassic::doInterpose(const LinkContext& context) for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { const uint8_t type = sect->flags & SECTION_TYPE; if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) { - const uint32_t pointerCount = sect->size / sizeof(uintptr_t); + const size_t pointerCount = sect->size / sizeof(uintptr_t); uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); - for (uint32_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { - for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { - // replace all references to 'replacee' with 'replacement' - if ( (symbolPointers[pointerIndex] == it->replacee) && (this != it->replacementImage) ) { - if ( context.verboseInterposing ) { - dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", - &symbolPointers[pointerIndex], it->replacee, it->replacement, this->getPath()); - } - symbolPointers[pointerIndex] = it->replacement; - } - } + for (size_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { + uintptr_t newValue = interposedAddress(context, symbolPointers[pointerIndex], this); + if ( newValue != symbolPointers[pointerIndex] ) + symbolPointers[pointerIndex] = newValue; } } #if __i386__ @@ -1975,16 +1968,10 @@ void ImageLoaderMachOClassic::doInterpose(const LinkContext& context) if ( entry[0] == 0xE9 ) { // 0xE9 == JMP uint32_t rel32 = *((uint32_t*)&entry[1]); // assume unaligned load of uint32_t is ok uint32_t target = (uint32_t)&entry[5] + rel32; - for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { - // replace all references to 'replacee' with 'replacement' - if ( (it->replacee == target) && (this != it->replacementImage) ) { - if ( context.verboseInterposing ) { - dyld::log("dyld: interposing: at %p replace JMP 0x%lX with JMP 0x%lX in %s\n", - &entry[1], it->replacee, it->replacement, this->getPath()); - } - uint32_t newRel32 = it->replacement - (uint32_t)&entry[5]; - *((uint32_t*)&entry[1]) = newRel32; // assume unaligned store of uint32_t is ok - } + uint32_t newTarget = interposedAddress(context, target, this); + if ( newTarget != target ) { + uint32_t newRel32 = newTarget - (uint32_t)&entry[5]; + *((uint32_t*)&entry[1]) = newRel32; // assume unaligned store of uint32_t is ok } } } @@ -2007,14 +1994,76 @@ void ImageLoaderMachOClassic::doInterpose(const LinkContext& context) case POINTER_RELOC: { uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); - for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + uintptr_t value = *location; + uintptr_t newValue = interposedAddress(context, value, this); + if ( newValue != value ) + *location = newValue; + } + break; + } + } + } +} + +void ImageLoaderMachOClassic::dynamicInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); + + // scan indirect symbols + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + for (size_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { + for(size_t i=0; i < context.dynamicInterposeCount; ++i) { + // replace all references to 'replacee' with 'replacement' + if ( symbolPointers[pointerIndex] == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + &symbolPointers[pointerIndex], context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); + } + symbolPointers[pointerIndex] = (uintptr_t)context.dynamicInterposeArray[i].replacement; + } + } + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // scan external relocations + const uintptr_t relocBase = this->getRelocBase(); + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case POINTER_RELOC: + { + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + for(size_t i=0; i < context.dynamicInterposeCount; ++i) { // replace all references to 'replacee' with 'replacement' - if ( (*location == it->replacee) && (this != it->replacementImage) ) { + if ( *location == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { if ( context.verboseInterposing ) { - dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", - location, it->replacee, it->replacement, this->getPath()); + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + location, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); } - *location = it->replacement; + *location = (uintptr_t)context.dynamicInterposeArray[i].replacement; } } } diff --git a/src/ImageLoaderMachOClassic.h b/src/ImageLoaderMachOClassic.h index 55685a5..88db64c 100644 --- a/src/ImageLoaderMachOClassic.h +++ b/src/ImageLoaderMachOClassic.h @@ -66,6 +66,7 @@ public: protected: virtual void doInterpose(const LinkContext& context); + virtual void dynamicInterpose(const LinkContext& context); virtual void setDyldInfo(const dyld_info_command*) {} virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*); virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const; diff --git a/src/ImageLoaderMachOCompressed.cpp b/src/ImageLoaderMachOCompressed.cpp index 5ba89ec..6a3a535 100644 --- a/src/ImageLoaderMachOCompressed.cpp +++ b/src/ImageLoaderMachOCompressed.cpp @@ -374,11 +374,11 @@ void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int ad // round to whole pages - start = start & (-4096); - end = (end + 4095) & (-4096); + start = dyld_page_trunc(start); + end = dyld_page_round(end); // do nothing if only one page of rebase/bind info - if ( (end-start) <= 4096 ) + if ( (end-start) <= dyld_page_size ) return; // tell kernel about our access to these pages @@ -433,8 +433,8 @@ void ImageLoaderMachOCompressed::rebase(const LinkContext& context) int segmentIndex = 0; uintptr_t address = segActualLoadAddress(0); uintptr_t segmentEndAddress = segActualEndAddress(0); - uint32_t count; - uint32_t skip; + uintptr_t count; + uintptr_t skip; bool done = false; while ( !done && (p < end) ) { uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; @@ -449,9 +449,9 @@ void ImageLoaderMachOCompressed::rebase(const LinkContext& context) break; case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segmentIndex = immediate; - if ( segmentIndex > fSegmentsCount ) - dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", - segmentIndex, fSegmentsCount); + if ( segmentIndex >= fSegmentsCount ) + dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + segmentIndex, fSegmentsCount-1); address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); segmentEndAddress = segActualEndAddress(segmentIndex); break; @@ -519,7 +519,7 @@ const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const { const uint8_t* p = start; while ( p != NULL ) { - uint32_t terminalSize = *p++; + uintptr_t terminalSize = *p++; if ( terminalSize > 127 ) { // except for re-export-with-rename, all terminal sizes fit in one byte --p; @@ -533,7 +533,7 @@ const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const //dyld::log("trieWalk(%p) sym=%s, terminalSize=%d, children=%p\n", start, s, terminalSize, children); uint8_t childrenRemaining = *children++; p = children; - uint32_t nodeOffset = 0; + uintptr_t nodeOffset = 0; for (; childrenRemaining > 0; --childrenRemaining) { const char* ss = s; //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p); @@ -592,16 +592,16 @@ const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol); if ( foundNodeStart != NULL ) { const uint8_t* p = foundNodeStart; - const uint32_t flags = read_uleb128(p, end); + const uintptr_t flags = read_uleb128(p, end); // found match, return pointer to terminal part of node if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { // re-export from another dylib, lookup there - const uint32_t ordinal = read_uleb128(p, end); + const uintptr_t ordinal = read_uleb128(p, end); const char* importedName = (char*)p; if ( importedName[0] == '\0' ) importedName = symbol; if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { - const ImageLoader* reexportedFrom = libImage(ordinal-1); + const ImageLoader* reexportedFrom = libImage((unsigned int)ordinal-1); //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName); return reexportedFrom->findExportedSymbol(importedName, true, foundIn); } @@ -638,23 +638,17 @@ uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& c if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) throw "symbol is not in trie"; //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName()); - uint32_t flags = read_uleb128(exportNode, exportTrieEnd); + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); 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; - } - } + uintptr_t interposedStub = interposedAddress(context, stub, requestor); + if ( interposedStub != stub ) + return interposedStub; + // stub was not interposed, so run resolver typedef uintptr_t (*ResolverProc)(void); ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); uintptr_t result = (*resolver)(); @@ -665,14 +659,14 @@ uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& c 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); + dyld::throwf("unsupported exported symbol kind. flags=%lu 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); + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); return read_uleb128(exportNode, exportTrieEnd); default: - dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol); + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); } } @@ -683,7 +677,7 @@ bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* sym const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) throw "symbol is not in trie"; - uint32_t flags = read_uleb128(exportNode, exportTrieEnd); + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); } @@ -740,7 +734,7 @@ uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, co // definition can't be found anywhere, ok because it is weak, just return 0 return 0; } - throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace"); + throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); } @@ -758,13 +752,29 @@ uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context return 0; } - // nowhere to be found - throwSymbolNotFound(context, symbolName, this->getPath(), targetImage->getPath()); + // nowhere to be found, check if maybe this image is too new for this OS + char versMismatch[256]; + versMismatch[0] = '\0'; + uint32_t imageMinOS = this->minOSVersion(); + // dyld is always built for the current OS, so we can get the current OS version + // from the load command in dyld itself. + extern const mach_header __dso_handle; + uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); + if ( imageMinOS > dyldMinOS ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); +#else + const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); +#endif + strcpy(versMismatch, msg); + ::free((void*)msg); + } + throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, targetImage->getPath()); } uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, - uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage, + uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, LastLookup* last, bool runResolver) { *targetImage = NULL; @@ -792,14 +802,14 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const *targetImage = this; } else if ( libraryOrdinal <= 0 ) { - dyld::throwf("bad mach-o binary, unknown special library ordinal (%u) too big for symbol %s in %s", + dyld::throwf("bad mach-o binary, unknown special library ordinal (%ld) too big for symbol %s in %s", libraryOrdinal, symbolName, this->getPath()); } else if ( (unsigned)libraryOrdinal <= libraryCount() ) { - *targetImage = libImage(libraryOrdinal-1); + *targetImage = libImage((unsigned int)libraryOrdinal-1); } else { - dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", + dyld::throwf("bad mach-o binary, library ordinal (%ld) too big (max %u) for symbol %s in %s", libraryOrdinal, libraryCount(), symbolName, this->getPath()); } if ( *targetImage == NULL ) { @@ -808,7 +818,7 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const symbolAddress = 0; } else { - dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%d could not be loaded", + dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", symbolName, this->getPath(), libraryOrdinal); } } @@ -830,7 +840,7 @@ uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const } uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, - uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, + uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, LastLookup* last, bool runResolver) { const ImageLoader* targetImage; @@ -915,10 +925,10 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl uintptr_t segmentEndAddress = segActualEndAddress(0); const char* symbolName = NULL; uint8_t symboFlags = 0; - int libraryOrdinal = 0; + long libraryOrdinal = 0; intptr_t addend = 0; - uint32_t count; - uint32_t skip; + uintptr_t count; + uintptr_t skip; LastLookup last = { 0, 0, NULL, 0, NULL }; const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; const uint8_t* const end = &start[fDyldInfo->bind_size]; @@ -962,9 +972,9 @@ void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handl break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segmentIndex = immediate; - if ( segmentIndex > fSegmentsCount ) - dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", - segmentIndex, fSegmentsCount); + if ( segmentIndex >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + segmentIndex, fSegmentsCount-1); address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); segmentEndAddress = segActualEndAddress(segmentIndex); break; @@ -1020,7 +1030,7 @@ void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_h uintptr_t segmentEndAddress = segActualEndAddress(0); const char* symbolName = NULL; uint8_t symboFlags = 0; - int libraryOrdinal = 0; + long libraryOrdinal = 0; intptr_t addend = 0; const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; @@ -1064,9 +1074,9 @@ void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_h break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: segmentIndex = immediate; - if ( segmentIndex > fSegmentsCount ) - dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", - segmentIndex, fSegmentsCount); + if ( segmentIndex >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + segmentIndex, fSegmentsCount-1); address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end); segmentEndAddress = segActualEndAddress(segmentIndex); break; @@ -1140,11 +1150,11 @@ uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, c const uint8_t type = sect->flags & SECTION_TYPE; uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; if ( type == S_LAZY_SYMBOL_POINTERS ) { - const uint32_t pointerCount = sect->size / sizeof(uintptr_t); + const size_t pointerCount = sect->size / sizeof(uintptr_t); uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { const uint32_t indirectTableOffset = sect->reserved1; - const uint32_t lazyIndex = lazyPointer - symbolPointers; + const size_t lazyIndex = lazyPointer - symbolPointers; symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; } } @@ -1193,7 +1203,7 @@ uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingI uintptr_t address = 0; const char* symbolName = NULL; uint8_t symboFlags = 0; - int libraryOrdinal = 0; + long libraryOrdinal = 0; bool done = false; uintptr_t result = 0; const uint8_t* p = &start[lazyBindingInfoOffset]; @@ -1231,9 +1241,9 @@ uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingI type = immediate; break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( immediate > fSegmentsCount ) - dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", - immediate, fSegmentsCount); + if ( immediate >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + immediate, fSegmentsCount-1); address = segActualLoadAddress(immediate) + read_uleb128(p, end); break; case BIND_OPCODE_DO_BIND: @@ -1289,8 +1299,8 @@ bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; const uint8_t* p = start + it.curIndex; const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; - uint32_t count; - uint32_t skip; + uintptr_t count; + uintptr_t skip; while ( p < end ) { uint8_t immediate = *p & BIND_IMMEDIATE_MASK; uint8_t opcode = *p & BIND_OPCODE_MASK; @@ -1317,9 +1327,9 @@ bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) it.addend = read_sleb128(p, end); break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( immediate > fSegmentsCount ) - dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", - immediate, fSegmentsCount); + if ( immediate >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + immediate, fSegmentsCount-1); it.address = segActualLoadAddress(immediate) + read_uleb128(p, end); break; case BIND_OPCODE_ADD_ADDR_ULEB: @@ -1379,8 +1389,8 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt uintptr_t address = it.address; const char* symbolName = it.symbolName; intptr_t addend = it.addend; - uint32_t count; - uint32_t skip; + uintptr_t count; + uintptr_t skip; bool done = false; bool boundSomething = false; while ( !done && (p < end) ) { @@ -1401,9 +1411,9 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt addend = read_sleb128(p, end); break; case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: - if ( immediate > fSegmentsCount ) - dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (%d)\n", - immediate, fSegmentsCount); + if ( immediate >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + immediate, fSegmentsCount-1); address = segActualLoadAddress(immediate) + read_uleb128(p, end); break; case BIND_OPCODE_ADD_ADDR_ULEB: @@ -1443,21 +1453,14 @@ void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintpt } uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, - uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver) + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) { if ( type == BIND_TYPE_POINTER ) { uintptr_t* fixupLocation = (uintptr_t*)addr; - uintptr_t value = *fixupLocation; - for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { - // replace all references to 'replacee' with 'replacement' - if ( (value == it->replacee) && (this != it->replacementImage) ) { - if ( context.verboseInterposing ) { - dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n", - fixupLocation, it->replacee, it->replacement, this->getPath()); - } - *fixupLocation = it->replacement; - } - } + uintptr_t curValue = *fixupLocation; + uintptr_t newValue = interposedAddress(context, curValue, this); + if ( newValue != curValue) + *fixupLocation = newValue; } return 0; } @@ -1473,6 +1476,37 @@ void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context) } +uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) +{ + if ( type == BIND_TYPE_POINTER ) { + uintptr_t* fixupLocation = (uintptr_t*)addr; + uintptr_t value = *fixupLocation; + // don't apply interposing to table entries. + if ( (context.dynamicInterposeArray <= (void*)addr) && ((void*)addr < &context.dynamicInterposeArray[context.dynamicInterposeCount]) ) + return 0; + for(size_t i=0; i < context.dynamicInterposeCount; ++i) { + if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); + } + *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement; + } + } + } + return 0; +} + +void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); + + // update already bound references to symbols + eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); + eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); +} const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const diff --git a/src/ImageLoaderMachOCompressed.h b/src/ImageLoaderMachOCompressed.h index 6d8c5b7..1a43900 100644 --- a/src/ImageLoaderMachOCompressed.h +++ b/src/ImageLoaderMachOCompressed.h @@ -68,6 +68,7 @@ public: protected: virtual void doInterpose(const LinkContext& context); + virtual void dynamicInterpose(const LinkContext& context); virtual void setDyldInfo(const dyld_info_command* dyldInfo) { fDyldInfo = dyldInfo; } virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) {} virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; } @@ -90,11 +91,11 @@ protected: private: - struct LastLookup { int ordinal; uint8_t flags; const char* name; uintptr_t result; const ImageLoader* foundIn; }; + struct LastLookup { long ordinal; uint8_t flags; const char* name; uintptr_t result; const ImageLoader* foundIn; }; typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type, - const char* symbolName, uint8_t symboFlags, intptr_t addend, int libraryOrdinal, + const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, LastLookup* last, bool runResolver); void eachLazyBind(const LinkContext& context, bind_handler); @@ -113,13 +114,13 @@ private: void throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); uintptr_t bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, - uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, + uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, LastLookup* last, bool runResolver=false); void bindCompressed(const LinkContext& context); void throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); uintptr_t resolve(const LinkContext& context, const char* symbolName, - uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage, + uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, LastLookup* last = NULL, bool runResolver=false); uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver, const ImageLoader** foundIn); @@ -127,7 +128,9 @@ private: uintptr_t resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import, const char* symbolName, bool runResolver, const ImageLoader** foundIn); uintptr_t interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, - uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver); + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver); + uintptr_t dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver); static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* s); void updateOptimizedLazyPointers(const LinkContext& context); void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context); diff --git a/src/dyld.cpp b/src/dyld.cpp index d143bc9..23e482f 100644 --- a/src/dyld.cpp +++ b/src/dyld.cpp @@ -1,6 +1,6 @@ /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- * - * Copyright (c) 2004-2010 Apple Inc. All rights reserved. + * Copyright (c) 2004-2013 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -51,6 +51,7 @@ #include #include #include <_simple.h> +#include #ifndef CPU_SUBTYPE_ARM_V5TEJ @@ -75,6 +76,10 @@ #define LC_DYLD_ENVIRONMENT 0x27 #endif +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif + #ifndef VM_PROT_SLIDE #define VM_PROT_SLIDE 0x20 #endif @@ -92,16 +97,19 @@ #if DYLD_SHARED_CACHE_SUPPORT #include "dyld_cache_format.h" #endif -#if CORESYMBOLICATION_SUPPORT -#include "coreSymbolicationDyldSupport.hpp" +#if TARGET_IPHONE_SIMULATOR + void coresymbolication_load_image(void*, const ImageLoader*, uint64_t); + void coresymbolication_unload_image(void*, const ImageLoader*); +#else + #include "coreSymbolicationDyldSupport.hpp" #endif // not libc header for send() syscall interface extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); -// ARM is the only architecture that use cpu-sub-types -#define CPU_SUBTYPES_SUPPORTED __arm__ +// ARM and x86_64 are the only architecture that use cpu-sub-types +#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR) @@ -123,7 +131,7 @@ extern "C" { extern "C" void dyld_fatal_error(const char* errString) __attribute__((noreturn)); // magic linker symbol for start of dyld binary -extern "C" void* __dso_handle; +extern "C" const macho_header __dso_handle; // @@ -212,13 +220,14 @@ static cpu_subtype_t sHostCPUsubtype; static ImageLoader* sMainExecutable = NULL; static bool sProcessIsRestricted = false; static RestrictedReason sRestrictedReason = restrictedNot; -static unsigned int sInsertedDylibCount = 0; +static size_t sInsertedDylibCount = 0; static std::vector sAllImages; static std::vector sImageRoots; static std::vector sImageFilesNeedingTermination; static std::vector sImageFilesNeedingDOFUnregistration; static std::vector sAddImageCallbacks; static std::vector sRemoveImageCallbacks; +static bool sRemoveImageCallbacksInUse = false; static void* sSingleHandlers[7][3]; static void* sBatchHandlers[7][3]; static ImageLoader* sLastImageByAddressCache; @@ -231,10 +240,11 @@ 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 && DYLD_SHARED_CACHE_SUPPORT - bool gSharedCacheOverridden = false; + bool gSharedCacheOverridden = false; +#if __IPHONE_OS_VERSION_MIN_REQUIRED static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR; static bool sDylibsOverrideCache = false; + #define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024 #else static const char* sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR; #endif @@ -250,9 +260,12 @@ static std::vector sDylibOverrides; static int sLogSocket = -1; #endif static bool sFrameworksFoundAsDylibs = false; - +#if __x86_64__ +static bool sHaswell = false; +#endif static std::vector sDynamicReferences; - +static bool sLogToFile = false; +static char sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries"; // // The MappedRanges structure is used for fast address->image lookups. @@ -368,7 +381,6 @@ void throwf(const char* format, ...) } -//#define ALTERNATIVE_LOGFILE "/dev/console" #if !TARGET_IPHONE_SIMULATOR static int sLogfile = STDERR_FILENO; #endif @@ -456,10 +468,11 @@ static void socket_syslogv(int priority, const char* format, va_list list) void vlog(const char* format, va_list list) { - if ( useSyslog() ) + if ( !sLogToFile && useSyslog() ) socket_syslogv(LOG_ERR, format, list); - else + else { _simple_vdprintf(sLogfile, format, list); + } } void log(const char* format, ...) @@ -491,22 +504,32 @@ void warn(const char* format, ...) // control access to sAllImages through a lock // because global dyld lock is not held during initialization phase of dlopen() -static long sAllImagesLock = 0; +// Use OSSpinLockLock to allow yielding +static OSSpinLock sAllImagesLock = 0; static void allImagesLock() { //dyld::log("allImagesLock()\n"); +#if TARGET_IPHONE_SIMULATOR + // can't use OSSpinLockLock in simulator until thread_switch is provided by host dyld while ( ! OSAtomicCompareAndSwapPtrBarrier((void*)0, (void*)1, (void**)&sAllImagesLock) ) { // spin } +#else + OSSpinLockLock(&sAllImagesLock); +#endif } static void allImagesUnlock() { //dyld::log("allImagesUnlock()\n"); +#if TARGET_IPHONE_SIMULATOR while ( ! OSAtomicCompareAndSwapPtrBarrier((void*)1, (void*)0, (void**)&sAllImagesLock) ) { - // spin - } + // spin + } +#else + OSSpinLockUnlock(&sAllImagesLock); +#endif } @@ -537,7 +560,7 @@ FileOpener::~FileOpener() static void registerDOFs(const std::vector& dofs) { - const unsigned int dofSectionCount = dofs.size(); + const size_t dofSectionCount = dofs.size(); if ( !sEnv.DYLD_DISABLE_DOFS && (dofSectionCount != 0) ) { int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); if ( fd < 0 ) { @@ -573,7 +596,7 @@ static void registerDOFs(const std::vector& dofs) } } else { - dyld::log( "dyld: ioctl to register dtrace DOF section failed\n"); + //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n"); } close(fd); } @@ -676,20 +699,23 @@ static void notifySingle(dyld_image_states state, const ImageLoader* image) } } } -#if CORESYMBOLICATION_SUPPORT // mach message csdlc about dynamically loaded images if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) { if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { dyld::log("dyld core symbolication unload notification: %p %s\n", image->machHeader(), image->getPath()); } if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) { +#if TARGET_IPHONE_SIMULATOR + void* connection = dyld::gProcessInfo->coreSymbolicationShmPage; + if ( *((uint32_t*)connection) == 2 ) { +#else CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld::gProcessInfo->coreSymbolicationShmPage; if ( connection->is_valid_version() ) { +#endif coresymbolication_unload_image(connection, image); } } } -#endif } @@ -759,7 +785,7 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image *end++ = sBundleBeingLoaded; } const char* dontLoadReason = NULL; - unsigned int count = end-images; + uint32_t count = (uint32_t)(end-images); if ( end != images ) { // sort bottom up qsort(images, count, sizeof(ImageLoader*), &imageSorter); @@ -802,7 +828,6 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image if ( dontLoadReason != NULL ) throw dontLoadReason; } -#if CORESYMBOLICATION_SUPPORT if ( state == dyld_image_state_rebased ) { if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { @@ -812,8 +837,13 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image } } if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) { +#if TARGET_IPHONE_SIMULATOR + void* connection = dyld::gProcessInfo->coreSymbolicationShmPage; + if ( *((uint32_t*)connection) == 2 ) { +#else CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld::gProcessInfo->coreSymbolicationShmPage; if ( connection->is_valid_version() ) { +#endif // This needs to be captured now uint64_t load_timestamp = mach_absolute_time(); for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { @@ -824,7 +854,6 @@ static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image } } } -#endif } @@ -861,7 +890,7 @@ static void printAllDepths() static unsigned int imageCount() { - return sAllImages.size(); + return (unsigned int)sAllImages.size(); } @@ -906,7 +935,7 @@ static void addDynamicReference(ImageLoader* from, ImageLoader* to) { t.to = to; sDynamicReferences.push_back(t); } - + static void addImage(ImageLoader* image) { // add to master list @@ -976,9 +1005,11 @@ void removeImage(ImageLoader* image) // tell all registered remove image handlers about this // do this before removing image from internal data structures so that the callback can query dyld about the image if ( image->getState() >= dyld_image_state_bound ) { + sRemoveImageCallbacksInUse = true; // This only runs inside dyld's global lock, so ok to use a global for the in-use flag. for (std::vector::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) { (*it)(image->machHeader(), image->getSlide()); } + sRemoveImageCallbacksInUse = false; } // notify @@ -1022,7 +1053,7 @@ void removeImage(ImageLoader* image) } -void runImageTerminators(ImageLoader* image) +void runImageStaticTerminators(ImageLoader* image) { // if in termination list, pull it out and run terminator bool mightBeMore; @@ -1031,17 +1062,13 @@ void runImageTerminators(ImageLoader* image) for (std::vector::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) { if ( *it == image ) { sImageFilesNeedingTermination.erase(it); + if (gLogAPIs) dyld::log("dlclose(), running static terminators for %p %s\n", image, image->getShortName()); image->doTermination(gLinkContext); mightBeMore = true; break; } } } while ( mightBeMore ); - - // dyld should directly call __cxa_finalize() - if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 8) ) - (*gLibSystemHelpers->cxa_finalize)(image->machHeader()); - } static void terminationRecorder(ImageLoader* image) @@ -1054,6 +1081,21 @@ const char* getExecutablePath() return sExecPath; } +static void runAllStaticTerminators(void* extra) +{ + try { + const size_t imageCount = sImageFilesNeedingTermination.size(); + for(size_t i=imageCount; i > 0; --i){ + ImageLoader* image = sImageFilesNeedingTermination[i-1]; + image->doTermination(gLinkContext); + } + sImageFilesNeedingTermination.clear(); + notifyBatch(dyld_image_state_terminated); + } + catch (const char* msg) { + halt(msg); + } +} void initializeMainExecutable() { @@ -1062,25 +1104,24 @@ void initializeMainExecutable() // run initialzers for any inserted dylibs ImageLoader::InitializerTimingList initializerTimes[sAllImages.size()]; - const int rootCount = sImageRoots.size(); + initializerTimes[0].count = 0; + const size_t rootCount = sImageRoots.size(); if ( rootCount > 1 ) { - for(int i=1; i < rootCount; ++i) { - initializerTimes[0].count = 0; + for(size_t i=1; i < rootCount; ++i) { sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); } } // run initializers for main executable and everything it brings up - initializerTimes[0].count = 0; sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); - // register atexit() handler to run terminators in all loaded images when this process exits + // register cxa_atexit() handler to run static terminators in all loaded images when this process exits if ( gLibSystemHelpers != NULL ) - (*gLibSystemHelpers->cxa_atexit)(&runTerminators, NULL, NULL); + (*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL); // dump info if requested if ( sEnv.DYLD_PRINT_STATISTICS ) - ImageLoaderMachO::printStatistics(sAllImages.size(), initializerTimes[0]); + ImageLoaderMachO::printStatistics((unsigned int)sAllImages.size(), initializerTimes[0]); } bool mainExecutablePrebound() @@ -1094,21 +1135,6 @@ ImageLoader* mainExecutable() } -void runTerminators(void* extra) -{ - try { - const unsigned int imageCount = sImageFilesNeedingTermination.size(); - for(unsigned int i=imageCount; i > 0; --i){ - ImageLoader* image = sImageFilesNeedingTermination[i-1]; - image->doTermination(gLinkContext); - } - sImageFilesNeedingTermination.clear(); - notifyBatch(dyld_image_state_terminated); - } - catch (const char* msg) { - halt(msg); - } -} #if SUPPORT_VERSIONED_PATHS @@ -1256,9 +1282,9 @@ static const char** parseColonList(const char* list, const char* mainExecutableD char** result = new char*[colonCount+2]; for(const char* s=list; *s != '\0'; ++s) { if (*s == ':') { - int len = s-start; + size_t len = s-start; if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { - int mainExecDirLen = strlen(mainExecutableDir); + size_t mainExecDirLen = strlen(mainExecutableDir); char* str = new char[mainExecDirLen+len+1]; strcpy(str, mainExecutableDir); strlcat(str, &start[13], mainExecDirLen+len+1); @@ -1267,7 +1293,7 @@ static const char** parseColonList(const char* list, const char* mainExecutableD result[index++] = str; } else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) { - int mainExecDirLen = strlen(mainExecutableDir); + size_t mainExecDirLen = strlen(mainExecutableDir); char* str = new char[mainExecDirLen+len+1]; strcpy(str, mainExecutableDir); strlcat(str, &start[17], mainExecDirLen+len+1); @@ -1284,9 +1310,9 @@ static const char** parseColonList(const char* list, const char* mainExecutableD } } } - int len = strlen(start); + size_t len = strlen(start); if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { - int mainExecDirLen = strlen(mainExecutableDir); + size_t mainExecDirLen = strlen(mainExecutableDir); char* str = new char[mainExecDirLen+len+1]; strcpy(str, mainExecutableDir); strlcat(str, &start[13], mainExecDirLen+len+1); @@ -1294,7 +1320,7 @@ static const char** parseColonList(const char* list, const char* mainExecutableD result[index++] = str; } else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) { - int mainExecDirLen = strlen(mainExecutableDir); + size_t mainExecDirLen = strlen(mainExecutableDir); char* str = new char[mainExecDirLen+len+1]; strcpy(str, mainExecutableDir); strlcat(str, &start[17], mainExecDirLen+len+1); @@ -1559,6 +1585,9 @@ void processDyldEnvironmentVariable(const char* key, const char* value, const ch appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_FRAMEWORK_PATH); } #endif + else if ( strcmp(key, "DYLD_PRINT_TO_FILE") == 0 ) { + // handled in _main() + } else { dyld::warn("unknown environment variable: %s\n", key); } @@ -1578,7 +1607,7 @@ static void checkLoadCommandEnvironmentVariables() { const struct dylinker_command* envcmd = (struct dylinker_command*)cmd; const char* keyEqualsValue = (char*)envcmd + envcmd->name.offset; - char mainExecutableDir[strlen(sExecPath)]; + char mainExecutableDir[strlen(sExecPath)+2]; strcpy(mainExecutableDir, sExecPath); char* lastSlash = strrchr(mainExecutableDir, '/'); if ( lastSlash != NULL) @@ -1589,7 +1618,7 @@ static void checkLoadCommandEnvironmentVariables() if ( equals != NULL ) { if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) { const char* value = &equals[1]; - const int keyLen = equals-keyEqualsValue; + const size_t keyLen = equals-keyEqualsValue; char key[keyLen+1]; strncpy(key, keyEqualsValue, keyLen); key[keyLen] = '\0'; @@ -1693,6 +1722,9 @@ static void pruneEnvironmentVariables(const char* envp[], const char*** applep) // disable framework and library fallback paths for setuid binaries rdar://problem/4589305 sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = NULL; sEnv.DYLD_FALLBACK_LIBRARY_PATH = NULL; + + if ( removedCount > 0 ) + strlcat(sLoadingCrashMessage, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage)); } @@ -1705,8 +1737,10 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) { const char* equals = strchr(keyEqualsValue, '='); if ( (equals != NULL) && !ignoreEnviron ) { + strlcat(sLoadingCrashMessage, "\n", sizeof(sLoadingCrashMessage)); + strlcat(sLoadingCrashMessage, keyEqualsValue, sizeof(sLoadingCrashMessage)); const char* value = &equals[1]; - const int keyLen = equals-keyEqualsValue; + const size_t keyLen = equals-keyEqualsValue; char key[keyLen+1]; strncpy(key, keyEqualsValue, keyLen); key[keyLen] = '\0'; @@ -1761,7 +1795,10 @@ static void checkEnvironmentVariables(const char* envp[], bool ignoreEnviron) static void getHostInfo() { #if CPU_SUBTYPES_SUPPORTED -#if __ARM_ARCH_7A__ +#if __ARM_ARCH_7K__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7K; +#elif __ARM_ARCH_7A__ sHostCPU = CPU_TYPE_ARM; sHostCPUsubtype = CPU_SUBTYPE_ARM_V7; #elif __ARM_ARCH_6K__ @@ -1773,9 +1810,6 @@ static void getHostInfo() #elif __ARM_ARCH_7S__ sHostCPU = CPU_TYPE_ARM; sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S; -#elif __ARM_ARCH_7K__ - sHostCPU = CPU_TYPE_ARM; - sHostCPUsubtype = CPU_SUBTYPE_ARM_V7K; #else struct host_basic_info info; mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; @@ -1785,6 +1819,7 @@ static void getHostInfo() throw "host_info() failed"; sHostCPU = info.cpu_type; sHostCPUsubtype = info.cpu_subtype; + mach_port_deallocate(mach_task_self(), hostPort); #endif #endif } @@ -1799,14 +1834,20 @@ static void checkSharedRegionDisable() if ( gLinkContext.verboseMapping ) dyld::warn("disabling shared region because main executable overlaps\n"); } +#if __i386__ + if ( sProcessIsRestricted ) { + // use private or no shared region for suid processes + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; + } +#endif #endif // iPhoneOS cannot run without shared region } bool validImage(const ImageLoader* possibleImage) { - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i) { + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { if ( possibleImage == sAllImages[i] ) { return true; } @@ -1816,7 +1857,7 @@ bool validImage(const ImageLoader* possibleImage) uint32_t getImageCount() { - return sAllImages.size(); + return (uint32_t)sAllImages.size(); } ImageLoader* getIndexedImage(unsigned int index) @@ -1852,8 +1893,8 @@ ImageLoader* findImageContainingSymbol(const void* symbol) void forEachImageDo( void (*callback)(ImageLoader*, void* userData), void* userData) { - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i) { + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { ImageLoader* anImage = sAllImages[i]; (*callback)(anImage, userData); } @@ -1861,8 +1902,8 @@ void forEachImageDo( void (*callback)(ImageLoader*, void* userData), void* userD ImageLoader* findLoadedImage(const struct stat& stat_buf) { - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i){ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i){ ImageLoader* anImage = sAllImages[i]; if ( anImage->statMatch(stat_buf) ) return anImage; @@ -1873,7 +1914,7 @@ ImageLoader* findLoadedImage(const struct stat& stat_buf) // based on ANSI-C strstr() static const char* strrstr(const char* str, const char* sub) { - const int sublen = strlen(sub); + const size_t sublen = strlen(sub); for(const char* p = &str[strlen(str)]; p != str; --p) { if ( strncmp(p, sub, sublen) == 0 ) return p; @@ -1903,7 +1944,7 @@ static const char* getFrameworkPartialPath(const char* path) const char* frameworkStart = &dirStart[1]; if ( dirStart == path ) --frameworkStart; - int len = dirDot - frameworkStart; + size_t len = dirDot - frameworkStart; char framework[len+1]; strncpy(framework, frameworkStart, len); framework[len] = '\0'; @@ -1966,11 +2007,11 @@ static const cpu_subtype_t kARM[kARM_RowCount][9] = { // armv7f can run: v7f, v7, v6, v5, and v4 { CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, - // armv7k can run: v7k, 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 }, + // armv7k can run: v7k + { CPU_SUBTYPE_ARM_V7K, CPU_SUBTYPE_END_OF_LIST }, // armv7s can run: v7s, v7, v7f, v7k, v6, v5, and v4 - { CPU_SUBTYPE_ARM_V7S, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V7K, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + { CPU_SUBTYPE_ARM_V7S, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, // armv7 can run: v7, v6, v5, and v4 { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, @@ -1989,6 +2030,22 @@ static const cpu_subtype_t kARM[kARM_RowCount][9] = { }; #endif +#if __x86_64__ +// +// x86_64 sub-type lists +// +const int kX86_64_RowCount = 2; +static const cpu_subtype_t kX86_64[kX86_64_RowCount][5] = { + + // x86_64h can run: x86_64h, x86_64h(lib), x86_64(lib), and x86_64 + { CPU_SUBTYPE_X86_64_H, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_H, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // x86_64 can run: x86_64(lib) and x86_64 + { CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST }, + +}; +#endif + // scan the tables above to find the cpu-sub-type-list for this machine static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t subtype) @@ -2001,6 +2058,14 @@ static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t sub return kARM[i]; } break; +#endif +#if __x86_64__ + case CPU_TYPE_X86_64: + for (int i=0; i < kX86_64_RowCount ; ++i) { + if ( kX86_64[i][0] == subtype ) + return kX86_64[i]; + } + break; #endif } return NULL; @@ -2056,6 +2121,15 @@ static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* return true; } break; +#endif +#if __x86_64__ + case CPU_TYPE_X86_64: + if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + break; #endif } } @@ -2179,7 +2253,7 @@ static ImageLoader* instantiateFromLoadedImage(const macho_header* mh, uintptr_t #if DYLD_SHARED_CACHE_SUPPORT -static bool findInSharedCacheImage(const char* path, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide) +static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide) { if ( sSharedCache != NULL ) { #if __MAC_OS_X_VERSION_MIN_REQUIRED @@ -2192,13 +2266,24 @@ static bool findInSharedCacheImage(const char* path, const struct stat* stat_buf stat_buf = &statb; } #endif +#if __IPHONE_OS_VERSION_MIN_REQUIRED + uint64_t hash = 0; + for (const char* s=path; *s != '\0'; ++s) + hash += hash*4 + *s; +#endif + // walk shared cache to see if there is a cached image that matches the inode/mtime/path desired const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)sSharedCache + sSharedCache->imagesOffset); const dyld_cache_image_info* const end = &start[sSharedCache->imagesCount]; +#if __IPHONE_OS_VERSION_MIN_REQUIRED + const bool cacheHasHashInfo = (start->modTime == 0); +#endif for( const dyld_cache_image_info* p = start; p != end; ++p) { #if __IPHONE_OS_VERSION_MIN_REQUIRED // just check path const char* aPath = (char*)sSharedCache + p->pathFileOffset; + if ( cacheHasHashInfo && (p->inode != hash) ) + continue; if ( strcmp(path, aPath) == 0 ) { // found image in cache *mh = (macho_header*)(p->address+sSharedCacheSlide); @@ -2208,12 +2293,12 @@ static bool findInSharedCacheImage(const char* path, const struct stat* stat_buf } #elif __MAC_OS_X_VERSION_MIN_REQUIRED // check mtime and inode first because it is fast - if ( sSharedCacheIgnoreInodeAndTimeStamp - || ( ((time_t)p->modTime == stat_buf->st_mtime) && ((ino_t)p->inode == stat_buf->st_ino) ) ) { + bool inodeMatch = ( ((time_t)p->modTime == stat_buf->st_mtime) && ((ino_t)p->inode == stat_buf->st_ino) ); + if ( searchByPath || sSharedCacheIgnoreInodeAndTimeStamp || inodeMatch ) { // mod-time and inode match an image in the shared cache, now check path const char* aPath = (char*)sSharedCache + p->pathFileOffset; bool cacheHit = (strcmp(path, aPath) == 0); - if ( ! cacheHit ) { + if ( inodeMatch && !cacheHit ) { // path does not match install name of dylib in cache, but inode and mtime does match // perhaps path is a symlink to the cached dylib struct stat pathInCacheStatBuf; @@ -2241,7 +2326,7 @@ bool inSharedCache(const char* path) const macho_header* mhInCache; const char* pathInCache; long slide; - return findInSharedCacheImage(path, NULL, &mhInCache, &pathInCache, &slide); + return findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slide); } #endif @@ -2284,6 +2369,32 @@ static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& cont return image; } +#if TARGET_IPHONE_SIMULATOR +static bool isSimulatorBinary(const uint8_t* firstPage, const char* path) +{ + const macho_header* mh = (macho_header*)firstPage; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* const cmdsReadEnd = (struct load_command*)(((char*)mh)+4096); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_VERSION_MIN_IPHONEOS: + return true; + case LC_VERSION_MIN_MACOSX: + // grandfather in a few libSystem dylibs + if ( strncmp(path, "/usr/lib/system/libsystem_", 26) == 0 ) + return true; + return false; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + if ( cmd > cmdsReadEnd ) + return true; + } + return false; +} +#endif + // map in file and instantiate an ImageLoader static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* path, const LoadContext& context) { @@ -2338,6 +2449,13 @@ static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* throw "mach-o, but wrong filetype"; } +#if TARGET_IPHONE_SIMULATOR + // dyld_sim should restrict loading osx binaries + if ( !isSimulatorBinary(firstPage, path) ) { + throw "mach-o, but not built for iOS simulator"; + } +#endif + // instantiate an image ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext); @@ -2421,7 +2539,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const const macho_header* mhInCache; const char* pathInCache; long slideInCache; - if ( findInSharedCacheImage(path, &stat_buf, &mhInCache, &pathInCache, &slideInCache) ) { + if ( findInSharedCacheImage(path, false, &stat_buf, &mhInCache, &pathInCache, &slideInCache) ) { image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); return checkandAddImage(image, context); } @@ -2483,7 +2601,7 @@ static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const const macho_header* mhInCache; const char* pathInCache; long slideInCache; - if ( findInSharedCacheImage(path, NULL, &mhInCache, &pathInCache, &slideInCache) ) { + if ( findInSharedCacheImage(path, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) { // see if this image in the cache was already loaded via a different path for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { ImageLoader* anImage = *it; @@ -2725,7 +2843,7 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load const char* frameworkPartialPath = getFrameworkPartialPath(path); if ( frameworkPaths != NULL ) { if ( frameworkPartialPath != NULL ) { - const int frameworkPartialPathLen = strlen(frameworkPartialPath); + const size_t frameworkPartialPathLen = strlen(frameworkPartialPath); for(const char* const* fp = frameworkPaths; *fp != NULL; ++fp) { char npath[strlen(*fp)+frameworkPartialPathLen+8]; strcpy(npath, *fp); @@ -2742,7 +2860,7 @@ static ImageLoader* loadPhase2(const char* path, const char* orgPath, const Load // 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); + const size_t libraryLeafNameLen = strlen(libraryLeafName); for(const char* const* lp = libraryPaths; *lp != NULL; ++lp) { char libpath[strlen(*lp)+libraryLeafNameLen+8]; strcpy(libpath, *lp); @@ -2816,6 +2934,16 @@ static ImageLoader* loadPhase0(const char* path, const char* orgPath, const Load return loadPhase1(path, orgPath, context, exceptions); } +#if DYLD_SHARED_CACHE_SUPPORT + static bool cacheablePath(const char* path) { + if (strncmp(path, "/usr/lib/", 9) == 0) + return true; + if (strncmp(path, "/System/Library/", 16) == 0) + return true; + return false; + } +#endif + // // Given all the DYLD_ environment variables, the general case for loading libraries // is that any given path expands into a list of possible locations to load. We @@ -2853,19 +2981,44 @@ ImageLoader* load(const char* path, const LoadContext& context) // try all path permutations and try open() until first success std::vector exceptions; image = loadPhase0(path, orgPath, context, &exceptions); +#if __IPHONE_OS_VERSION_MIN_REQUIRED && DYLD_SHARED_CACHE_SUPPORT && !TARGET_IPHONE_SIMULATOR + // support symlinks on disk to a path in dyld shared cache + if ( (image == NULL) && cacheablePath(path) && !context.dontLoad ) { + char resolvedPath[PATH_MAX]; + realpath(path, resolvedPath); + int myerr = errno; + // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT + if ( (myerr == ENOENT) || (myerr == 0) ) + { + // see if this image is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( findInSharedCacheImage(resolvedPath, false, NULL, &mhInCache, &pathInCache, &slideInCache) ) { + struct stat stat_buf; + bzero(&stat_buf, sizeof(stat_buf)); + try { + image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext); + image = checkandAddImage(image, context); + } + catch (...) { + image = NULL; + } + } + } + } +#endif CRSetCrashLogMessage2(NULL); if ( image != NULL ) { // leak in dyld during dlopen when using DYLD_ variables for (std::vector::iterator it = exceptions.begin(); it != exceptions.end(); ++it) { free((void*)(*it)); } -#if __IPHONE_OS_VERSION_MIN_REQUIRED && DYLD_SHARED_CACHE_SUPPORT +#if DYLD_SHARED_CACHE_SUPPORT // if loaded image is not from cache, but original path is in cache // set gSharedCacheOverridden flag to disable some ObjC optimizations - if ( !gSharedCacheOverridden ) { - if ( !image->inSharedCache() && inSharedCache(path) ) { - gSharedCacheOverridden = true; - } + if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && cacheablePath(path) && inSharedCache(path) ) { + gSharedCacheOverridden = true; } #endif return image; @@ -2881,11 +3034,11 @@ ImageLoader* load(const char* path, const LoadContext& context) const char* msgStart = "no suitable image found. Did find:"; const char* delim = "\n\t"; size_t allsizes = strlen(msgStart)+8; - for (unsigned int i=0; i < exceptions.size(); ++i) + for (size_t i=0; i < exceptions.size(); ++i) allsizes += (strlen(exceptions[i]) + strlen(delim)); char* fullMsg = new char[allsizes]; strcpy(fullMsg, msgStart); - for (unsigned int i=0; i < exceptions.size(); ++i) { + for (size_t i=0; i < exceptions.size(); ++i) { strcat(fullMsg, delim); strcat(fullMsg, exceptions[i]); free((void*)exceptions[i]); @@ -2906,6 +3059,8 @@ 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 ARCH_NAME_H "x86_64h" + #define ARCH_CACHE_MAGIC_H "dyld_v1 x86_64h" #elif __ARM_ARCH_5TEJ__ #define ARCH_NAME "armv5" #define ARCH_CACHE_MAGIC "dyld_v1 armv5" @@ -2915,15 +3070,18 @@ ImageLoader* load(const char* path, const LoadContext& context) #elif __ARM_ARCH_7F__ #define ARCH_NAME "armv7f" #define ARCH_CACHE_MAGIC "dyld_v1 armv7f" +#elif __ARM_ARCH_7K__ + #define ARCH_NAME "armv7k" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7k" #elif __ARM_ARCH_7A__ #define ARCH_NAME "armv7" #define ARCH_CACHE_MAGIC "dyld_v1 armv7" #elif __ARM_ARCH_7S__ #define ARCH_NAME "armv7s" #define ARCH_CACHE_MAGIC "dyld_v1 armv7s" -#elif __ARM_ARCH_7K__ - #define ARCH_NAME "armv7k" - #define ARCH_CACHE_MAGIC "dyld_v1 armv7k" +#elif __arm64__ + #define ARCH_NAME "arm64" + #define ARCH_CACHE_MAGIC "dyld_v1 arm64" #endif @@ -2936,7 +3094,7 @@ 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[], - int codeSignatureMappingIndex, int slide, void* slideInfo, uint32_t slideInfoSize) + int codeSignatureMappingIndex, long slide, void* slideInfo, unsigned long slideInfoSize) { // register code signature blob for whole dyld cache if ( codeSignatureMappingIndex != -1 ) { @@ -3034,10 +3192,43 @@ int openSharedCacheFile() char path[MAXPATHLEN]; strlcpy(path, sSharedCacheDir, MAXPATHLEN); strlcat(path, "/", MAXPATHLEN); +#if __x86_64__ + if ( sHaswell ) { + strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, MAXPATHLEN); + int fd = my_open(path, O_RDONLY, 0); + if ( fd != -1 ) { + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path); + return fd; + } + strlcpy(path, sSharedCacheDir, MAXPATHLEN); + } +#endif strlcat(path, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, MAXPATHLEN); + if ( gLinkContext.verboseMapping ) + dyld::log("dyld: Mapping%s shared cache from %s\n", (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion) ? " private": "", path); return my_open(path, O_RDONLY, 0); } + +static void getCacheBounds(uint32_t mappingsCount, const shared_file_mapping_np mappings[], uint64_t& lowAddress, uint64_t& highAddress) +{ + lowAddress = 0; + highAddress = 0; + for(uint32_t i=0; i < mappingsCount; ++i) { + if ( lowAddress == 0 ) { + lowAddress = mappings[i].sfm_address; + highAddress = mappings[i].sfm_address + mappings[i].sfm_size; + } + else { + if ( mappings[i].sfm_address < lowAddress ) + lowAddress = mappings[i].sfm_address; + if ( (mappings[i].sfm_address + mappings[i].sfm_size) > highAddress ) + highAddress = mappings[i].sfm_address + mappings[i].sfm_size; + } + } +} + static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappings[]) { #if __x86_64__ @@ -3068,26 +3259,15 @@ static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappin // else fall through to handle old style cache #endif // get bounds of cache - uint64_t lowAddress = 0; - uint64_t highAddress = 0; - for(uint32_t i=0; i < mappingsCount; ++i) { - if ( lowAddress == 0 ) { - lowAddress = mappings[i].sfm_address; - highAddress = mappings[i].sfm_address + mappings[i].sfm_size; - } - else { - if ( mappings[i].sfm_address < lowAddress ) - lowAddress = mappings[i].sfm_address; - if ( (mappings[i].sfm_address + mappings[i].sfm_size) > highAddress ) - highAddress = mappings[i].sfm_address + mappings[i].sfm_size; - } - } + uint64_t lowAddress; + uint64_t highAddress; + getCacheBounds(mappingsCount, mappings, lowAddress, highAddress); // find slop space const uint64_t space = (SHARED_REGION_BASE + SHARED_REGION_SIZE) - highAddress; // choose new random slide - long slide = (arc4random() % space) & (-4096); + long slide = dyld_page_trunc(arc4random() % space); //dyld::log("slideSpace=0x%0llX\n", space); //dyld::log("slide=0x%0lX\n", slide); @@ -3102,11 +3282,16 @@ static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappin static void mapSharedCache() { uint64_t cacheBaseAddress = 0; - // quick check if a cache is alreay mapped into shared region + // quick check if a cache is already mapped into shared region if ( _shared_region_check_np(&cacheBaseAddress) == 0 ) { sSharedCache = (dyld_cache_header*)cacheBaseAddress; // if we don't understand the currently mapped shared cache, then ignore - if ( strcmp(sSharedCache->magic, ARCH_CACHE_MAGIC) != 0 ) { +#if __x86_64__ + const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); +#else + const char* magic = ARCH_CACHE_MAGIC; +#endif + if ( strcmp(sSharedCache->magic, magic) != 0 ) { sSharedCache = NULL; if ( gLinkContext.verboseMapping ) { dyld::log("dyld: existing shared cached in memory is not compatible\n"); @@ -3128,6 +3313,10 @@ static void mapSharedCache() if ( header->mappingOffset >= 0x68 ) { memcpy(dyld::gProcessInfo->sharedCacheUUID, header->uuid, 16); } + // verbose logging + if ( gLinkContext.verboseMapping ) { + dyld::log("dyld: re-using existing shared cache mapping\n"); + } } else { #if __i386__ || __x86_64__ @@ -3160,7 +3349,12 @@ static void mapSharedCache() uint8_t firstPages[8192]; if ( ::read(fd, firstPages, 8192) == 8192 ) { dyld_cache_header* header = (dyld_cache_header*)firstPages; - if ( strcmp(header->magic, ARCH_CACHE_MAGIC) == 0 ) { + #if __x86_64__ + const char* magic = (sHaswell ? ARCH_CACHE_MAGIC_H : ARCH_CACHE_MAGIC); + #else + const char* magic = ARCH_CACHE_MAGIC; + #endif + if ( strcmp(header->magic, magic) == 0 ) { const dyld_cache_mapping_info* const fileMappingsStart = (dyld_cache_mapping_info*)&firstPages[header->mappingOffset]; const dyld_cache_mapping_info* const fileMappingsEnd = &fileMappingsStart[header->mappingCount]; shared_file_mapping_np mappings[header->mappingCount+1]; // add room for code-sig @@ -3194,7 +3388,7 @@ static void mapSharedCache() } } // if shared cache is code signed, add a mapping for the code signature - uint32_t signatureSize = header->codeSignatureSize; + uint64_t signatureSize = header->codeSignatureSize; // zero size in header means signature runs to end-of-file if ( signatureSize == 0 ) signatureSize = stat_buf.st_size - header->codeSignatureOffset; @@ -3202,7 +3396,11 @@ static void mapSharedCache() int linkeditMapping = mappingCount-1; codeSignatureMappingIndex = mappingCount++; mappings[codeSignatureMappingIndex].sfm_address = mappings[linkeditMapping].sfm_address + mappings[linkeditMapping].sfm_size; +#if __arm__ || __arm64__ + mappings[codeSignatureMappingIndex].sfm_size = (signatureSize+16383) & (-16384); +#else mappings[codeSignatureMappingIndex].sfm_size = (signatureSize+4095) & (-4096); +#endif mappings[codeSignatureMappingIndex].sfm_file_offset = header->codeSignatureOffset; mappings[codeSignatureMappingIndex].sfm_max_prot = VM_PROT_READ; mappings[codeSignatureMappingIndex].sfm_init_prot = VM_PROT_READ; @@ -3228,7 +3426,17 @@ static void mapSharedCache() goodCache = false; } } -#endif +#endif +#if __IPHONE_OS_VERSION_MIN_REQUIRED + { + uint64_t lowAddress; + uint64_t highAddress; + getCacheBounds(mappingCount, mappings, lowAddress, highAddress); + if ( (highAddress-lowAddress) > SHARED_REGION_SIZE ) + throw "dyld shared cache is too big to fit in shared region"; + } +#endif + if ( goodCache && (readWriteMappingIndex == -1) ) { dyld::log("dyld: shared cached file is missing read/write mapping: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); goodCache = false; @@ -3240,7 +3448,7 @@ static void mapSharedCache() if ( goodCache ) { long cacheSlide = 0; void* slideInfo = NULL; - uint32_t slideInfoSize = 0; + uint64_t slideInfoSize = 0; // check if shared cache contains slid info if ( header->slideInfoSize != 0 ) { // don't slide shared cache if ASLR disabled (main executable didn't slide) @@ -3256,6 +3464,12 @@ static void mapSharedCache() mappings[readWriteMappingIndex].sfm_init_prot |= VM_PROT_SLIDE; } } + if ( gLinkContext.verboseMapping ) { + dyld::log("dyld: calling _shared_region_map_and_slide_np() with regions:\n"); + for (int i=0; i < mappingCount; ++i) { + dyld::log(" address=0x%08llX, size=0x%08llX, fileOffset=0x%08llX\n", mappings[i].sfm_address, mappings[i].sfm_size, mappings[i].sfm_file_offset); + } + } if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, codeSignatureMappingIndex, cacheSlide, slideInfo, slideInfoSize) == 0) { // successfully mapped cache into shared region sSharedCache = (dyld_cache_header*)mappings[0].sfm_address; @@ -3268,6 +3482,9 @@ static void mapSharedCache() } } else { +#if __IPHONE_OS_VERSION_MIN_REQUIRED + throw "dyld shared cache could not be mapped"; +#endif if ( gLinkContext.verboseMapping ) dyld::log("dyld: shared cached file could not be mapped\n"); } @@ -3302,12 +3519,6 @@ static void mapSharedCache() // only room to tell gdb about first four regions if ( dyld_shared_cache_ranges.sharedRegionsCount > 4 ) dyld_shared_cache_ranges.sharedRegionsCount = 4; - if ( gLinkContext.verboseMapping ) { - if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion ) - dyld::log("dyld: Mapping shared cache from %s/" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); - else if ( gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion ) - dyld::log("dyld: Mapping private shared cache from %s/" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir); - } const dyld_cache_mapping_info* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount]; int index = 0; for (const dyld_cache_mapping_info* p = start; p < end; ++p, ++index ) { @@ -3339,7 +3550,7 @@ static void mapSharedCache() if ( gLinkContext.verboseMapping ) { // list the code blob dyld_cache_header* header = (dyld_cache_header*)sSharedCache; - uint32_t signatureSize = header->codeSignatureSize; + uint64_t signatureSize = header->codeSignatureSize; // zero size in header means signature runs to end-of-file if ( signatureSize == 0 ) { struct stat stat_buf; @@ -3355,7 +3566,10 @@ static void mapSharedCache() #if __IPHONE_OS_VERSION_MIN_REQUIRED // check for file that enables dyld shared cache dylibs to be overridden struct stat enableStatBuf; - sDylibsOverrideCache = ( my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0 ); + // check file size to determine if correct file is in place. + // See Need a way to disable roots without removing /S/L/C/com.apple.dyld/enable... + sDylibsOverrideCache = ( (my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0) + && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) ); #endif } } @@ -3445,6 +3659,9 @@ void registerAddCallback(ImageCallback func) void registerRemoveCallback(ImageCallback func) { + // ignore calls to register a notification during a notification + if ( sRemoveImageCallbacksInUse ) + return; sRemoveImageCallbacks.push_back(func); } @@ -3541,7 +3758,7 @@ uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindi // bind lazy pointer and return it try { - result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext, + result = (*imageLoaderCache)->doBindFastLazySymbol((uint32_t)lazyBindingInfoOffset, gLinkContext, (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL, (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL); } @@ -3573,8 +3790,8 @@ static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const Ima // search all images in order const ImageLoader* firstWeakImage = NULL; const ImageLoader::Symbol* firstWeakSym = NULL; - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i) { + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { ImageLoader* anImage = sAllImages[i]; // the use of inserted libraries alters search order // so that inserted libraries are found before the main executable @@ -3625,8 +3842,8 @@ bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** s bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image) { // search all images in order - const unsigned int imageCount = sAllImages.size(); - for(unsigned int i=0; i < imageCount; ++i){ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i){ ImageLoader* anImage = sAllImages[i]; // only look at images whose paths contain the hint string (NULL hint string is wildcard) if ( ! anImage->isBundle() && ((librarySubstring==NULL) || (strstr(anImage->getPath(), librarySubstring) != NULL)) ) { @@ -3784,8 +4001,14 @@ static void setContext(const macho_header* mainExecutableMH, int argc, const cha gLinkContext.programVars.__prognamePtr=&gLinkContext.progname; gLinkContext.mainExecutable = NULL; gLinkContext.imageSuffix = NULL; + gLinkContext.dynamicInterposeArray = NULL; + gLinkContext.dynamicInterposeCount = 0; gLinkContext.prebindUsage = ImageLoader::kUseAllPrebinding; +#if TARGET_IPHONE_SIMULATOR + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; +#else gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; +#endif } @@ -3898,6 +4121,7 @@ static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* versi } #endif // SUPPORT_VERSIONED_PATHS + #if 0 static void printAllImages() { @@ -3997,17 +4221,31 @@ void garbageCollectImages() } // 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 + const int maxRangeCount = deadCount*2; + __cxa_range_t ranges[maxRangeCount]; + int rangeCount = 0; for (unsigned i=0; i < deadCount; ++i) { ImageLoader* image = deadImages[i]; + for (unsigned int j=0; j < image->segmentCount(); ++j) { + if ( !image->segExecutable(j) ) + continue; + if ( rangeCount < maxRangeCount ) { + ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j); + ranges[rangeCount].length = image->segSize(j); + ++rangeCount; + } + } try { - if (gLogAPIs) dyld::log("dlclose(), running terminators for %p %s\n", image, image->getShortName()); - runImageTerminators(image); + runImageStaticTerminators(image); } catch (const char* msg) { dyld::warn("problem running terminators for image: %s\n", msg); } } + + // dyld should call __cxa_finalize_ranges() + if ( (rangeCount > 0) && (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 13) ) + (*gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount); // collect phase: delete all images which are not marked in-use bool mightBeMore; @@ -4062,6 +4300,25 @@ void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths) preflight_finally(image); } +#if __x86_64__ +static bool isHaswell() +{ +#if TARGET_IPHONE_SIMULATOR + return false; +#else + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + if ( result != KERN_SUCCESS ) + return false; + return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H ); +#endif +} +#endif + static void loadInsertedDylib(const char* path) { ImageLoader* image = NULL; @@ -4081,7 +4338,11 @@ static void loadInsertedDylib(const char* path) image = load(path, context); } catch (const char* msg) { +#if TARGET_IPHONE_SIMULATOR + dyld::log("dyld: warning: could not load inserted library '%s' because %s\n", path, msg); +#else halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg)); +#endif } catch (...) { halt(dyld::mkstringf("could not load inserted library '%s'\n", path)); @@ -4156,7 +4417,8 @@ 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, + 3, + // added in version 1 (open_proc_t)&open, &close, &pread, @@ -4185,7 +4447,13 @@ static SyscallHelpers sSysCalls = { &OSMemoryBarrier, &getProcessInfo, &__error, - &mach_absolute_time + &mach_absolute_time, + // added in version 2 + &thread_switch, + // added in version 3 + &opendir, + &readdir_r, + &closedir }; __attribute__((noinline)) @@ -4325,6 +4593,19 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, { uintptr_t result = 0; sMainExecutableMachHeader = mainExecutableMH; +#if !TARGET_IPHONE_SIMULATOR + const char* loggingPath = _simple_getenv(envp, "DYLD_PRINT_TO_FILE"); + if ( loggingPath != NULL ) { + int fd = open(loggingPath, O_WRONLY | O_CREAT | O_APPEND, 0644); + if ( fd != -1 ) { + sLogfile = fd; + sLogToFile = true; + } + else { + dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", loggingPath, errno); + } + } +#endif #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"); @@ -4344,13 +4625,6 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, #endif CRSetCrashLogMessage("dyld: launch started"); -#ifdef ALTERNATIVE_LOGFILE - sLogfile = open(ALTERNATIVE_LOGFILE, O_WRONLY | O_CREAT | O_APPEND); - if ( sLogfile == -1 ) { - sLogfile = STDERR_FILENO; - dyld::log("error opening alternate log file %s, errno = %d\n", ALTERNATIVE_LOGFILE, errno); - } -#endif #if LOG_BINDINGS char bindingsLogPath[256]; @@ -4438,16 +4712,35 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, try { // add dyld itself to UUID list addDyldImageToUUIDList(); - if ( sProcessIsRestricted ) - CRSetCrashLogMessage("dyld: launch, loading dependent libraries, ignoring DYLD_* env vars"); - else - CRSetCrashLogMessage("dyld: launch, loading dependent libraries"); + CRSetCrashLogMessage(sLoadingCrashMessage); // instantiate ImageLoader for main executable sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); gLinkContext.mainExecutable = sMainExecutable; gLinkContext.processIsRestricted = sProcessIsRestricted; gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH); + +#if TARGET_IPHONE_SIMULATOR + // check main executable is not too new for this OS + { + if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) { + throwf("program was built for Mac OS X and cannot be run in simulator"); + } + uint32_t mainMinOS = sMainExecutable->minOSVersion(); + // dyld is always built for the current OS, so we can get the current OS version + // from the load command in dyld itself. + uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle); + if ( mainMinOS > dyldMinOS ) { + throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d", + mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), + dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); + } + } +#endif + // load shared cache + #if __x86_64__ + sHaswell = isHaswell(); + #endif checkSharedRegionDisable(); #if DYLD_SHARED_CACHE_SUPPORT if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) @@ -4479,7 +4772,11 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, ImageLoader* image = sAllImages[i+1]; link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL)); image->setNeverUnloadRecursive(); - // only INSERTED libraries can interpose + } + // only INSERTED libraries can interpose + // register interposing info after all inserted libraries are bound so chaining works + for(unsigned int i=0; i < sInsertedDylibCount; ++i) { + ImageLoader* image = sAllImages[i+1]; image->registerInterposing(); } } @@ -4524,13 +4821,6 @@ _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, dyld::log("dyld: launch failed\n"); } -#ifdef ALTERNATIVE_LOGFILE - // only use alternate log during launch, otherwise file is open forever - if ( sLogfile != STDERR_FILENO ) { - close(sLogfile); - sLogfile = STDERR_FILENO; - } -#endif CRSetCrashLogMessage(NULL); return result; diff --git a/src/dyld.h b/src/dyld.h index 9c55ecd..3cdc3c7 100644 --- a/src/dyld.h +++ b/src/dyld.h @@ -75,8 +75,7 @@ namespace dyld { extern void preflight(ImageLoader* image, 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 void runImageStaticTerminators(ImageLoader* image); extern const char* getExecutablePath(); extern bool validImage(const ImageLoader*); extern ImageLoader* getIndexedImage(uint32_t index); @@ -93,8 +92,8 @@ namespace dyld { extern void removeImage(ImageLoader* image); 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) __attribute__((noinline)); // + 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) __attribute__((noinline)); // version >= 13) ) { + __cxa_range_t ranges[3]; + int rangeCount = 0; + for (unsigned int j=0; j < image->segmentCount(); ++j) { + if ( !image->segExecutable(j) ) + continue; + ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j); + ranges[rangeCount].length = image->segSize(j); + ++rangeCount; + } + (*dyld::gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount); + } dyld::removeImage(image); if ( (options & NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) != 0 ) @@ -1195,27 +1206,6 @@ void _dyld_fork_child() dyld::gProcessInfo->systemOrderFlag = 0; } -typedef void (*MonitorProc)(char *lowpc, char *highpc); - -static void monInitCallback(ImageLoader* image, void* userData) -{ - MonitorProc proc = (MonitorProc)userData; - void* start; - size_t length; - if ( image->getSectionContent("__TEXT", "__text", &start, &length) ) { - proc((char*)start, (char*)start+length); - } -} - -// -// _dyld_moninit is called from profiling runtime routine moninit(). -// dyld calls back with the range of each __TEXT/__text section in every -// linked image. -// -void _dyld_moninit(MonitorProc proc) -{ - dyld::forEachImageDo(&monInitCallback, (void*)proc); -} #if DEPRECATED_APIS_SUPPORTED // returns true if prebinding was used in main executable @@ -1250,13 +1240,13 @@ static bool NSMakePrivateModulePublic(NSModule module) #endif // DEPRECATED_APIS_SUPPORTED -bool lookupDyldFunction(const char* name, uintptr_t* address) +int _dyld_func_lookup(const char* name, void** address) { for (const dyld_func* p = dyld_funcs; p->name != NULL; ++p) { if ( strcmp(p->name, name) == 0 ) { if( p->implementation == unimplemented ) dyld::log("unimplemented dyld function: %s\n", p->name); - *address = (uintptr_t)p->implementation; + *address = p->implementation; return true; } } @@ -1272,7 +1262,7 @@ static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) // let gdb know it is safe to run code in inferior that might call malloc() dyld::gProcessInfo->libSystemInitialized = true; -#if __arm__ +#if !SUPPORT_ZERO_COST_EXCEPTIONS if ( helpers->version >= 5 ) { // create key use by dyld exception handling pthread_key_t key; @@ -1744,7 +1734,13 @@ void* dlsym(void* handle, const char* symbolName) if ( sym != NULL ) { CRSetCrashLogMessage(NULL); - return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + ImageLoader* callerImage = NULL; + if ( sDynamicInterposing ) { + // only take time to look up caller, if dynamic interposing in use + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + callerImage = dyld::findImageContainingAddress(callerAddress); + } + return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage); } const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName); dlerrorSet(str); @@ -1771,7 +1767,7 @@ const struct dyld_all_image_infos* _dyld_get_all_image_infos() return dyld::gProcessInfo; } -#if !__arm__ +#if SUPPORT_ZERO_COST_EXCEPTIONS static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) { //if ( dyld::gLogAPIs ) @@ -1811,7 +1807,6 @@ 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 @@ -1820,7 +1815,32 @@ bool dyld_shared_cache_some_image_overridden() return true; #endif } -#endif + + +void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) +{ + if ( mh == NULL ) + return; + if ( array == NULL ) + return; + if ( count == 0 ) + return; + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image == NULL ) + return; + + // make pass at bound references in this image and update them + dyld::gLinkContext.dynamicInterposeArray = array; + dyld::gLinkContext.dynamicInterposeCount = count; + image->dynamicInterpose(dyld::gLinkContext); + dyld::gLinkContext.dynamicInterposeArray = NULL; + dyld::gLinkContext.dynamicInterposeCount = 0; + + // leave interposing info so any future (lazy) binding will get it too + image->addDynamicInterposingTuples(array, count); + + sDynamicInterposing = true; +} diff --git a/src/dyldAPIsInLibSystem.cpp b/src/dyldAPIsInLibSystem.cpp index f7b59be..6ded1a7 100644 --- a/src/dyldAPIsInLibSystem.cpp +++ b/src/dyldAPIsInLibSystem.cpp @@ -34,11 +34,13 @@ #include "mach-o/dyld.h" #include "mach-o/dyld_priv.h" +#include "ImageLoader.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); +extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int count); #ifndef LC_VERSION_MIN_MACOSX @@ -61,7 +63,6 @@ extern "C" void __cxa_finalize(const void *dso); #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ #endif -#define DYLD_SHARED_CACHE_SUPPORT (__i386__ || __x86_64__ || __arm__) // deprecated APIs are still availble on Mac OS X, but not on iPhone OS #if __IPHONE_OS_VERSION_MIN_REQUIRED @@ -79,10 +80,10 @@ extern "C" void __cxa_finalize(const void *dso); static bool names_match( -char *install_name, +const char *install_name, const char* libraryName) { - char *basename; + const char *basename; unsigned long n; /* @@ -103,7 +104,7 @@ const char* libraryName) * of the -framework cases. */ if(strcmp(basename, libraryName) == 0) - return(TRUE); + return true; /* * Now check the base name for "lib" if so proceed to check for the @@ -113,14 +114,14 @@ const char* libraryName) n = strlen(libraryName); if(strncmp(basename+3, libraryName, n) == 0){ if(strncmp(basename+3+n, ".dylib", 6) == 0) - return(TRUE); + return true; if(basename[3+n] == '.' && basename[3+n+1] != '\0' && strncmp(basename+3+n+2, ".dylib", 6) == 0) - return(TRUE); + return true; } } - return(FALSE); + return false; } #if DEPRECATED_APIS_SUPPORTED @@ -346,42 +347,35 @@ uint32_t options) * and not a list of current versions that dependent libraries and bundles the * program is using were built with. */ -int32_t -NSVersionOfLinkTimeLibrary( -const char* libraryName) +int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) { - unsigned long i; - struct load_command *load_commands, *lc; - struct dylib_command *dl; - char *install_name; + // Lazily call _NSGetMachExecuteHeader() and cache result #if __LP64__ - static struct mach_header_64 *mh = NULL; + static mach_header_64* mh = NULL; #else - static struct mach_header *mh = NULL; + static mach_header* mh = NULL; #endif - if(mh == NULL) + if ( mh == NULL ) mh = _NSGetMachExecuteHeader(); - load_commands = (struct load_command *) #if __LP64__ - ((char *)mh + sizeof(struct mach_header_64)); + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header_64)); #else - ((char *)mh + sizeof(struct mach_header)); + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header)); #endif - lc = load_commands; - for(i = 0; i < mh->ncmds; i++){ + for(uint32_t i = 0; i < mh->ncmds; i++){ switch ( lc->cmd ) { case LC_LOAD_DYLIB: case LC_LOAD_WEAK_DYLIB: case LC_LOAD_UPWARD_DYLIB: - dl = (struct dylib_command *)lc; - install_name = (char *)dl + dl->dylib.name.offset; - if(names_match(install_name, libraryName) == TRUE) - return(dl->dylib.current_version); + const dylib_command* dl = (dylib_command *)lc; + const char* install_name = (char*)dl + dl->dylib.name.offset; + if ( names_match(install_name, libraryName) ) + return dl->dylib.current_version; break; } - lc = (struct load_command *)((char *)lc + lc->cmdsize); + lc = (load_command*)((char*)lc + lc->cmdsize); } - return(-1); + return (-1); } /* @@ -391,39 +385,31 @@ const char* libraryName) * it would be "x" and with -framework Foo it would be "Foo"). If the program * is not using the specified library it returns -1. */ -int32_t -NSVersionOfRunTimeLibrary( -const char* libraryName) -{ - unsigned long i, j, n; - char *install_name; - struct load_command *load_commands, *lc; - struct dylib_command *dl; - const struct mach_header *mh; - - n = _dyld_image_count(); - for(i = 0; i < n; i++){ - mh = _dyld_get_image_header(i); - if(mh->filetype != MH_DYLIB) - continue; - load_commands = (struct load_command *) +int32_t NSVersionOfRunTimeLibrary(const char* libraryName) +{ + uint32_t n = _dyld_image_count(); + for(uint32_t i = 0; i < n; i++){ + const mach_header* mh = _dyld_get_image_header(i); + if ( mh == NULL ) + continue; + if ( mh->filetype != MH_DYLIB ) + continue; #if __LP64__ - ((char *)mh + sizeof(struct mach_header_64)); + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header_64)); #else - ((char *)mh + sizeof(struct mach_header)); + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header)); #endif - lc = load_commands; - for(j = 0; j < mh->ncmds; j++){ - if(lc->cmd == LC_ID_DYLIB){ - dl = (struct dylib_command *)lc; - install_name = (char *)dl + dl->dylib.name.offset; - if(names_match(install_name, libraryName) == TRUE) - return(dl->dylib.current_version); - } - lc = (struct load_command *)((char *)lc + lc->cmdsize); + for(uint32_t j = 0; j < mh->ncmds; j++){ + if ( lc->cmd == LC_ID_DYLIB ) { + const dylib_command* dl = (dylib_command*)lc; + const char* install_name = (char *)dl + dl->dylib.name.offset; + if ( names_match(install_name, libraryName) ) + return dl->dylib.current_version; + } + lc = (load_command*)((char*)lc + lc->cmdsize); } } - return(-1); + return (-1); } #define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff)) @@ -459,7 +445,12 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) uint32_t libSystemVers = 0; #endif for(uint32_t i = 0; i < mh->ncmds; ++i) { - switch ( cmd->cmd ) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + // sanity check size of command + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return 0; + } + switch ( cmd->cmd ) { #if __IPHONE_OS_VERSION_MIN_REQUIRED case LC_VERSION_MIN_IPHONEOS: #else @@ -478,6 +469,9 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) case LC_LOAD_WEAK_DYLIB: case LC_LOAD_UPWARD_DYLIB: dylibCmd = (dylib_command*)cmd; + // sanity check dylib command layout + if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) + return 0; dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset; #if __IPHONE_OS_VERSION_MIN_REQUIRED if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 ) @@ -488,13 +482,7 @@ uint32_t dyld_get_sdk_version(const mach_header* mh) #endif break; } - // sanity check size of command - if ( (cmd->cmdsize < 8) || (cmd->cmdsize > mh->sizeofcmds) ) - return 0; - cmd = (load_command*)((char *)cmd + cmd->cmdsize); - // bounds check - if ( (cmd > cmdsEnd) || (cmd < startCmds) ) - return 0; + cmd = nextCmd; } struct DylibToOSMapping { @@ -582,7 +570,12 @@ uint32_t dyld_get_min_os_version(const struct mach_header* mh) const version_min_command* versCmd; const load_command* cmd = startCmds; for(uint32_t i = 0; i < mh->ncmds; ++i) { - switch ( cmd->cmd ) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + // sanity check size of command + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return 0; + } + switch ( cmd->cmd ) { #if __IPHONE_OS_VERSION_MIN_REQUIRED case LC_VERSION_MIN_IPHONEOS: #else @@ -592,13 +585,7 @@ uint32_t dyld_get_min_os_version(const struct mach_header* mh) return versCmd->version; // found explicit min OS version break; } - // sanity check size of command - if ( (cmd->cmdsize < 8) || (cmd->cmdsize > mh->sizeofcmds) ) - return 0; - cmd = (load_command*)((char *)cmd + cmd->cmdsize); - // bounds check - if ( (cmd > cmdsEnd) || (cmd < startCmds) ) - return 0; + cmd = nextCmd; } return 0; } @@ -1106,7 +1093,7 @@ uint32_t _dyld_image_count(void) { DYLD_NO_LOCK_THIS_BLOCK; - static unsigned long (*p)(void) = NULL; + static uint32_t (*p)(void) = NULL; if(p == NULL) _dyld_func_lookup("__dyld_image_count", (void**)&p); @@ -1181,17 +1168,6 @@ const void* address) return p(address); } -void _dyld_moninit( -void (*monaddition)(char *lowpc, char *highpc)) -{ - DYLD_LOCK_THIS_BLOCK; - typedef void (*monproc)(char *lowpc, char *highpc); - static void (*p)(monproc monaddition) = NULL; - - if(p == NULL) - _dyld_func_lookup("__dyld_moninit", (void**)&p); - p(monaddition); -} #if DEPRECATED_APIS_SUPPORTED bool _dyld_launched_prebound(void) @@ -1232,12 +1208,12 @@ static bool dlerrorPerThreadKeyInitialized = false; // data kept per-thread struct dlerrorPerThreadData { - uint32_t sizeAllocated; + size_t sizeAllocated; char message[1]; }; // function called by dyld to get buffer to store dlerror message -static char* getPerThreadBufferFor_dlerror(uint32_t sizeRequired) +static char* getPerThreadBufferFor_dlerror(size_t sizeRequired) { // ok to create key lazily because this function is called within dyld lock, so there is no race condition if (!dlerrorPerThreadKeyInitialized ) { @@ -1246,11 +1222,11 @@ static char* getPerThreadBufferFor_dlerror(uint32_t sizeRequired) dlerrorPerThreadKeyInitialized = true; } - const int size = (sizeRequired < 256) ? 256 : sizeRequired; + const size_t size = (sizeRequired < 256) ? 256 : sizeRequired; dlerrorPerThreadData* data = (dlerrorPerThreadData*)pthread_getspecific(dlerrorPerThreadKey); if ( data == NULL ) { //int mallocSize = offsetof(dlerrorPerThreadData, message[size]); - const int mallocSize = sizeof(dlerrorPerThreadData)+size; + const size_t mallocSize = sizeof(dlerrorPerThreadData)+size; data = (dlerrorPerThreadData*)malloc(mallocSize); data->sizeAllocated = size; pthread_setspecific(dlerrorPerThreadKey, data); @@ -1258,7 +1234,7 @@ static char* getPerThreadBufferFor_dlerror(uint32_t sizeRequired) else if ( data->sizeAllocated < sizeRequired ) { free(data); //int mallocSize = offsetof(dlerrorPerThreadData, message[size]); - const int mallocSize = sizeof(dlerrorPerThreadData)+size; + const size_t mallocSize = sizeof(dlerrorPerThreadData)+size; data = (dlerrorPerThreadData*)malloc(mallocSize); data->sizeAllocated = size; pthread_setspecific(dlerrorPerThreadKey, data); @@ -1307,7 +1283,7 @@ static void shared_cache_out_of_date() // the table passed to dyld containing thread helpers -static dyld::LibSystemHelpers sHelpers = { 12, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, +static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, #if DYLD_SHARED_CACHE_SUPPORT &shared_cache_missing, &shared_cache_out_of_date, @@ -1323,7 +1299,8 @@ static dyld::LibSystemHelpers sHelpers = { 12, &dyldGlobalLockAcquire, &dyldGlob &hasPerThreadBufferFor_dlerror, &isLaunchdOwned, &vm_allocate, - &mmap}; + &mmap, + &__cxa_finalize_ranges}; // @@ -1434,7 +1411,7 @@ const struct dyld_all_image_infos* _dyld_get_all_image_infos() return p(); } -#if !__arm__ +#if SUPPORT_ZERO_COST_EXCEPTIONS bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) { DYLD_NO_LOCK_THIS_BLOCK; @@ -1447,7 +1424,7 @@ bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) #endif -#if __i386__ || __x86_64__ || __arm__ +#if __i386__ || __x86_64__ || __arm__ || __arm64__ __attribute__((visibility("hidden"))) void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo) { @@ -1471,7 +1448,6 @@ const char* dyld_image_path_containing_address(const void* addr) return p(addr); } -#if __IPHONE_OS_VERSION_MIN_REQUIRED bool dyld_shared_cache_some_image_overridden() { DYLD_NO_LOCK_THIS_BLOCK; @@ -1481,7 +1457,6 @@ bool dyld_shared_cache_some_image_overridden() _dyld_func_lookup("__dyld_shared_cache_some_image_overridden", (void**)&p); return p(); } -#endif bool dyld_process_is_restricted() @@ -1495,6 +1470,15 @@ bool dyld_process_is_restricted() } +void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) = NULL; + + if (p == NULL) + _dyld_func_lookup("__dyld_dynamic_interpose", (void**)&p); + p(mh, array, count); +} // SPI called __fork diff --git a/src/dyldExceptions.c b/src/dyldExceptions.c index b015be5..487835e 100644 --- a/src/dyldExceptions.c +++ b/src/dyldExceptions.c @@ -35,6 +35,7 @@ #include "mach-o/dyld_priv.h" #include "dyldLibSystemInterface.h" + extern void _ZN4dyld3logEPKcz(const char*, ...); extern struct LibSystemHelpers* _ZN4dyld17gLibSystemHelpersE; @@ -95,11 +96,10 @@ char* __cxa_get_globals_fast() - -#if !__arm__ +#if !__USING_SJLJ_EXCEPTIONS__ // -// The intel versions of dyld uses zero-cost exceptions which are handled by -// linking with a special copy of libunwind.a +// When dyld uses zero-cost exceptions it just needs to implement +// _dyld_find_unwind_sections to return sections inside dyld proper. // extern void* ehStart __asm("section$start$__TEXT$__eh_frame"); @@ -128,10 +128,11 @@ bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info) } } -#endif // !__arm__ - - -#if __arm__ +#else +// +// When dyld uses setjump-longjump exceptions it needs to implement +// routines to push and pop a stack of _Unwind_FunctionContext. +// struct _Unwind_FunctionContext { diff --git a/src/dyldInitialization.cpp b/src/dyldInitialization.cpp index 7957c0c..50d758d 100644 --- a/src/dyldInitialization.cpp +++ b/src/dyldInitialization.cpp @@ -154,7 +154,7 @@ static void rebaseDyld(const struct macho_header* mh, intptr_t slide) const uint8_t type = sect->flags & SECTION_TYPE; if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) - const uint32_t pointerCount = sect->size / sizeof(uintptr_t); + const uint32_t pointerCount = (uint32_t)(sect->size / sizeof(uintptr_t)); uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide); for (uint32_t j=0; j < pointerCount; ++j) { symbolPointers[j] += slide; diff --git a/src/dyldLibSystemGlue.c b/src/dyldLibSystemGlue.c index 20c6cdf..4b6a638 100644 --- a/src/dyldLibSystemGlue.c +++ b/src/dyldLibSystemGlue.c @@ -26,10 +26,6 @@ #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 @@ -78,5 +74,3 @@ 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 f191f78..f39a275 100644 --- a/src/dyldLibSystemInterface.h +++ b/src/dyldLibSystemInterface.h @@ -28,6 +28,8 @@ #include +struct __cxa_range_t { const void* addr; size_t length; }; + #if __cplusplus namespace dyld { #endif @@ -41,7 +43,7 @@ namespace dyld { uintptr_t version; void (*acquireGlobalDyldLock)(); void (*releaseGlobalDyldLock)(); - char* (*getThreadBufferFor_dlerror)(uint32_t sizeRequired); + char* (*getThreadBufferFor_dlerror)(size_t sizeRequired); // addded in version 2 void* (*malloc)(size_t); void (*free)(void*); @@ -70,6 +72,8 @@ namespace dyld { // 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); + // added in version 13 + void (*cxa_finalize_ranges)(const struct __cxa_range_t ranges[], int count); }; #if __cplusplus } diff --git a/src/dyldNew.cpp b/src/dyldNew.cpp index bd99cde..d68fc4e 100644 --- a/src/dyldNew.cpp +++ b/src/dyldNew.cpp @@ -41,7 +41,7 @@ extern "C" void* __dso_handle; #if __LP64__ // room for about ~1000 initial dylibs - #define DYLD_POOL_CHUNK_SIZE 224*1024 + #define DYLD_POOL_CHUNK_SIZE 200*1024 #else // room for about ~900 initial dylibs #define DYLD_POOL_CHUNK_SIZE 150*1024 @@ -102,8 +102,8 @@ void free(void* ptr) // ignore any pointer within dyld (i.e. stuff from pool or static strings) if ( (dyld::gLibSystemHelpers != NULL) && ((ptr < &__dso_handle) || (ptr >= &initialPoolContent[DYLD_POOL_CHUNK_SIZE])) ) { // ignore stuff in any dynamically alloated dyld pools - for (dyld_static_pool* p = initialPool.previousPool; p != NULL; p = p->previousPool) { - if ( (p->pool < ptr) && (ptr < p->end) ) { + for (dyld_static_pool* p = currentPool; p != NULL; p = p->previousPool) { + if ( (p->pool <= ptr) && (ptr < p->end) ) { // do nothing, pool entries can't be reclaimed //dyld::log("free(%p) from dynamic pool\n", ptr); return; diff --git a/src/dyldStartup.s b/src/dyldStartup.s index d340ee8..58055cb 100644 --- a/src/dyldStartup.s +++ b/src/dyldStartup.s @@ -69,33 +69,7 @@ .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 -# stable entry points into dyld - .globl _stub_binding_helper -_stub_binding_helper: - jmp _stub_binding_helper_interface - nop - nop - nop - .globl _dyld_func_lookup -_dyld_func_lookup: - jmp __Z18lookupDyldFunctionPKcPm - nop - nop - nop - - # space for future stable entry points - .space 32 - - #if !TARGET_IPHONE_SIMULATOR .text .align 4, 0x90 @@ -151,11 +125,15 @@ Lapple: movl (%ebx),%ecx # look for NULL ending env[] array 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: - hlt -L_end: + +#if !TARGET_IPHONE_SIMULATOR + .data +__dyld_start_static_picbase: + .long L__dyld_start_picbase +Lmh: .long ___dso_handle +#endif + + #endif /* __i386__ */ @@ -168,24 +146,6 @@ __dyld_start_static: .quad __dyld_start #endif -# stable entry points into dyld - .text - .align 2 - .globl _stub_binding_helper -_stub_binding_helper: - jmp _stub_binding_helper_interface - nop - nop - nop - .globl _dyld_func_lookup -_dyld_func_lookup: - jmp __Z18lookupDyldFunctionPKcPm - nop - nop - nop - - # space for future stable entry points - .space 24 #if !TARGET_IPHONE_SIMULATOR .text @@ -242,21 +202,6 @@ Lapple: movq (%rcx),%r8 __dyld_start_static_picbase: .long L__dyld_start_picbase - .text - .align 2 - .globl _stub_binding_helper -_stub_binding_helper: - b _stub_binding_helper_interface - nop - - .globl _dyld_func_lookup -_dyld_func_lookup: - b _branch_to_lookupDyldFunction - nop - - # space for future stable entry points - .space 24 - // Hack to make ___dso_handle work // Without this local symbol, assembler will error out about in subtraction expression @@ -319,28 +264,65 @@ Lapple: ldr r4, [r3] L__dyld_start_picbase_ptr: .long __dyld_start_static_picbase-L__dyld_start_picbase Lmh: .long ___dso_handle-L3-8 - + +#endif /* __arm__ */ + + + + +#if __arm64__ + .data + .align 3 +__dso_static: + .quad ___dso_handle + .text .align 2 -_branch_to_lookupDyldFunction: - // arm has no "bx label" instruction, so need this island in case lookupDyldFunction() is in thumb - ldr ip, L2 -L1: ldr pc, [pc, ip] -L2: .long _lookupDyldFunction_ptr-8-L1 - - .data - .align 2 -_lookupDyldFunction_ptr: - .long __Z18lookupDyldFunctionPKcPm + .globl __dyld_start +__dyld_start: + mov x28, sp + and sp, x28, #~15 // force 16-byte alignment of stack + mov x0, #0 + mov x1, #0 + stp x1, x0, [sp, #-16]! // make aligned terminating frame + mov fp, sp // set up fp to point to terminating frame + sub sp, sp, #16 // make room for local variables + ldr x0, [x28] // get app's mh into x0 + ldr x1, [x28, #8] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment) + add x2, x28, #16 // get argv into x2 + adrp x4,___dso_handle@page + add x4,x4,___dso_handle@pageoff // get dyld's mh in to x4 + adrp x3,__dso_static@page + ldr x3,[x3,__dso_static@pageoff] // get unslid start of dyld + sub x3,x4,x3 // x3 now has slide of dyld + mov x5,sp // x5 has &startGlue - - .text - .globl dyld_stub_binding_helper -dyld_stub_binding_helper: - trap + // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + mov x16,x0 // save entry point address in x16 + ldr x1, [sp] + cmp x1, #0 + b.ne Lnew + + // LC_UNIXTHREAD way, clean up stack and jump to result + add sp, x28, #8 // restore unaligned stack pointer without app mh + br x16 // jump to the program's entry point + + // LC_MAIN case, set up stack for call to main() +Lnew: mov lr, x1 // simulate return address into _start in libdyld.dylib + ldr x0, [x28, #8] // main param1 = argc + add x1, x28, #16 // main param2 = argv + add x2, x1, x0, lsl #3 + add x2, x2, #8 // main param3 = &env[0] + mov x3, x2 +Lapple: ldr x4, [x3] + add x3, x3, #8 + cmp x4, #0 + b.ne Lapple // main param4 = apple + br x16 + +#endif // __arm64__ -L_end: -#endif /* __arm__ */ /* * dyld calls this function to terminate a process. @@ -357,6 +339,8 @@ _dyld_fatal_error: #elif __x86_64__ || __i386__ int3 nop +#elif __arm64__ + brk #3 #else #error unknown architecture #endif diff --git a/src/dyldSyscallInterface.h b/src/dyldSyscallInterface.h index a9bb0bf..12be7db 100644 --- a/src/dyldSyscallInterface.h +++ b/src/dyldSyscallInterface.h @@ -26,6 +26,7 @@ #ifndef __DYLD_SYSCALL_HELPERS__ #define __DYLD_SYSCALL_HELPERS__ +#include #if __cplusplus namespace dyld { @@ -67,6 +68,12 @@ namespace dyld { void* (*getProcessInfo)(void); // returns dyld_all_image_infos*; int* (*errnoAddress)(); uint64_t (*mach_absolute_time)(); + // Added in version 2 + kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t); + // Added in version 3 + DIR* (*opendir)(const char* path); + int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result); + int (*closedir)(DIR* dirp); }; extern const struct SyscallHelpers* gSyscallHelpers; diff --git a/src/dyld_gdb.cpp b/src/dyld_gdb.cpp index 3f1fa0e..5f07dcf 100644 --- a/src/dyld_gdb.cpp +++ b/src/dyld_gdb.cpp @@ -61,7 +61,7 @@ void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) // append all new images for (uint32_t i=0; i < infoCount; ++i) sImageInfos.push_back(info[i]); - dyld::gProcessInfo->infoArrayCount = sImageInfos.size(); + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); // set infoArray back to base address of vector (other process can now read) dyld::gProcessInfo->infoArray = &sImageInfos[0]; @@ -79,7 +79,7 @@ void syncProcessInfo() sImageInfos.push_back(dyld::gProcessInfo->infoArray[i]); } dyld::gProcessInfo->infoArray = &sImageInfos[0]; - dyld::gProcessInfo->infoArrayCount = sImageInfos.size(); + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); } } dyld::gProcessInfo->notification(dyld_image_info_change, 0, NULL); @@ -127,7 +127,7 @@ void removeImageFromAllImages(const struct mach_header* loadAddress) break; } } - dyld::gProcessInfo->infoArrayCount = sImageInfos.size(); + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); // set infoArray back to base address of vector dyld::gProcessInfo->infoArray = &sImageInfos[0]; @@ -189,7 +189,8 @@ void setAlImageInfosHalt(const char* message, uintptr_t flags) = { 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 + 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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,} }; struct dyld_shared_cache_ranges dyld_shared_cache_ranges; diff --git a/src/dyld_stub_binder.s b/src/dyld_stub_binder.s index 9949a87..c4c45ef 100644 --- a/src/dyld_stub_binder.s +++ b/src/dyld_stub_binder.s @@ -25,9 +25,6 @@ #include -// simulator does not have full libdyld.dylib - just a small libdyld_sim.dylib -#if ! TARGET_IPHONE_SIMULATOR - #ifdef __i386__ @@ -93,112 +90,164 @@ dyld_stub_binder_: #if __x86_64__ -#define MH_PARAM_BP 8 -#define LP_PARAM_BP 16 - -#define RDI_SAVE 0 -#define RSI_SAVE 8 -#define RDX_SAVE 16 -#define RCX_SAVE 24 -#define R8_SAVE 32 -#define R9_SAVE 40 -#define RAX_SAVE 48 -#define 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 - +#define RET_ADDR_RBP 24 +#define LP_PARAM_RBP 16 +#define MH_PARAM_RBP 8 +#define OLD_RBP_RBP 0 + +#define RDI_SAVE_RBP -8 +#define RSI_SAVE_RBP -16 +#define RDX_SAVE_RBP -24 +#define RCX_SAVE_RBP -32 +#define RBX_SAVE_RBP -40 +#define RAX_SAVE_RBP -48 +#define R8_SAVE_RBP -56 +#define R9_SAVE_RBP -64 +#define STATIC_STACK_SIZE 256 // extra padding to allow it to be 64-byte aligned + +#define XMM0_SAVE_RSP 0x00 +#define XMM1_SAVE_RSP 0x10 +#define XMM2_SAVE_RSP 0x20 +#define XMM3_SAVE_RSP 0x30 +#define XMM4_SAVE_RSP 0x40 +#define XMM5_SAVE_RSP 0x50 +#define XMM6_SAVE_RSP 0x60 +#define XMM7_SAVE_RSP 0x70 + /* - * sp+4 lazy binding info offset - * sp+0 address of ImageLoader cache + * sp+16 return address + * sp+8 lazy binding info offset + * sp+0 address of ImageLoader cache */ .align 2,0x90 .globl dyld_stub_binder dyld_stub_binder: pushq %rbp + test $0xF,%rsp # at this point stack should be 16-byte aligned + jne _stack_not_16_byte_aligned_error movq %rsp,%rbp - subq $STACK_SIZE,%rsp # at this point stack is 16-byte aligned because two meta-parameters where pushed - movq %rdi,RDI_SAVE(%rsp) # save registers that might be used as parameters - movq %rsi,RSI_SAVE(%rsp) - movq %rdx,RDX_SAVE(%rsp) - movq %rcx,RCX_SAVE(%rsp) - movq %r8,R8_SAVE(%rsp) - movq %r9,R9_SAVE(%rsp) - movq %rax,RAX_SAVE(%rsp) -misaligned_stack_error_entering_dyld_stub_binder: - 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 + subq $STATIC_STACK_SIZE,%rsp + movq %rdi,RDI_SAVE_RBP(%rbp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE_RBP(%rbp) + movq %rdx,RDX_SAVE_RBP(%rbp) + movq %rcx,RCX_SAVE_RBP(%rbp) + movq %rbx,RBX_SAVE_RBP(%rbp) + movq %rax,RAX_SAVE_RBP(%rbp) + movq %r8, R8_SAVE_RBP(%rbp) + movq %r9, R9_SAVE_RBP(%rbp) + + cmpl $0, _inited(%rip) + jne Linited + movl $0x01,%eax + cpuid # get cpu features to check on xsave instruction support + andl $0x08000000,%ecx # check OSXSAVE bit + movl %ecx,_hasXSave(%rip) + cmpl $0, %ecx + jne LxsaveInfo + movl $1, _inited(%rip) + jmp Lsse + +LxsaveInfo: + movl $0x0D,%eax + movl $0x00,%ecx + cpuid # get xsave parameter info + movl %eax,_features_lo32(%rip) + movl %edx,_features_hi32(%rip) + movl %ecx,_bufferSize32(%rip) + movl $1, _inited(%rip) + +Linited: + cmpl $0, _hasXSave(%rip) + jne Lxsave + +Lsse: + subq $128, %rsp + movdqa %xmm0, XMM0_SAVE_RSP(%rsp) + movdqa %xmm1, XMM1_SAVE_RSP(%rsp) + movdqa %xmm2, XMM2_SAVE_RSP(%rsp) + movdqa %xmm3, XMM3_SAVE_RSP(%rsp) + movdqa %xmm4, XMM4_SAVE_RSP(%rsp) + movdqa %xmm5, XMM5_SAVE_RSP(%rsp) + movdqa %xmm6, XMM6_SAVE_RSP(%rsp) + movdqa %xmm7, XMM7_SAVE_RSP(%rsp) + jmp Lbind + +Lxsave: + movl _bufferSize32(%rip),%eax + movq %rsp, %rdi + subq %rax, %rdi # stack alloc buffer + andq $-64, %rdi # 64-byte align stack + movq %rdi, %rsp + # xsave requires buffer to be zero'ed out + movq $0, %rcx + movq %rdi, %r8 + movq %rdi, %r9 + addq %rax, %r9 +Lz: movq %rcx, (%r8) + addq $8, %r8 + cmpq %r8,%r9 + ja Lz + + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + # note: do not use xsaveopt, it assumes you are using the same + # buffer as previous xsaves, and this thread is on the same cpu. + xsave (%rsp) + +Lbind: + movq MH_PARAM_RBP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo) + movq LP_PARAM_RBP(%rbp),%rsi call __Z21_dyld_fast_stub_entryPvl - movq %rax,%r11 # save target - 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 - movq R8_SAVE(%rsp),%r8 - movq R9_SAVE(%rsp),%r9 - movq RAX_SAVE(%rsp),%rax - addq $STACK_SIZE,%rsp + movq %rax,%r11 # copy jump target + + cmpl $0, _hasXSave(%rip) + jne Lxrstror + + movdqa XMM0_SAVE_RSP(%rsp),%xmm0 + movdqa XMM1_SAVE_RSP(%rsp),%xmm1 + movdqa XMM2_SAVE_RSP(%rsp),%xmm2 + movdqa XMM3_SAVE_RSP(%rsp),%xmm3 + movdqa XMM4_SAVE_RSP(%rsp),%xmm4 + movdqa XMM5_SAVE_RSP(%rsp),%xmm5 + movdqa XMM6_SAVE_RSP(%rsp),%xmm6 + movdqa XMM7_SAVE_RSP(%rsp),%xmm7 + jmp Ldone + +Lxrstror: + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + xrstor (%rsp) + +Ldone: + movq RDI_SAVE_RBP(%rbp),%rdi + movq RSI_SAVE_RBP(%rbp),%rsi + movq RDX_SAVE_RBP(%rbp),%rdx + movq RCX_SAVE_RBP(%rbp),%rcx + movq RBX_SAVE_RBP(%rbp),%rbx + movq RAX_SAVE_RBP(%rbp),%rax + movq R8_SAVE_RBP(%rbp),%r8 + movq R9_SAVE_RBP(%rbp),%r9 + movq %rbp,%rsp popq %rbp addq $16,%rsp # remove meta-parameters - jmp *%r11 # jmp to target + jmp *%r11 # jmp to target + +_stack_not_16_byte_aligned_error: + movdqa %xmm0, 0(%rsp) + int3 + + .data +# Cached info from cpuid. These must be lazily evaluated. +# You cannot initalize these from _dyld_initializer() because +# that function is called from another dylib... +_inited: .long 0 +_features_lo32: .long 0 +_features_hi32: .long 0 +_bufferSize32: .long 0 +_hasXSave: .long 0 #endif @@ -219,10 +268,17 @@ dyld_stub_binder: ldr r0, [sp, #24] // move address ImageLoader cache to 1st parameter ldr r1, [sp, #28] // move lazy info offset 2nd parameter +#if __ARM_ARCH_7K__ + vpush {d0, d1, d2, d3, d4, d5, d6, d7} +#endif // call dyld::fastBindLazySymbol(loadercache, lazyinfo) bl __Z21_dyld_fast_stub_entryPvl mov ip, r0 // move the symbol`s address into ip +#if __ARM_ARCH_7K__ + vpop {d0, d1, d2, d3, d4, d5, d6, d7} +#endif + ldmfd sp!, {r0,r1,r2,r3,r7,lr} // restore registers add sp, sp, #8 // remove meta-parameters @@ -230,7 +286,50 @@ dyld_stub_binder: #endif /* __arm__ */ + +#if __arm64__ + /* + * sp+0 lazy binding info offset + * sp+8 address of ImageLoader cache + */ + .text + .align 2 + .globl dyld_stub_binder +dyld_stub_binder: + stp fp, lr, [sp, #-16]! + mov fp, sp + sub sp, sp, #240 + stp x0,x1, [fp, #-16] ; x0-x7 are int parameter registers + stp x2,x3, [fp, #-32] + stp x4,x5, [fp, #-48] + stp x6,x7, [fp, #-64] + stp x8,x9, [fp, #-80] ; x8 is used for struct returns + stp q0,q1, [fp, #-128] ; q0-q7 are vector/fp parameter registers + stp q2,q3, [fp, #-160] + stp q4,q5, [fp, #-192] + stp q6,q7, [fp, #-224] + + ldr x0, [fp, #24] ; move address ImageLoader cache to 1st parameter + ldr x1, [fp, #16] ; move lazy info offset 2nd parameter + ; call dyld::fastBindLazySymbol(loadercache, lazyinfo) + bl __Z21_dyld_fast_stub_entryPvl + mov x16,x0 ; save target function address in lr + ; restore parameter registers + ldp x0,x1, [fp, #-16] + ldp x2,x3, [fp, #-32] + ldp x4,x5, [fp, #-48] + ldp x6,x7, [fp, #-64] + ldp x8,x9, [fp, #-80] + ldp q0,q1, [fp, #-128] + ldp q2,q3, [fp, #-160] + ldp q4,q5, [fp, #-192] + ldp q6,q7, [fp, #-224] -// simulator does not have full libdyld.dylib - just a small libdyld_sim.dylib -#endif // ! TARGET_IPHONE_SIMULATOR + mov sp, fp + ldp fp, lr, [sp], #16 + add sp, sp, #16 ; remove meta-parameters + br x16 + +#endif + diff --git a/src/glue.c b/src/glue.c index 69b5f22..5616c87 100644 --- a/src/glue.c +++ b/src/glue.c @@ -43,6 +43,20 @@ #include #if TARGET_IPHONE_SIMULATOR #include "dyldSyscallInterface.h" + #include "dyld_images.h" + #include + #include + #if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + typedef struct segment_command_64 macho_segment_command; + typedef struct mach_header_64 macho_header; + typedef struct nlist_64 macho_nlist; + #else + #define LC_SEGMENT_COMMAND LC_SEGMENT + typedef struct segment_command macho_segment_command; + typedef struct mach_header macho_header; + typedef struct nlist macho_nlist; + #endif #endif // from _simple.h in libc @@ -108,10 +122,12 @@ void __assert_rtn(const char* func, const char* file, int line, const char* fail } +int myfprintf(FILE* file, const char* format, ...) __asm("_fprintf"); + // called by libuwind code before aborting size_t fwrite(const void* ptr, size_t size, size_t nitme, FILE* stream) { - return fprintf(stream, "%s", (char*)ptr); + return myfprintf(stream, "%s", (char*)ptr); } // called by libuwind code before aborting @@ -447,6 +463,109 @@ uint64_t mach_absolute_time(void) { return gSyscallHelpers->mach_absolute_time(); } +DIR* opendir(const char* path) { + if ( gSyscallHelpers->version < 3 ) + return NULL; + return gSyscallHelpers->opendir(path); +} + +int readdir_r(DIR* dirp, struct dirent* entry, struct dirent **result) { + if ( gSyscallHelpers->version < 3 ) + return EPERM; + return gSyscallHelpers->readdir_r(dirp, entry, result); +} + +int closedir(DIR* dirp) { + if ( gSyscallHelpers->version < 3 ) + return EPERM; + return gSyscallHelpers->closedir(dirp); +} + + +typedef void (*LoadFuncPtr)(void* shm, void* image, uint64_t timestamp); +typedef void (*UnloadFuncPtr)(void* shm, void* image); + +static LoadFuncPtr sLoadPtr = NULL; +static UnloadFuncPtr sUnloadPtr = NULL; + +// Lookup of coresymbolication functions in host dyld. +static void findCSProcs() { + struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo()); + const struct mach_header* hostDyldMH = imageInfo->dyldImageLoadAddress; + + // find symbol table and slide of host dyld + uintptr_t slide = 0; + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const struct dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = hostDyldMH->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)hostDyldMH)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + const uint8_t* linkEditBase = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( (seg->fileoff == 0) && (seg->filesize != 0) ) + slide = (uintptr_t)hostDyldMH - seg->vmaddr; + if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) + linkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff + slide); + } + break; + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + if ( linkEditBase == NULL ) + return; + symbolTableStrings = (const char*)&linkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( symbolTableStrings == NULL ) + return; + if ( dynSymbolTable == NULL ) + return; + + // scan local symbols in host dyld looking for load/unload functions + const macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; + const macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; + for (const macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + const char* name = &symbolTableStrings[s->n_un.n_strx]; + if ( strcmp(name, "__Z28coresymbolication_load_imageP25CSCppDyldSharedMemoryPagePK11ImageLoadery") == 0 ) + sLoadPtr = (LoadFuncPtr)(s->n_value + slide); + else if ( strcmp(name, "__Z30coresymbolication_unload_imageP25CSCppDyldSharedMemoryPagePK11ImageLoader") == 0 ) + sUnloadPtr = (UnloadFuncPtr)(s->n_value + slide); + } + } +} + +//void coresymbolication_unload_image(void*, const ImageLoader*); +void _Z28coresymbolication_load_imagePvPK11ImageLoadery(void* shm, void* image, uint64_t time) { + // look up function in host dyld just once + if ( sLoadPtr == NULL ) + findCSProcs(); + if ( sLoadPtr != NULL ) + (*sLoadPtr)(shm, image, time); +} + +//void coresymbolication_load_image(void**, const ImageLoader*, uint64_t); +void _Z30coresymbolication_unload_imagePvPK11ImageLoader(void* shm, void* image) { + // look up function in host dyld just once + if ( sUnloadPtr == NULL ) + findCSProcs(); + if ( sUnloadPtr != NULL ) + (*sUnloadPtr)(shm, image); +} + + int* __error(void) { return gSyscallHelpers->errnoAddress(); } @@ -454,7 +573,6 @@ int* __error(void) { void mach_init() { mach_task_self_ = task_self_trap(); //_task_reply_port = _mach_reply_port(); - } mach_port_t mach_task_self_ = MACH_PORT_NULL; diff --git a/src/start_glue.h b/src/start_glue.h index 4e21bec..57b03c1 100644 --- a/src/start_glue.h +++ b/src/start_glue.h @@ -32,6 +32,8 @@ extern "C" void start(); #if __x86_64__ || __i386__ #define address_of_start (void*)((uintptr_t)&start + 1) +#elif __arm64__ + #define address_of_start (void*)((uintptr_t)&start + 4) #elif __arm__ #define address_of_start (void*)((uintptr_t)&start + 2) #endif diff --git a/src/start_glue.s b/src/start_glue.s index b35eb50..3336f66 100644 --- a/src/start_glue.s +++ b/src/start_glue.s @@ -68,5 +68,19 @@ Lstart: #endif /* __arm__ */ + +#if __arm64__ + + .align 2 + .globl _start + .private_extern _start +_start: + nop +Lstart: + bl _exit // result in x0 already in param reg x0 + brk #3 + +#endif /* __arm64__ */ + .subsections_via_symbols diff --git a/src/stub_binding_helper.s b/src/stub_binding_helper.s index 8433475..b00937e 100644 --- a/src/stub_binding_helper.s +++ b/src/stub_binding_helper.s @@ -54,12 +54,12 @@ .text .align 4,0x90 - .globl _fast_stub_binding_helper_interface -_fast_stub_binding_helper_interface: + .globl _stub_binding_helper_i386_old +_stub_binding_helper_i386_old: pushl $0 - .globl _stub_binding_helper_interface + .globl _stub_binding_helper .globl _misaligned_stack_error -_stub_binding_helper_interface: +_stub_binding_helper: subl $STACK_SIZE,%esp # makes stack 16-byte aligned movl %eax,EAX_SAVE(%esp) movl LP_OLD_BP_SAVE(%esp),%eax # get lazy-pointer meta-parameter @@ -133,8 +133,8 @@ _stub_binding_helper_interface2: .text .align 2,0x90 - .globl _stub_binding_helper_interface -_stub_binding_helper_interface: + .globl _stub_binding_helper +_stub_binding_helper: pushq %rbp movq %rsp,%rbp subq $STACK_SIZE,%rsp # at this point stack is 16-byte aligned because two meta-parameters where pushed @@ -196,8 +196,8 @@ _stub_binding_helper_interface: .text .align 2 - .globl _stub_binding_helper_interface -_stub_binding_helper_interface: + .globl _stub_binding_helper +_stub_binding_helper: stmfd sp!, {r0,r1,r2,r3,r7,lr} // save registers add r7, sp, #16 // point FP to previous FP diff --git a/src/threadLocalHelpers.s b/src/threadLocalHelpers.s index 7830d8d..625c320 100644 --- a/src/threadLocalHelpers.s +++ b/src/threadLocalHelpers.s @@ -26,11 +26,29 @@ // 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 +#define RDI_SAVE_RBP -8 +#define RSI_SAVE_RBP -16 +#define RDX_SAVE_RBP -24 +#define RCX_SAVE_RBP -32 +#define RBX_SAVE_RBP -40 +#define R8_SAVE_RBP -48 +#define R9_SAVE_RBP -56 +#define R10_SAVE_RBP -64 +#define R11_SAVE_RBP -72 +#define STATIC_STACK_SIZE 256 // extra padding to allow it to be 64-byte aligned + +#define XMM0_SAVE_RSP 0x00 +#define XMM1_SAVE_RSP 0x10 +#define XMM2_SAVE_RSP 0x20 +#define XMM3_SAVE_RSP 0x30 +#define XMM4_SAVE_RSP 0x40 +#define XMM5_SAVE_RSP 0x50 +#define XMM6_SAVE_RSP 0x60 +#define XMM7_SAVE_RSP 0x70 + + + // returns address of TLV in %rax, all other registers preserved .globl _tlv_get_addr .private_extern _tlv_get_addr _tlv_get_addr: @@ -41,109 +59,127 @@ _tlv_get_addr: addq 16(%rdi),%rax // add offset from descriptor ret LlazyAllocate: - pushq %rbp - movq %rsp, %rbp - subq $STACK_SIZE,%rsp // fxsave uses 512 bytes of store, xsave uses - movq %rdi,-8(%rbp) - movq %rsi,-16(%rbp) - movq %rdx,-24(%rbp) - movq %rcx,-32(%rbp) - movq %r8,-40(%rbp) - movq %r9,-48(%rbp) - movq %r10,-56(%rbp) - movq %r11,-64(%rbp) - 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 + pushq %rbp + movq %rsp,%rbp + subq $STATIC_STACK_SIZE,%rsp + movq %rdi,RDI_SAVE_RBP(%rbp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE_RBP(%rbp) + movq %rdx,RDX_SAVE_RBP(%rbp) + movq %rcx,RCX_SAVE_RBP(%rbp) + movq %rbx,RBX_SAVE_RBP(%rbp) + movq %r8, R8_SAVE_RBP(%rbp) + movq %r9, R9_SAVE_RBP(%rbp) + movq %r10,R10_SAVE_RBP(%rbp) + movq %r11,R11_SAVE_RBP(%rbp) + + cmpl $0, _inited(%rip) + jne Linited + movl $0x01,%eax + cpuid # get cpu features to check on xsave instruction support + andl $0x08000000,%ecx # check OSXSAVE bit + movl %ecx,_hasXSave(%rip) + cmpl $0, %ecx + jne LxsaveInfo + movl $1, _inited(%rip) + jmp Lsse + +LxsaveInfo: + movl $0x0D,%eax + movl $0x00,%ecx + cpuid # get xsave parameter info + movl %eax,_features_lo32(%rip) + movl %edx,_features_hi32(%rip) + movl %ecx,_bufferSize32(%rip) + movl $1, _inited(%rip) + +Linited: + cmpl $0, _hasXSave(%rip) + jne Lxsave + +Lsse: + subq $128, %rsp + movdqa %xmm0, XMM0_SAVE_RSP(%rsp) + movdqa %xmm1, XMM1_SAVE_RSP(%rsp) + movdqa %xmm2, XMM2_SAVE_RSP(%rsp) + movdqa %xmm3, XMM3_SAVE_RSP(%rsp) + movdqa %xmm4, XMM4_SAVE_RSP(%rsp) + movdqa %xmm5, XMM5_SAVE_RSP(%rsp) + movdqa %xmm6, XMM6_SAVE_RSP(%rsp) + movdqa %xmm7, XMM7_SAVE_RSP(%rsp) + jmp Lalloc + +Lxsave: + movl _bufferSize32(%rip),%eax + movq %rsp, %rdi + subq %rax, %rdi # stack alloc buffer + andq $-64, %rdi # 64-byte align stack + movq %rdi, %rsp + # xsave requires buffer to be zero'ed out + movq $0, %rcx + movq %rdi, %r8 + movq %rdi, %r9 + addq %rax, %r9 +Lz: movq %rcx, (%r8) + addq $8, %r8 + cmpq %r8,%r9 + ja Lz + + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + # note: do not use xsaveopt, it assumes you are using the same + # buffer as previous xsaves, and this thread is on the same cpu. + xsave (%rsp) + +Lalloc: + movq RDI_SAVE_RBP(%rbp),%rdi + movq 8(%rdi),%rdi // get key from descriptor + call _tlv_allocate_and_initialize_for_key + + cmpl $0, _hasXSave(%rip) + jne Lxrstror + + movdqa XMM0_SAVE_RSP(%rsp),%xmm0 + movdqa XMM1_SAVE_RSP(%rsp),%xmm1 + movdqa XMM2_SAVE_RSP(%rsp),%xmm2 + movdqa XMM3_SAVE_RSP(%rsp),%xmm3 + movdqa XMM4_SAVE_RSP(%rsp),%xmm4 + movdqa XMM5_SAVE_RSP(%rsp),%xmm5 + movdqa XMM6_SAVE_RSP(%rsp),%xmm6 + movdqa XMM7_SAVE_RSP(%rsp),%xmm7 + jmp Ldone - 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 - movq -32(%rbp),%rcx - movq -24(%rbp),%rdx - movq -16(%rbp),%rsi - movq -8(%rbp),%rdi - addq 16(%rdi),%rax // result = buffer + offset - addq $STACK_SIZE,%rsp - popq %rbp +Lxrstror: + movq %rax,%r11 + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + xrstor (%rsp) + movq %r11,%rax + +Ldone: + movq RDI_SAVE_RBP(%rbp),%rdi + movq RSI_SAVE_RBP(%rbp),%rsi + movq RDX_SAVE_RBP(%rbp),%rdx + movq RCX_SAVE_RBP(%rbp),%rcx + movq RBX_SAVE_RBP(%rbp),%rbx + movq R8_SAVE_RBP(%rbp),%r8 + movq R9_SAVE_RBP(%rbp),%r9 + movq R10_SAVE_RBP(%rbp),%r10 + movq R11_SAVE_RBP(%rbp),%r11 + movq %rbp,%rsp + popq %rbp + addq 16(%rdi),%rax // result = buffer + offset ret + + .data +# Cached info from cpuid. +_inited: .long 0 +_features_lo32: .long 0 +_features_hi32: .long 0 +_bufferSize32: .long 0 +_hasXSave: .long 0 + #endif @@ -184,6 +220,65 @@ LlazyAllocate: ret #endif +#if __arm64__ + // Parameters: X0 = descriptor + // Result: X0 = address of TLV + // Note: all registers except X0, x16, and x17 are preserved + .align 2 + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + ldr x16, [x0, #8] // get key from descriptor + mrs x17, TPIDRRO_EL0 + and x17, x17, #-8 // clear low 3 bits??? + ldr x17, [x17, x16, lsl #3] // get thread allocation address for this key + cbz x17, LlazyAllocate // if NULL, lazily allocate + ldr x16, [x0, #16] // get offset from descriptor + add x0, x17, x16 // return allocation+offset + ret lr + +LlazyAllocate: + stp fp, lr, [sp, #-16]! + mov fp, sp + sub sp, sp, #288 + stp x1, x2, [sp, #-16]! // save all registers that C function might trash + stp x3, x4, [sp, #-16]! + stp x5, x6, [sp, #-16]! + stp x7, x8, [sp, #-16]! + stp x9, x10, [sp, #-16]! + stp x11, x12, [sp, #-16]! + stp x13, x14, [sp, #-16]! + stp x15, x16, [sp, #-16]! + stp q0, q1, [sp, #-32]! + stp q2, q3, [sp, #-32]! + stp q4, q5, [sp, #-32]! + stp q6, q7, [sp, #-32]! + stp x0, x17, [sp, #-16]! // save descriptor + + mov x0, x16 // use key from descriptor as parameter + bl _tlv_allocate_and_initialize_for_key + ldp x16, x17, [sp], #16 // pop descriptor + ldr x16, [x16, #16] // get offset from descriptor + add x0, x0, x16 // return allocation+offset + + ldp q6, q7, [sp], #32 + ldp q4, q5, [sp], #32 + ldp q2, q3, [sp], #32 + ldp q0, q1, [sp], #32 + ldp x15, x16, [sp], #16 + ldp x13, x14, [sp], #16 + ldp x11, x12, [sp], #16 + ldp x9, x10, [sp], #16 + ldp x7, x8, [sp], #16 + ldp x5, x6, [sp], #16 + ldp x3, x4, [sp], #16 + ldp x1, x2, [sp], #16 + + mov sp, fp + ldp fp, lr, [sp], #16 + ret lr + +#endif #if 0 #if __arm__ diff --git a/src/threadLocalVariables.c b/src/threadLocalVariables.c index b5fcd6d..01a73eb 100644 --- a/src/threadLocalVariables.c +++ b/src/threadLocalVariables.c @@ -78,7 +78,7 @@ typedef void (*TermFunc)(void*); -#if __has_feature(tls) +#if __has_feature(tls) || __arm64__ typedef struct TLVHandler { struct TLVHandler *next; @@ -237,8 +237,8 @@ void* tlv_allocate_and_initialize_for_key(pthread_key_t key) if ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_INIT_FUNCTION_POINTERS ) { typedef void (*InitFunc)(void); InitFunc* funcs = (InitFunc*)(sect->addr + slide); - const uint32_t count = sect->size / sizeof(uintptr_t); - for (uint32_t i=count; i > 0; --i) { + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t i=count; i > 0; --i) { InitFunc func = funcs[i-1]; func(); } @@ -401,7 +401,7 @@ void _tlv_atexit(TermFunc func, void* objAddr) if ( list->allocCount == list->allocCount ) { // handle resizing allocation uint32_t newAllocCount = list->allocCount * 2; - uint32_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]); + size_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]); struct TLVTerminatorList* newlist = (struct TLVTerminatorList*)malloc(newAllocSize); newlist->allocCount = newAllocCount; newlist->useCount = list->useCount; diff --git a/unit-tests/build-iPhoneOS-unit-tests b/unit-tests/build-iPhoneOS-unit-tests index d6d19fc..93cb583 100755 --- a/unit-tests/build-iPhoneOS-unit-tests +++ b/unit-tests/build-iPhoneOS-unit-tests @@ -17,9 +17,9 @@ echo "#!/bin/sh" > /var/root/testing/run-all-tests chmod +x /var/root/testing/run-all-tests # do every combination of OS version and architectures -for os in "6.0" "3.0" +for os in "7.0" do - for arch in armv7 armv6 thumb + for arch in armv7 arm64 do # make copy of tests cp -r ${TEST_CASE_DIR}/../../unit-tests /var/root/testing/unit-tests-${arch}-${os} diff --git a/unit-tests/include/common.makefile b/unit-tests/include/common.makefile index 36b4ed9..40cfde7 100644 --- a/unit-tests/include/common.makefile +++ b/unit-tests/include/common.makefile @@ -24,7 +24,7 @@ IOSROOT = ifeq "$(OS_NAME)" "iPhoneOS" #IOSROOT = $(shell xcodebuild -version -sdk iphoneos.internal Path) - IOSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.Internal.sdk + IOSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.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) diff --git a/unit-tests/test-cases/dlclose-order/Makefile b/unit-tests/test-cases/dlclose-order/Makefile new file mode 100644 index 0000000..6447f72 --- /dev/null +++ b/unit-tests/test-cases/dlclose-order/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2013 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# foo depends on bar depends on baz +# foo links with baz then bar +# verify that baz terminators run before bar terminators + + +all-check: all check + +check: + ./main libfoo.dylib + ./main libfoo2.dylib + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CXX} ${CXXFLAGS} -dynamiclib baz.cxx libbase.dylib -o libbaz.dylib + ${CXX} ${CXXFLAGS} -dynamiclib bar.cxx libbaz.dylib libbase.dylib -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib libbaz.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbaz.dylib libbar.dylib -o libfoo2.dylib + ${CC} ${CCFLAGS} main.c libbase.dylib -I${TESTROOT}/include -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo2.dylib libbaz.dylib libbar.dylib libbase.dylib + + diff --git a/unit-tests/test-cases/dlclose-order/bar.cxx b/unit-tests/test-cases/dlclose-order/bar.cxx new file mode 100644 index 0000000..94fddfe --- /dev/null +++ b/unit-tests/test-cases/dlclose-order/bar.cxx @@ -0,0 +1,12 @@ +#include "base.h" + + +class Bar { +public: + Bar() { if ( bazInitied) barInitied = true; } + ~Bar() { if ( barInitied && !bazTeminated ) barTeminated = true; } +}; + + +Bar bar; + diff --git a/unit-tests/test-cases/dlclose-order/base.c b/unit-tests/test-cases/dlclose-order/base.c new file mode 100644 index 0000000..d128ed8 --- /dev/null +++ b/unit-tests/test-cases/dlclose-order/base.c @@ -0,0 +1,8 @@ +#include "base.h" + +bool barInitied = false; +bool barTeminated = false; + +bool bazInitied = false; +bool bazTeminated = false; + diff --git a/unit-tests/test-cases/dlclose-order/base.h b/unit-tests/test-cases/dlclose-order/base.h new file mode 100644 index 0000000..75b5960 --- /dev/null +++ b/unit-tests/test-cases/dlclose-order/base.h @@ -0,0 +1,9 @@ +#include + +extern bool barInitied; +extern bool barTeminated; + +extern bool bazInitied; +extern bool bazTeminated; + + diff --git a/unit-tests/test-cases/dlclose-order/baz.cxx b/unit-tests/test-cases/dlclose-order/baz.cxx new file mode 100644 index 0000000..415a9c6 --- /dev/null +++ b/unit-tests/test-cases/dlclose-order/baz.cxx @@ -0,0 +1,12 @@ +#include "base.h" + +class Baz { +public: + Baz() { bazInitied = true; } + ~Baz() { bazTeminated = true; } + +}; + + +Baz baz; + diff --git a/src/dyld_debug.c b/unit-tests/test-cases/dlclose-order/foo.c similarity index 85% rename from src/dyld_debug.c rename to unit-tests/test-cases/dlclose-order/foo.c index 8132b2c..39ab8be 100644 --- a/src/dyld_debug.c +++ b/unit-tests/test-cases/dlclose-order/foo.c @@ -1,6 +1,5 @@ -/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- - * - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,6 +20,7 @@ * * @APPLE_LICENSE_HEADER_END@ */ - -int dummy_dyld_symbol = 1; - +int foo() +{ + return 1; +} diff --git a/unit-tests/test-cases/dlclose-order/main.c b/unit-tests/test-cases/dlclose-order/main.c new file mode 100644 index 0000000..c776426 --- /dev/null +++ b/unit-tests/test-cases/dlclose-order/main.c @@ -0,0 +1,60 @@ +/* + * 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(int argc, const char* argv[]) +{ + // regular open + void* handle = dlopen(argv[1], RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlclose-order: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed"); + return EXIT_SUCCESS; + } + + // regular close + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose-order: dlclose() failed"); + return EXIT_SUCCESS; + } + + if ( !bazTeminated ) { + FAIL("dlclose-order: baz not terminated"); + return EXIT_SUCCESS; + } + + if ( !barTeminated ) { + FAIL("dlclose-order: bar not terminated"); + return EXIT_SUCCESS; + } + + PASS("dlclose-order"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/dlopen-leak-threaded/main.c b/unit-tests/test-cases/dlopen-leak-threaded/main.c index ed9840e..c1b3a37 100644 --- a/unit-tests/test-cases/dlopen-leak-threaded/main.c +++ b/unit-tests/test-cases/dlopen-leak-threaded/main.c @@ -75,7 +75,7 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); int status = system(cmd); if ( status == EXIT_SUCCESS ) PASS("dlopen-leak-threaded"); diff --git a/unit-tests/test-cases/dlopen-leak/main.c b/unit-tests/test-cases/dlopen-leak/main.c index d84b8e2..3ff66e5 100644 --- a/unit-tests/test-cases/dlopen-leak/main.c +++ b/unit-tests/test-cases/dlopen-leak/main.c @@ -42,7 +42,7 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); int result = system(cmd); if ( result == EXIT_SUCCESS ) PASS("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 a953bff..33c608b 100644 --- a/unit-tests/test-cases/dlopen-search-leak/main.c +++ b/unit-tests/test-cases/dlopen-search-leak/main.c @@ -40,7 +40,7 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); int result = system(cmd); if ( result == EXIT_SUCCESS ) PASS("dlopen-search-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 4d90de4..48b5b72 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,7 +54,7 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); int result = system(cmd); if ( result == EXIT_SUCCESS ) PASS("dlopen_preflight-leak-image-deny-single"); diff --git a/unit-tests/test-cases/dlopen_preflight-leak/main.c b/unit-tests/test-cases/dlopen_preflight-leak/main.c index 3f04f2e..0941597 100644 --- a/unit-tests/test-cases/dlopen_preflight-leak/main.c +++ b/unit-tests/test-cases/dlopen_preflight-leak/main.c @@ -38,7 +38,7 @@ int main() // execute leaks command on myself char cmd[512]; - sprintf(cmd, "leaks %u > /dev/null\n", getpid()); + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); int result = system(cmd); if ( result == EXIT_SUCCESS ) PASS("dlopen_preflight-leak"); diff --git a/unit-tests/test-cases/image-remove-crash/Makefile b/unit-tests/test-cases/image-remove-crash/Makefile new file mode 100644 index 0000000..050a99c --- /dev/null +++ b/unit-tests/test-cases/image-remove-crash/Makefile @@ -0,0 +1,17 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o foo.bundle foo.c -bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/unit-tests/test-cases/image-remove-crash/foo.c b/unit-tests/test-cases/image-remove-crash/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/image-remove-crash/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/image-remove-crash/main.c b/unit-tests/test-cases/image-remove-crash/main.c new file mode 100644 index 0000000..612671f --- /dev/null +++ b/unit-tests/test-cases/image-remove-crash/main.c @@ -0,0 +1,74 @@ +/* + * 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 + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static bool sNotified = false; + +static void removeNotification2(const struct mach_header *mh, intptr_t vmaddr_slide) +{ +} + +static void removeNotification(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + // crash in dyld possible if remove callbacks added within a remove callback + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + + sNotified = true; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images go away + _dyld_register_func_for_remove_image(removeNotification); + + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-remove-crash: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-remove-crash: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } + + if ( sNotified ) + PASS("image-remove-crash"); + else + FAIL("image-remove-crash"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/image-state-deny-cache-leak/Makefile b/unit-tests/test-cases/image-state-deny-cache-leak/Makefile new file mode 100644 index 0000000..48aff7d --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-cache-leak/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib -o libfoo1.dylib foo.c -lz + ${CC} ${CCFLAGS} -dynamiclib -o libfoo2.dylib foo.c -weak-lz + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main *.dylib + diff --git a/unit-tests/test-cases/image-state-deny-cache-leak/bar.c b/unit-tests/test-cases/image-state-deny-cache-leak/bar.c new file mode 100644 index 0000000..30ade8f --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-cache-leak/bar.c @@ -0,0 +1,2 @@ + +void bar() { } diff --git a/unit-tests/test-cases/image-state-deny-cache-leak/foo.c b/unit-tests/test-cases/image-state-deny-cache-leak/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-cache-leak/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/image-state-deny-cache-leak/main.c b/unit-tests/test-cases/image-state-deny-cache-leak/main.c new file mode 100644 index 0000000..edcb894 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-cache-leak/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "libz" in its name from loading. +// +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "libz") != NULL ) + return "can't load libz"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + void* handle = dlopen("libfoo1.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-cache-leak: dlopen(libfoo.dylib) should have failed"); + return EXIT_SUCCESS; + } + + handle = dlopen("libfoo2.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-deny-cache-leak: dlopen(libfoo.dylib) should have succeeded"); + return EXIT_SUCCESS; + } + dlclose(handle); + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("image-state-deny-cache-leak"); + else + FAIL("image-state-deny-cache-leak"); + + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/image-state-deny-leak/Makefile b/unit-tests/test-cases/image-state-deny-leak/Makefile new file mode 100644 index 0000000..364f994 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-leak/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib -o libbar1.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -o libbar2.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -o libfoo.dylib foo.c libbar1.dylib -weak_library libbar2.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main *.dylib + diff --git a/unit-tests/test-cases/image-state-deny-leak/bar.c b/unit-tests/test-cases/image-state-deny-leak/bar.c new file mode 100644 index 0000000..30ade8f --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-leak/bar.c @@ -0,0 +1,2 @@ + +void bar() { } diff --git a/unit-tests/test-cases/image-state-deny-leak/foo.c b/unit-tests/test-cases/image-state-deny-leak/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-leak/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/unit-tests/test-cases/image-state-deny-leak/main.c b/unit-tests/test-cases/image-state-deny-leak/main.c new file mode 100644 index 0000000..42e369e --- /dev/null +++ b/unit-tests/test-cases/image-state-deny-leak/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +const char* sBlockName = NULL; + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, sBlockName) != NULL ) + return "can't load blocked name"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + sBlockName = "libbar1"; + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-leak: dlopen(libfoo.dylib) should have failed"); + return EXIT_SUCCESS; + } + + sBlockName = "libbar2"; + handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-deny-leak: dlopen(libfoo.dylib) should have succeeded"); + return EXIT_SUCCESS; + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null \n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("image-state-deny-leak"); + else + FAIL("image-state-deny-leak"); + + + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c b/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c index 077a8a2..18041ac 100644 --- a/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c +++ b/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c @@ -26,7 +26,7 @@ char* mystrdup(const char* in) { - return "hello"; + return strdup("hello"); } DYLD_INTERPOSE(mystrdup, strdup) diff --git a/unit-tests/test-cases/interpose-basic/mystrdup.c b/unit-tests/test-cases/interpose-basic/mystrdup.c index 077a8a2..18041ac 100644 --- a/unit-tests/test-cases/interpose-basic/mystrdup.c +++ b/unit-tests/test-cases/interpose-basic/mystrdup.c @@ -26,7 +26,7 @@ char* mystrdup(const char* in) { - return "hello"; + return strdup("hello"); } DYLD_INTERPOSE(mystrdup, strdup) diff --git a/unit-tests/test-cases/interpose-dynamic-basic/Makefile b/unit-tests/test-cases/interpose-dynamic-basic/Makefile new file mode 100644 index 0000000..b1cf89d --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-basic/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/unit-tests/test-cases/interpose-dynamic-basic/main.c b/unit-tests/test-cases/interpose-dynamic-basic/main.c new file mode 100644 index 0000000..b302512 --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-basic/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +extern const struct mach_header __dso_handle; + + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef char* (*DupProc)(const char*); + +char* mystrdup(const char* in) +{ + return "hello"; +} + +static const struct dyld_interpose_tuple sTable[] = { {&mystrdup, &strdup} }; + +int main() +{ + const char* preCall = strdup("123"); + DupProc preProc = &strdup; + + dyld_dynamic_interpose(&__dso_handle, sTable, 1); + + const char* postCall = strdup("123"); + DupProc postProc = &strdup; + + if ( strcmp(preCall, "123") != 0 ) { + FAIL("interpose-dynamic-basic control strdup failed"); + return EXIT_SUCCESS; + } + + if ( strcmp(postCall, "hello") != 0 ) { + FAIL("interpose-dynamic-basic interposed lazy strdup failed"); + return EXIT_SUCCESS; + } + + if ( preProc == postProc ) { + FAIL("interpose-dynamic-basic interposed non-lazy strdup failed"); + return EXIT_SUCCESS; + } + + PASS("interpose-dynamic-basic"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile b/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile new file mode 100644 index 0000000..410d9ae --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c b/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c new file mode 100644 index 0000000..5bfa892 --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c @@ -0,0 +1,26 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + + +int foo() { return 0; } + +int alt_foo() { return 10; } + + + + +static const struct dyld_interpose_tuple sTable[] = { {&alt_foo, &foo} }; + + +__attribute__((constructor)) +void init() +{ + // switch main executable to use alt_foo() when it calls foo() + dyld_dynamic_interpose((const struct mach_header*)_NSGetMachExecuteHeader(), sTable, 1); + +} + diff --git a/unit-tests/test-cases/interpose-dynamic-dlsym/main.c b/unit-tests/test-cases/interpose-dynamic-dlsym/main.c new file mode 100644 index 0000000..da21a5c --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-dlsym/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*FooProc)(); + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("interpose-dynamic-dlsym: dlopen() error: %s", dlerror()); + return 0; + } + FooProc dlsym_foo = (FooProc)dlsym(handle, "foo"); + if ( dlsym_foo == NULL ) { + FAIL("interpose-dynamic-dlsym: dlsym() error: %s", dlerror()); + return 0; + } + + int result = (*dlsym_foo)(); + if ( result == 10 ) + PASS("interpose-dynamic-dlsym"); + else + FAIL("interpose-dynamic-dlsym"); + + return 0; +} diff --git a/unit-tests/test-cases/interpose-dynamic-lazy/Makefile b/unit-tests/test-cases/interpose-dynamic-lazy/Makefile new file mode 100644 index 0000000..61586cc --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-lazy/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/unit-tests/test-cases/interpose-dynamic-lazy/foo.c b/unit-tests/test-cases/interpose-dynamic-lazy/foo.c new file mode 100644 index 0000000..49b5897 --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-lazy/foo.c @@ -0,0 +1,27 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + + + +int foo() { return 0; } + +int alt_foo() { return 10; } + + + + +static const struct dyld_interpose_tuple sTable[] = { {&alt_foo, &foo} }; + + +__attribute__((constructor)) +void init() +{ + + dyld_dynamic_interpose((const struct mach_header*)_NSGetMachExecuteHeader(), sTable, 1); + +} + diff --git a/unit-tests/test-cases/interpose-dynamic-lazy/main.c b/unit-tests/test-cases/interpose-dynamic-lazy/main.c new file mode 100644 index 0000000..89e9ad9 --- /dev/null +++ b/unit-tests/test-cases/interpose-dynamic-lazy/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); + +int main() +{ + int result = foo(); + if ( result == 10 ) + PASS("interpose-dynamic-lazy"); + else + FAIL("interpose-dynamic-lazy"); + + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/rpath-no-trailing-slash/Makefile b/unit-tests/test-cases/rpath-no-trailing-slash/Makefile new file mode 100644 index 0000000..e10933d --- /dev/null +++ b/unit-tests/test-cases/rpath-no-trailing-slash/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2014 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath used to locate a dylib it links against +# + +all-check: all check + +check: + ./hole/main + +all: main + + +main: + mkdir -p hole + ${CC} foo.c -dynamiclib -o hole/libfoo.dylib -install_name @rpath/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o hole/main hole/libfoo.dylib -Wl,-rpath,@executable_path + +clean: + ${RM} -rf *~ hole diff --git a/unit-tests/test-cases/rpath-no-trailing-slash/foo.c b/unit-tests/test-cases/rpath-no-trailing-slash/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/unit-tests/test-cases/rpath-no-trailing-slash/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/unit-tests/test-cases/rpath-no-trailing-slash/main.c b/unit-tests/test-cases/rpath-no-trailing-slash/main.c new file mode 100644 index 0000000..97bfaa2 --- /dev/null +++ b/unit-tests/test-cases/rpath-no-trailing-slash/main.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-no-trailing-slash"); + return EXIT_SUCCESS; +} diff --git a/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o b/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o new file mode 100644 index 0000000000000000000000000000000000000000..92572692039a8219f08d55b879604c342823188e GIT binary patch literal 1364 zcmbVLO=}ZT6undX5kHy)aiL;q7B0kv1Q)KGn&1>%1c{JMd7WgYB;ZU+lVa^Eiy{;e zT=*mEKj=~@h&x^O54dn4WF^H#<2m#0Yiq}Vc*uR{oqO-QxpQaUmtQ|l=0vdB3{1cWjjizvu&)|5g*HPcV9X%e9@xFrjasvDrroZk8`hh~K8J;jWk_-` zrq%B|0XC*uX;jV}u8&ErU-TOj_d?T6x&yPmm9#VUI$AoX{2BdmR`<{xYh6~qBV*dV zcq3>J%vSQUn}o(Zt-2U_L&Z?}tIn^CdiW9QvBH?BW7ao=ICA>W`Hiew4wUO?j{P}Y z6>*P{x>)=std2js0v>@wF!H1hBbY@<#cOl9cgVU6{~g%qgtHirwLg(czW93T@%*fwV;;sLN1CU7{x$Cf@;?4U z-h1TjYo7LMlTDGz(A~h#T57s@@rr`&g_zMNg0&-SHO9EH17U2sqqF! zOaN}f3Z8gwjumRehdRmYHZCPF!9EyoYMl2B=TG0`eT|njPGa0)ks9Z7&S&CRE4bxi rTEXU%UN1~H%YkLl=!2+pn&9@xaodzTL4P@l5*CX>srmMH^0|Hk$=;*c literal 0 HcmV?d00001 diff --git a/unit-tests/test-cases/text-perm-alt-segment/Makefile b/unit-tests/test-cases/text-perm-alt-segment/Makefile new file mode 100644 index 0000000..163719d --- /dev/null +++ b/unit-tests/test-cases/text-perm-alt-segment/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +### +### This test case is to verify __TEXT relocations work in dylibs +### +### + + +ifeq "i386" "$(ARCH)" + EXTRA_DEP = libfoo.dylib +else + +endif + +all-check: all check + +check: + ./main + +all: ${EXTRA_DEP} + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c ${EXTRA_DEP} + +libfoo.dylib: + ${CC} ${CCFLAGS} foo.c -c + ${LD} -r -arch ${ARCH} foo.o -rename_section __TEXT __text MYTEXT mytext -o foo2.o + ${CC} ${CCFLAGS} -dynamiclib foo2.o -o libfoo.dylib -Wl,-segprot,MYTEXT,rwx,r-x -read_only_relocs suppress + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib foo.o foo2.o + diff --git a/unit-tests/test-cases/text-perm-alt-segment/foo.c b/unit-tests/test-cases/text-perm-alt-segment/foo.c new file mode 100644 index 0000000..9f41b6b --- /dev/null +++ b/unit-tests/test-cases/text-perm-alt-segment/foo.c @@ -0,0 +1,36 @@ +/* + * 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 + +int x = 5; + +__attribute__((constructor)) +void myInit() { + x = 10; +} + + +__attribute__((section("MYTEXT,myconst,regular,pure_instructions"))) +const void* p = &myInit; + + diff --git a/unit-tests/test-cases/text-perm-alt-segment/main.c b/unit-tests/test-cases/text-perm-alt-segment/main.c new file mode 100644 index 0000000..e6f9eed --- /dev/null +++ b/unit-tests/test-cases/text-perm-alt-segment/main.c @@ -0,0 +1,36 @@ +/* + * 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(int argc, const char* argv[]) +{ + PASS("text-reloc-alt-segment"); + return EXIT_SUCCESS; +} + + diff --git a/unit-tests/test-cases/tlv-basic/Makefile b/unit-tests/test-cases/tlv-basic/Makefile index 977b5a8..cf25175 100644 --- a/unit-tests/test-cases/tlv-basic/Makefile +++ b/unit-tests/test-cases/tlv-basic/Makefile @@ -36,7 +36,7 @@ check: all: main main : main.c - clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c -o main clean: diff --git a/unit-tests/test-cases/tlv-basic/main.c b/unit-tests/test-cases/tlv-basic/main.c index 0fa5240..1bbdc61 100644 --- a/unit-tests/test-cases/tlv-basic/main.c +++ b/unit-tests/test-cases/tlv-basic/main.c @@ -26,6 +26,9 @@ #include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() +__attribute__((weak)) +void foobar() { +} __thread int a; __thread int b = 5; @@ -35,45 +38,82 @@ __thread static int d = 5; static void* work(void* arg) { + uintptr_t offset = (uintptr_t)arg; //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + void* result = malloc(10); if ( a != 0 ) { - FAIL("tlv-basic: get_a() non-zero"); + FAIL("tlv-basic: a non-zero on slow-path"); exit(0); } if ( b != 5 ) { - FAIL("tlv-basic: get_b() not five"); + FAIL("tlv-basic: b not five"); exit(0); } + if ( c != 0 ) { - FAIL("tlv-basic: get_c() non-zero"); + FAIL("tlv-basic: c non-zero"); exit(0); } + if ( d != 5 ) { - FAIL("tlv-basic: get_d() not five"); + FAIL("tlv-basic: gd not five"); exit(0); } - return NULL; + + for(int i=0; i < 10000; ++i) { + a = 3 + offset + i; + b = 7 + offset + i; + c = 11 + offset + i; + d = 13 + offset + i; + foobar(); + if ( a != 3 + offset + i ) { + FAIL("tlv-basic: a not three"); + exit(0); + } + if ( b != 7 + offset + i ) { + FAIL("tlv-basic: b not seven"); + exit(0); + } + if ( c != 11 + offset + i ) { + FAIL("tlv-basic: c not eleven"); + exit(0); + } + if ( d != 13 + offset + i ) { + FAIL("tlv-basic: d not thirteen"); + exit(0); + } + } + + return result; } int main() { pthread_t worker1; - if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) { + if ( pthread_create(&worker1, NULL, work, (void*)1) != 0 ) { FAIL("pthread_create failed"); exit(0); } pthread_t worker2; - if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) { + if ( pthread_create(&worker2, NULL, work, (void*)10) != 0 ) { FAIL("pthread_create failed"); exit(0); } + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, (void*)100) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + void* result; //fprintf(stderr, "waiting for worker 1\n"); pthread_join(worker1, &result); //fprintf(stderr, "waiting for worker 2\n"); pthread_join(worker2, &result); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); work(NULL); diff --git a/unit-tests/test-cases/tlv-dylib/Makefile b/unit-tests/test-cases/tlv-dylib/Makefile index ff8103e..9011041 100644 --- a/unit-tests/test-cases/tlv-dylib/Makefile +++ b/unit-tests/test-cases/tlv-dylib/Makefile @@ -34,8 +34,8 @@ check: ./main all: - clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include foo.c -dynamiclib -o libfoo.dylib - clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include foo.c -dynamiclib -o libfoo.dylib + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main clean: diff --git a/unit-tests/test-cases/upward-dylib-init-order/Makefile b/unit-tests/test-cases/upward-dylib-init-order/Makefile index a0a54a1..bc48b5c 100644 --- a/unit-tests/test-cases/upward-dylib-init-order/Makefile +++ b/unit-tests/test-cases/upward-dylib-init-order/Makefile @@ -40,14 +40,15 @@ check_1: all_1: ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib common.c -o libcommon.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib u.c libcommon.dylib -o libu.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib u2.c libcommon.dylib -o libu2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib u.c libcommon.dylib -o libu.dylib -Wl,-upward_library,libu2.dylib ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib c.c libcommon.dylib -o libc.dylib -Wl,-upward_library,libu.dylib ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib b.c libcommon.dylib -o libb.dylib libc.dylib - ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libb.dylib libu.dylib libcommon.dylib -o main + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libb.dylib libcommon.dylib -o main clean: - ${RM} ${RMFLAGS} *~ main libu.dylib libb.dylib libc.dylib libcommon.dylib + ${RM} ${RMFLAGS} *~ main libu.dylib libu2.dylib libb.dylib libc.dylib libcommon.dylib diff --git a/unit-tests/test-cases/upward-dylib-init-order/common.c b/unit-tests/test-cases/upward-dylib-init-order/common.c index c703835..663e4a1 100644 --- a/unit-tests/test-cases/upward-dylib-init-order/common.c +++ b/unit-tests/test-cases/upward-dylib-init-order/common.c @@ -3,27 +3,35 @@ static bool b = false; static bool u = false; +static bool u2 = false; static bool isOk = true; void setB() { - if ( u || b ) + if ( u || b || u2 ) isOk = false; b = true; } void setU() { - if ( u ) + if ( u || u2 ) isOk = false; u = true; } +void setU2() +{ + if ( u2 ) + isOk = false; + u2 = true; +} + // return true iff // setB() was called, then setU() bool ok() { //fprintf(stderr, "isOk=%d, u=%d, b=%d\n", isOk, u, b); - return isOk && u && b; + return isOk && u && b && u2; } diff --git a/unit-tests/test-cases/upward-dylib-init-order/common.h b/unit-tests/test-cases/upward-dylib-init-order/common.h index 0094023..05427dc 100644 --- a/unit-tests/test-cases/upward-dylib-init-order/common.h +++ b/unit-tests/test-cases/upward-dylib-init-order/common.h @@ -2,6 +2,7 @@ extern void setB(); extern void setU(); +extern void setU2(); extern bool ok(); diff --git a/unit-tests/test-cases/upward-dylib-init-order/u2.c b/unit-tests/test-cases/upward-dylib-init-order/u2.c new file mode 100644 index 0000000..7add33f --- /dev/null +++ b/unit-tests/test-cases/upward-dylib-init-order/u2.c @@ -0,0 +1,10 @@ +#include +#include "common.h" + + +static __attribute__((constructor)) void myInit() +{ + setU2(); + //fprintf(stderr, "init u2\n"); +} + -- 2.45.2