--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+undef $/;
+
+if(@ARGV == 0)
+{
+ print "Usage: $0 <macho-executable> [<macho-executable> ...]\n";
+ exit 1;
+}
+
+my $arg;
+foreach $arg (@ARGV)
+{
+ open IN, "<$arg" or die $!;
+ my $in = <IN>;
+ close IN or die $!;
+
+ if($in =~ s{/usr/lib/dyld}{/usr/lib/dyle})
+ {
+ open OUT, ">$arg" or die $!;
+ print OUT $in;
+ close OUT or die $!;
+ }
+ else
+ {
+ print STDERR "ERROR: $arg\n";
+ exit 1;
+ }
+}
-.TH DYLD 1 "November 25, 2008" "Apple Inc."
+.TH DYLD 1 "December 14, 2009" "Apple Inc."
.SH NAME
dyld \- the dynamic link editor
.SH SYNOPSIS
.br
DYLD_FALLBACK_FRAMEWORK_PATH
.br
+DYLD_VERSIONED_FRAMEWORK_PATH
+.br
DYLD_LIBRARY_PATH
.br
DYLD_FALLBACK_LIBRARY_PATH
.br
+DYLD_VERSIONED_LIBRARY_PATH
+.br
DYLD_ROOT_PATH
.br
DYLD_SHARED_REGION
.br
DYLD_PRINT_DOFS
.br
-DYLD_NO_PIE
+DYLD_PRINT_RPATHS
.br
DYLD_SHARED_CACHE_DIR
.br
By default, it is set to
/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks
.TP
+.B DYLD_VERSIONED_FRAMEWORK_PATH
+This is a colon separated list of directories that contain potential override frameworks.
+The dynamic linker searches these directories for frameworks. For
+each framework found dyld looks at its LC_ID_DYLIB and gets the current_version
+and install name. Dyld then looks for the framework at the install name path.
+Whichever has the larger current_version value will be used in the process whenever
+a framework with that install name is required. This is similar to DYLD_FRAMEWORK_PATH
+except instead of always overriding, it only overrides is the supplied framework is newer.
+Note: dyld does not check the framework's Info.plist to find its version. Dyld only
+checks the -currrent_version number supplied when the framework was created.
+.TP
.B DYLD_LIBRARY_PATH
This is a colon separated list of directories that contain libraries. The
dynamic linker searches these directories before it searches the default
By default, it is set
to $(HOME)/lib:/usr/local/lib:/lib:/usr/lib.
.TP
+.B DYLD_VERSIONED_LIBRARY_PATH
+This is a colon separated list of directories that contain potential override libraries.
+The dynamic linker searches these directories for dynamic libraries. For
+each library found dyld looks at its LC_ID_DYLIB and gets the current_version
+and install name. Dyld then looks for the library at the install name path.
+Whichever has the larger current_version value will be used in the process whenever
+a dylib with that install name is required. This is similar to DYLD_LIBRARY_PATH
+except instead of always overriding, it only overrides is the supplied library is newer.
+.TP
.B DYLD_ROOT_PATH
This is a colon separated list of directories. The dynamic linker will prepend each of
this directory paths to every image access until a file is found.
.B DYLD_PRINT_DOFS
Causes dyld to print out information about dtrace static probes registered with the kernel.
.TP
-.B DYLD_NO_PIE
-Causes dyld to not randomize the load addresses of images in a process where the main
-executable was built position independent. This can be helpful when trying to reproduce
-and debug a problem in a PIE.
+.B DYLD_PRINT_RPATHS
+Cause dyld to print a line each time it expands an @rpath variable and whether
+that expansion was successful or not.
.TP
.B DYLD_SHARED_CACHE_DIR
This is a directory containing dyld shared cache files. This variable can be used in
Causes dyld to not check that the inode and mod-time of files in the shared cache match
the requested dylib on disk. Thus a program can be made to run with the dylib in the
shared cache even though the real dylib has been updated on disk.
+.TP
.SH DYNAMIC LIBRARY LOADING
Unlike many other operating systems, Darwin does not locate dependent dynamic libraries
via their leaf file name. Instead the full path to each dylib is used (e.g. /usr/lib/libSystem.B.dylib).
.TP
.B @executable_path/
This variable is replaced with the path to the directory containing the main executable for
-the process. This is useful for .app directories where the main executable is in a well
-known location inside the .app directory. A typical load path for an embedded framework would
-look like @executable_path/../Frameworks/Foo.framework/Versions/A/Foo.
+the process. This is useful for loading dylibs/frameworks embedded in a .app directory.
+If the main executable file is at /some/path/My.app/Contents/MacOS/My and a framework dylib
+file is at /some/path/My.app/Contents/Frameworks/Foo.framework/Versions/A/Foo, then
+the framework load path could be encoded as
+@executable_path/../Frameworks/Foo.framework/Versions/A/Foo and the .app directory could be
+moved around in the file system and dyld will still be able to load the embedded framework.
.TP
.B @loader_path/
This variable is replaced with the path to the directory containing the mach-o binary which
-contains the load path. This is useful for a plug-in that has an embedded framework. @executable_path/
-is not helpful because you may not know where the plugin-in will be installed relative to the
-main executable, or there may be multiple applications that load the plug-in.
-A typical load path for an embedded framework for reference a sibling framework would
-look like @loader_path/../../../Frameworks/Foo.framework/Versions/A/Foo.
+contains the load command using @loader_path. Thus, in every binary, @loader_path resolves to
+a different path, whereas @executable_path always resolves to the same path. @loader_path is
+useful as the load path for a framework/dylib embedded in a plug-in, if the final file
+system location of the plugin-in unknown (so absolute paths cannot be used) or if the plug-in
+is used by multiple applications (so @executable_path cannot be used). If the plug-in mach-o
+file is at /some/path/Myfilter.plugin/Contents/MacOS/Myfilter and a framework dylib
+file is at /some/path/Myfilter.plugin/Contents/Frameworks/Foo.framework/Versions/A/Foo, then
+the framework load path could be encoded as
+@loader_path/../Frameworks/Foo.framework/Versions/A/Foo and the Myfilter.plugin directory could
+be moved around in the file system and dyld will still be able to load the embedded framework.
.TP
.B @rpath/
Dyld maintains a current stack of paths called the run path list. When @rpath is encountered
.Op Fl universal_boot
.Op Fl verify
.Op Fl dylib_list Ar file
+.Op Fl iPhone
+.Op Fl cache_dir Ar dir
.Sh DESCRIPTION
.Nm update_dyld_shared_cache
ensures that dyld's shared cache is up-to-date. This tool is normally
Will regenerate a shared cache in-memory that matches the randomization of the existing shared
cache file. Then instead of writing the cache file, it compares the in-memory cache file to
the on disk version and reports any differences.
+.It Fl iPhone
+indicates that cache is not for the current Mac OS X, but for rather for an iPhone
+.It Fl cache_dir Ar directory
+This option specifies the directory in which to create the cache file(s). If not specified,
+the cache file(s) are created in the standard location (e.g. var/db/dyld/) of the root partition.
.El
.Sh FILES
.Tp
-.Dd February 12, 2009
+.Dd November 29, 2010
.Dt dyld 3
.Sh NAME
.Nm _dyld_image_count,
.Fa buf .
The
.Fa bufsize
-parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied.
+parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied,
+and *
+.Fa bufsize
+is left unchanged.
It returns -1 if the buffer is not large enough, and *
.Fa bufsize
is set to the size required.
archiveVersion = 1;
classes = {
};
- objectVersion = 46;
+ objectVersion = 45;
objects = {
/* Begin PBXAggregateTarget section */
+ F908134211D3ED0B00626CC1 /* libdyld */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */;
+ buildPhases = (
+ F908135111D3ED9000626CC1 /* usr|include|mach-o */,
+ F908137011D3FB5000626CC1 /* usr|include */,
+ F908137111D3FB5000626CC1 /* usr|local|include|mach-o */,
+ F908137211D3FB5000626CC1 /* usr|share|man|man1 */,
+ F908137311D3FB5000626CC1 /* usr|share|man|man3 */,
+ );
+ dependencies = (
+ F9B4D78012AD9736000605A6 /* PBXTargetDependency */,
+ F908134811D3ED1A00626CC1 /* PBXTargetDependency */,
+ );
+ name = libdyld;
+ productName = libdyld;
+ };
F9ED4C920630A73900DF4E74 /* all */ = {
isa = PBXAggregateTarget;
buildConfigurationList = F9D8C7E5087B087300E93EFB /* Build configuration list for PBXAggregateTarget "all" */;
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
- EF79A010070D293E00F78484 /* dyld.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = EF799FE9070D27BB00F78484 /* dyld.1 */; };
- EF79A011070D295200F78484 /* dladdr.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEB070D27BB00F78484 /* dladdr.3 */; };
- EF79A012070D295200F78484 /* dlclose.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEC070D27BB00F78484 /* dlclose.3 */; };
- EF79A013070D295200F78484 /* dlerror.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FED070D27BB00F78484 /* dlerror.3 */; };
- EF79A014070D295200F78484 /* dlopen.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEE070D27BB00F78484 /* dlopen.3 */; };
- EF79A015070D295200F78484 /* dlsym.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEF070D27BB00F78484 /* dlsym.3 */; };
- EF79A016070D295200F78484 /* dyld.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; };
F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = F906E2230639E96400B13DB2 /* dyld_debug.c */; };
+ F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; };
+ F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; };
+ F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; };
+ F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; };
+ F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F93937400A94FC4700070A07 /* dyld_cache_format.h */; };
+ F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; };
+ F908136011D3FACD00626CC1 /* dyld_priv.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; };
+ F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = EF799FE9070D27BB00F78484 /* dyld.1 */; };
+ F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEB070D27BB00F78484 /* dladdr.3 */; };
+ F908136911D3FB3A00626CC1 /* dlclose.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEC070D27BB00F78484 /* dlclose.3 */; };
+ F908136A11D3FB3A00626CC1 /* dlerror.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FED070D27BB00F78484 /* dlerror.3 */; };
+ F908136B11D3FB3A00626CC1 /* dlopen.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEE070D27BB00F78484 /* dlopen.3 */; };
+ F908136C11D3FB3A00626CC1 /* dlsym.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FEF070D27BB00F78484 /* dlsym.3 */; };
+ F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = EF799FF0070D27BB00F78484 /* dyld.3 */; };
+ F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; };
F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; };
- F918691608B16D3500E0F9DB /* dyld-interposing.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; };
F93937470A94FC4700070A07 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */; };
- F93AA9A30630AE1E00301D9F /* dyld_gdb.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; };
- F93AA9A40630AE1E00301D9F /* dyld_priv.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; };
- F93AA9A50630AE1E00301D9F /* dyld.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; };
F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */; };
F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */; settings = {COMPILER_FLAGS = "-O3"; }; };
- F9574CB306C95C1B00142BFA /* dlfcn.h in usr|include */ = {isa = PBXBuildFile; fileRef = F99EE6AE06B48D4200BF1992 /* dlfcn.h */; };
F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; };
- F98D274D0AA79D7400416316 /* dyld_images.h in usr|include|mach-o */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; };
- F99EFC0E0EAD60E8001032B8 /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; };
+ F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */; };
+ F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; };
F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */; };
- F9AC7E940B7BB67700FEB38B /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = F9AC7E930B7BB67700FEB38B /* version.c */; };
+ F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */; };
+ F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */; };
+ F9B0912911F11D3400096D49 /* dsc_slider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B0912811F11D3400096D49 /* dsc_slider.cpp */; };
+ F9B0913911F11DD300096D49 /* dsc_slider.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9B0913811F11DAB00096D49 /* dsc_slider.h */; };
F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; };
+ F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; };
+ F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9CE30791208F1B50098B590 /* dsc_extractor.h */; };
F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; };
- F9E572020A66EF4A007D9BE9 /* dlopen_preflight.3 in usr|share|man|man3 */ = {isa = PBXBuildFile; fileRef = F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */; };
F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */; };
F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC70630A7F100DF4E74 /* dyld.cpp */; };
F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */; };
/* End PBXBuildRule section */
/* Begin PBXContainerItemProxy section */
+ F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74;
+ remoteInfo = libdyld.dylib;
+ };
F93937370A94FB6A00070A07 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
remoteGlobalIDString = F93937310A94FAF700070A07;
remoteInfo = update_dyld_shared_cache;
};
- F98C78D90F7C017F006257D2 /* PBXContainerItemProxy */ = {
+ F99B8E9F0FEC195800701838 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F99B8E550FEC10F600701838;
+ remoteInfo = dyld_shared_cache_util;
+ };
+ F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F99B8E550FEC10F600701838;
+ remoteInfo = dyld_shared_cache_util;
+ };
+ F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
proxyType = 1;
remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
- remoteInfo = dsc;
+ remoteInfo = libdsc;
+ };
+ F9CE330A120F40EA0098B590 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB;
+ remoteInfo = libdsc;
};
F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
- F90CF2950E71D1FB000BF0F1 /* usr|local|include */ = {
+ F908135111D3ED9000626CC1 /* usr|include|mach-o */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = /usr/local/include;
+ dstPath = "/usr/include/mach-o";
dstSubfolderSpec = 0;
files = (
+ F908134C11D3ED6200626CC1 /* dyld.h in usr|include|mach-o */,
+ F908134D11D3ED6200626CC1 /* dyld_images.h in usr|include|mach-o */,
);
- name = "usr|local|include";
+ name = "usr|include|mach-o";
runOnlyForDeploymentPostprocessing = 1;
};
- F93AA9B30630AE8200301D9F /* usr|include|mach-o */ = {
+ F908137011D3FB5000626CC1 /* usr|include */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = "/usr/include/mach-o";
+ dstPath = /usr/include;
dstSubfolderSpec = 0;
files = (
- F93AA9A50630AE1E00301D9F /* dyld.h in usr|include|mach-o */,
- F98D274D0AA79D7400416316 /* dyld_images.h in usr|include|mach-o */,
+ F908135911D3FA8700626CC1 /* dlfcn.h in usr|include */,
);
- name = "usr|include|mach-o";
+ name = "usr|include";
runOnlyForDeploymentPostprocessing = 1;
};
- F93AA9B60630AEB100301D9F /* usr|local|include|mach-o */ = {
+ F908137111D3FB5000626CC1 /* usr|local|include|mach-o */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = "/usr/local/include/mach-o";
dstSubfolderSpec = 0;
files = (
- F918691608B16D3500E0F9DB /* dyld-interposing.h in usr|local|include|mach-o */,
- F93AA9A30630AE1E00301D9F /* dyld_gdb.h in usr|local|include|mach-o */,
- F93AA9A40630AE1E00301D9F /* dyld_priv.h in usr|local|include|mach-o */,
+ F908135D11D3FACD00626CC1 /* dyld-interposing.h in usr|local|include|mach-o */,
+ F908135E11D3FACD00626CC1 /* dyld_cache_format.h in usr|local|include|mach-o */,
+ F908135F11D3FACD00626CC1 /* dyld_gdb.h in usr|local|include|mach-o */,
+ F908136011D3FACD00626CC1 /* dyld_priv.h in usr|local|include|mach-o */,
);
name = "usr|local|include|mach-o";
runOnlyForDeploymentPostprocessing = 1;
};
- F93AA9C20630AF0700301D9F /* usr|share|man|man1 */ = {
+ F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man1;
dstSubfolderSpec = 0;
files = (
- EF79A010070D293E00F78484 /* dyld.1 in usr|share|man|man1 */,
+ F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */,
);
name = "usr|share|man|man1";
runOnlyForDeploymentPostprocessing = 1;
};
- F93AA9C60630AF1F00301D9F /* usr|share|man|man3 */ = {
+ F908137311D3FB5000626CC1 /* usr|share|man|man3 */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man3;
dstSubfolderSpec = 0;
files = (
- EF79A011070D295200F78484 /* dladdr.3 in usr|share|man|man3 */,
- EF79A012070D295200F78484 /* dlclose.3 in usr|share|man|man3 */,
- EF79A013070D295200F78484 /* dlerror.3 in usr|share|man|man3 */,
- EF79A014070D295200F78484 /* dlopen.3 in usr|share|man|man3 */,
- EF79A015070D295200F78484 /* dlsym.3 in usr|share|man|man3 */,
- EF79A016070D295200F78484 /* dyld.3 in usr|share|man|man3 */,
- F9E572020A66EF4A007D9BE9 /* dlopen_preflight.3 in usr|share|man|man3 */,
+ F908136811D3FB3A00626CC1 /* dladdr.3 in usr|share|man|man3 */,
+ F908136911D3FB3A00626CC1 /* dlclose.3 in usr|share|man|man3 */,
+ F908136A11D3FB3A00626CC1 /* dlerror.3 in usr|share|man|man3 */,
+ F908136B11D3FB3A00626CC1 /* dlopen.3 in usr|share|man|man3 */,
+ F908136C11D3FB3A00626CC1 /* dlsym.3 in usr|share|man|man3 */,
+ F908136D11D3FB3A00626CC1 /* dyld.3 in usr|share|man|man3 */,
+ F908136E11D3FB3A00626CC1 /* dlopen_preflight.3 in usr|share|man|man3 */,
);
name = "usr|share|man|man3";
runOnlyForDeploymentPostprocessing = 1;
};
- F9574CB206C95C0D00142BFA /* usr|include */ = {
+ F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = /usr/include;
+ dstPath = "$(INSTALL_LOCATION)/usr/local/include/mach-o";
dstSubfolderSpec = 0;
files = (
- F9574CB306C95C1B00142BFA /* dlfcn.h in usr|include */,
+ F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */,
+ F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */,
);
- name = "usr|include";
+ name = "usr|local|include|mach-o";
runOnlyForDeploymentPostprocessing = 1;
};
- F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = {
+ F9B0913511F11D8B00096D49 /* usr|local|include|mach-o */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = "/usr/local/include/mach-o";
+ dstPath = "usr/local/include/mach-o";
dstSubfolderSpec = 0;
files = (
- F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */,
+ F9B0913911F11DD300096D49 /* dsc_slider.h in usr|local|include|mach-o */,
);
name = "usr|local|include|mach-o";
runOnlyForDeploymentPostprocessing = 1;
F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
- dstPath = /usr/share/man/man1;
+ dstPath = "$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1";
dstSubfolderSpec = 0;
files = (
F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */,
F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = "<group>"; };
F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = "<group>"; };
F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = "<group>"; };
+ F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = "<group>"; };
F98935B90A9A412B00FB6228 /* MachOBinder.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOBinder.hpp; sourceTree = "<group>"; };
F98935BA0A9A412B00FB6228 /* MachORebaser.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachORebaser.hpp; sourceTree = "<group>"; };
F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = "<group>"; };
+ F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = "<group>"; };
+ F99B8E670FEC121100701838 /* dyld_shared_cache_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_util; sourceTree = BUILT_PRODUCTS_DIR; };
F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = "<group>"; };
F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = "<group>"; };
F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = "<group>"; };
+ F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threadLocalVariables.c; path = src/threadLocalVariables.c; sourceTree = "<group>"; };
+ F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = "<group>"; };
F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = "<group>"; };
F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; };
F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; };
+ F9B0912311F11D1600096D49 /* libdsc_slider.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdsc_slider.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9B0912811F11D3400096D49 /* dsc_slider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_slider.cpp; sourceTree = "<group>"; };
+ F9B0913811F11DAB00096D49 /* dsc_slider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_slider.h; sourceTree = "<group>"; };
+ F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = "<group>"; };
+ F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = "<group>"; };
F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = "<group>"; };
F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = "<group>"; };
F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld; sourceTree = BUILT_PRODUCTS_DIR; };
- F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdyldapis.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdyld.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_gdb.cpp; path = src/dyld_gdb.cpp; sourceTree = SOURCE_ROOT; };
F9ED4CC70630A7F100DF4E74 /* dyld.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; };
F9ED4CC80630A7F100DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = src/dyld.h; sourceTree = SOURCE_ROOT; };
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F99B8E540FEC10F600701838 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
F93937460A94FC4700070A07 /* update_dyld_shared_cache.cpp */,
F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */,
F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */,
+ F9B0912811F11D3400096D49 /* dsc_slider.cpp */,
+ F9B0913811F11DAB00096D49 /* dsc_slider.h */,
+ F9CE30781208F1B50098B590 /* dsc_extractor.cpp */,
+ F9CE30791208F1B50098B590 /* dsc_extractor.h */,
+ F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */,
);
path = "launch-cache";
sourceTree = "<group>";
isa = PBXGroup;
children = (
F9ED4C980630A76000DF4E74 /* dyld */,
- F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */,
+ F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */,
F93937320A94FAF700070A07 /* update_dyld_shared_cache */,
F9F2A5590F7AEE9800B7C9EB /* libdsc.a */,
+ F99B8E670FEC121100701838 /* dyld_shared_cache_util */,
+ F9B0912311F11D1600096D49 /* libdsc_slider.a */,
);
name = Products;
sourceTree = "<group>";
F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */,
F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */,
F9B01E3D0739ABDE00CF981B /* dyld.exp */,
+ F976F548127B90F8004BA2A5 /* dyld.order */,
F9AC7E930B7BB67700FEB38B /* version.c */,
F918691408B16D2500E0F9DB /* dyld-interposing.h */,
F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */,
F906E2230639E96400B13DB2 /* dyld_debug.c */,
+ F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */,
+ F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */,
);
name = src;
sourceTree = "<group>";
F939372F0A94FAF700070A07 /* Sources */,
F93937300A94FAF700070A07 /* Frameworks */,
F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */,
+ F991E3030FF1A4EC0082CCC9 /* do not install duplicates */,
);
buildRules = (
);
dependencies = (
- F98C78DA0F7C017F006257D2 /* PBXTargetDependency */,
+ F99B8EA00FEC195800701838 /* PBXTargetDependency */,
+ F9CE330B120F40EA0098B590 /* PBXTargetDependency */,
);
name = update_dyld_shared_cache;
productName = update_dyld_shared_cache;
productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */;
productType = "com.apple.product-type.tool";
};
+ F99B8E550FEC10F600701838 /* dyld_shared_cache_util */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */;
+ buildPhases = (
+ F99B8E530FEC10F600701838 /* Sources */,
+ F99B8E540FEC10F600701838 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = dyld_shared_cache_util;
+ productName = dscutil;
+ productReference = F99B8E670FEC121100701838 /* dyld_shared_cache_util */;
+ productType = "com.apple.product-type.tool";
+ };
+ F9B0912211F11D1600096D49 /* libdsc_slider */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F9B0912A11F11D3400096D49 /* Build configuration list for PBXNativeTarget "libdsc_slider" */;
+ buildPhases = (
+ F9B0912011F11D1600096D49 /* Sources */,
+ F9B0913511F11D8B00096D49 /* usr|local|include|mach-o */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = libdsc_slider;
+ productName = libdsc_slider;
+ productReference = F9B0912311F11D1600096D49 /* libdsc_slider.a */;
+ productType = "com.apple.product-type.library.static";
+ };
F9ED4C970630A76000DF4E74 /* dyld */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */;
buildPhases = (
+ F9D050C811DD701A00FB0A29 /* configure archives */,
F9ED4C950630A76000DF4E74 /* Sources */,
+ F907E2490FA6469000BFEDBD /* install iPhone file */,
+ F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */,
);
buildRules = (
F921D318070376B0000D1056 /* PBXBuildRule */,
F921D3160703769A000D1056 /* PBXBuildRule */,
);
dependencies = (
+ F99B8EB20FEC220C00701838 /* PBXTargetDependency */,
);
name = dyld;
productName = dyld;
productReference = F9ED4C980630A76000DF4E74 /* dyld */;
productType = "com.apple.product-type.tool";
};
- F9ED4C9E0630A76B00DF4E74 /* libdyld */ = {
+ F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */ = {
isa = PBXNativeTarget;
- buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld" */;
+ buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */;
buildPhases = (
- F9AC7E7E0B7BB3D300FEB38B /* create version.c */,
F9ED4C9C0630A76B00DF4E74 /* Sources */,
- F93AA9B30630AE8200301D9F /* usr|include|mach-o */,
- F9574CB206C95C0D00142BFA /* usr|include */,
- F90CF2950E71D1FB000BF0F1 /* usr|local|include */,
- F93AA9B60630AEB100301D9F /* usr|local|include|mach-o */,
- F93AA9C20630AF0700301D9F /* usr|share|man|man1 */,
- F93AA9C60630AF1F00301D9F /* usr|share|man|man3 */,
- F918692408B16F6900E0F9DB /* install symlinks */,
);
buildRules = (
F921D31E070376F1000D1056 /* PBXBuildRule */,
);
dependencies = (
);
- name = libdyld;
+ name = libdyld.dylib;
productName = libdyld;
- productReference = F9ED4C9F0630A76B00DF4E74 /* libdyldapis.a */;
- productType = "com.apple.product-type.library.static";
+ productReference = F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */;
+ productType = "com.apple.product-type.library.dynamic";
};
- F9F2A5580F7AEE9800B7C9EB /* dsc */ = {
+ F9F2A5580F7AEE9800B7C9EB /* libdsc */ = {
isa = PBXNativeTarget;
- buildConfigurationList = F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "dsc" */;
+ buildConfigurationList = F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */;
buildPhases = (
F9F2A5560F7AEE9800B7C9EB /* Sources */,
F9F2A5570F7AEE9800B7C9EB /* Frameworks */,
);
dependencies = (
);
- name = dsc;
+ name = libdsc;
productName = dsc;
productReference = F9F2A5590F7AEE9800B7C9EB /* libdsc.a */;
productType = "com.apple.product-type.library.static";
F9ED4C8B0630A72300DF4E74 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */;
- compatibilityVersion = "Xcode 2.4";
+ compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
mainGroup = F9ED4C870630A72200DF4E74;
productRefGroup = F9ED4C990630A76000DF4E74 /* Products */;
projectDirPath = "";
targets = (
F9ED4C920630A73900DF4E74 /* all */,
F9ED4C970630A76000DF4E74 /* dyld */,
- F9ED4C9E0630A76B00DF4E74 /* libdyld */,
+ F908134211D3ED0B00626CC1 /* libdyld */,
+ F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */,
F93937310A94FAF700070A07 /* update_dyld_shared_cache */,
- F9F2A5580F7AEE9800B7C9EB /* dsc */,
+ F9F2A5580F7AEE9800B7C9EB /* libdsc */,
+ F99B8E550FEC10F600701838 /* dyld_shared_cache_util */,
+ F9B0912211F11D1600096D49 /* libdsc_slider */,
);
};
/* End PBXProject section */
/* Begin PBXShellScriptBuildPhase section */
- F918692408B16F6900E0F9DB /* install symlinks */ = {
+ F907E2490FA6469000BFEDBD /* install iPhone file */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
);
inputPaths = (
);
- name = "install symlinks";
+ name = "install iPhone file";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
- shellScript = "cd ${DSTROOT}/usr/local/lib/system\nln -sf libdyldapis.a libdyldapis_profile.a\nln -sf libdyldapis.a libdyldapis_debug.a\n";
+ shellScript = "if [ \"${PLATFORM_NAME}\" = \"iphoneos\" ] \nthen\n\tmkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n\techo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n";
showEnvVarsInLog = 0;
};
- F9AC7E7E0B7BB3D300FEB38B /* create version.c */ = {
+ F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = {
isa = PBXShellScriptBuildPhase;
- buildActionMask = 2147483647;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "do not install duplicates";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "if [ \"${INSTALL_LOCATION}\" = \"\" ] \nthen\n # on iOS, libdyld builds arm libdsc.a and u_d_s_c builds intel libdsc.a\n # on MacOSX, to avoid collision, u_d_s_c does not install libdsc.a\n\trm -rf ${DSTROOT}/usr/local/include\n\trm -rf ${DSTROOT}/usr/local/lib\nfi\n";
+ showEnvVarsInLog = 0;
+ };
+ F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 8;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "suppress macosx dyld_shared_cache_util";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ shellPath = /bin/sh;
+ shellScript = "# iPhone wants a copy of dyld_shared_cache_util on the device\n# MacOSX does not need a copy because update_dyld_shared_cache target already installed a copy\nif [ \"${PLATFORM_NAME}\" = \"macosx\" ] \nthen\n\trm -rf ${DSTROOT}/usr/local\nfi\n";
+ showEnvVarsInLog = 0;
+ };
+ F9D050C811DD701A00FB0A29 /* configure archives */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 12;
files = (
);
inputPaths = (
);
- name = "create version.c";
+ name = "configure archives";
outputPaths = (
- "$(BUILT_PRODUCTS_DIR)/version.c",
+ "$(DERIVED_SOURCES_DIR)/archives.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "/Developer/Makefiles/bin/version.pl ${ProjectName} > ${BUILT_PRODUCTS_DIR}/version.c\n";
+ shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
);
runOnlyForDeploymentPostprocessing = 0;
};
+ F99B8E530FEC10F600701838 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F99B8EA30FEC1C4200701838 /* dsc_iterator.cpp in Sources */,
+ F99B8E630FEC11B400701838 /* dyld_shared_cache_util.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F9B0912011F11D1600096D49 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F9B0912911F11D3400096D49 /* dsc_slider.cpp in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
F9ED4C950630A76000DF4E74 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */,
F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */,
F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */,
- F99EFC0E0EAD60E8001032B8 /* dyld_stub_binder.s in Sources */,
F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */,
F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */,
);
buildActionMask = 2147483647;
files = (
F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */,
- F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */,
- F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */,
F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */,
- F9AC7E940B7BB67700FEB38B /* version.c in Sources */,
F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */,
+ F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */,
+ F906E2240639E96400B13DB2 /* dyld_debug.c in Sources */,
+ F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */,
+ F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
buildActionMask = 2147483647;
files = (
F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */,
+ F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
+ targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */;
+ };
F93937380A94FB6A00070A07 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F93937310A94FAF700070A07 /* update_dyld_shared_cache */;
targetProxy = F93937370A94FB6A00070A07 /* PBXContainerItemProxy */;
};
- F98C78DA0F7C017F006257D2 /* PBXTargetDependency */ = {
+ F99B8EA00FEC195800701838 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
+ targetProxy = F99B8E9F0FEC195800701838 /* PBXContainerItemProxy */;
+ };
+ F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */;
+ targetProxy = F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */;
+ };
+ F9B4D78012AD9736000605A6 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
+ targetProxy = F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */;
+ };
+ F9CE330B120F40EA0098B590 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F9F2A5580F7AEE9800B7C9EB /* dsc */;
- targetProxy = F98C78D90F7C017F006257D2 /* PBXContainerItemProxy */;
+ target = F9F2A5580F7AEE9800B7C9EB /* libdsc */;
+ targetProxy = F9CE330A120F40EA0098B590 /* PBXContainerItemProxy */;
};
F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
};
F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
- target = F9ED4C9E0630A76B00DF4E74 /* libdyld */;
+ target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */;
targetProxy = F9ED4CA80630A78A00DF4E74 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
+ F908134311D3ED0C00626CC1 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALLHDRS_COPY_PHASE = YES;
+ PRODUCT_NAME = libdyld;
+ };
+ name = Debug;
+ };
+ F908134411D3ED0C00626CC1 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ INSTALLHDRS_COPY_PHASE = YES;
+ PRODUCT_NAME = libdyld;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
F93937350A94FB2900070A07 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_WARN_UNUSED_VARIABLE = YES;
PREBINDING = NO;
PRODUCT_NAME = update_dyld_shared_cache;
- VALID_ARCHS = "ppc i386";
+ VALID_ARCHS = "x86_64 i386";
};
name = Debug;
};
F93937360A94FB2900070A07 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ARCHS = x86_64;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- GCC_OPTIMIZATION_LEVEL = 3;
+ GCC_OPTIMIZATION_LEVEL = s;
GCC_THREADSAFE_STATICS = NO;
GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
- INSTALL_PATH = /usr/bin;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/$(LOCAL)/bin";
+ LOCAL = "$(LOCAL_$(RC_TARGET_CONFIG))";
+ LOCAL_iPhone = local;
PREBINDING = NO;
PRODUCT_NAME = update_dyld_shared_cache;
STRIP_INSTALLED_PRODUCT = YES;
};
name = Release;
};
+ F99B8E580FEC10F600701838 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ PREBINDING = NO;
+ PRODUCT_NAME = dyld_shared_cache_util;
+ };
+ name = Debug;
+ };
+ F99B8E590FEC10F600701838 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin";
+ PREBINDING = NO;
+ PRODUCT_NAME = dyld_shared_cache_util;
+ SKIP_INSTALL = NO;
+ };
+ name = Release;
+ };
+ F9B0912411F11D1700096D49 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ INSTALL_PATH = /usr/local/lib;
+ PREBINDING = NO;
+ PRODUCT_NAME = libdsc_slider;
+ };
+ name = Debug;
+ };
+ F9B0912511F11D1700096D49 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ INSTALLHDRS_COPY_PHASE = YES;
+ INSTALL_PATH = /usr/local/lib;
+ PRODUCT_NAME = dsc_slider;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
F9D8C7DE087B087300E93EFB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = (
- ppc,
i386,
x86_64,
);
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
- EXCEPTION_LIB_armv6 = "-lgcc_eh";
- EXCEPTION_LIB_armv7 = "-lgcc_eh";
- EXCEPTION_LIB_i386 = /usr/local/lib/dyld/libunwind.a;
- EXCEPTION_LIB_ppc = /usr/local/lib/dyld/libunwind.a;
- EXCEPTION_LIB_x86_64 = /usr/local/lib/dyld/libunwind.a;
EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/dyld.exp";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_DYNAMIC_NO_PIC = NO;
"./launch-cache",
);
INSTALL_PATH = /usr/lib;
- LIBC_OVERRIDES_iphoneos = "";
- LIBC_OVERRIDES_macosx = "/usr/local/lib/system/libc-dyld.a";
OTHER_CFLAGS = "";
OTHER_LDFLAGS = (
"-seg1addr",
"$(BASE_ADDRESS_$(CURRENT_ARCH))",
- "-lstdc++-static",
- "$(LIBC_OVERRIDES_$(PLATFORM_NAME))",
+ "@$(DERIVED_SOURCES_DIR)/archives.txt",
"-nostdlib",
- /usr/local/lib/system/libc.a,
- /usr/local/lib/libCoreSymbolicationSharedWithDyld.a,
- "$(EXCEPTION_LIB_$(CURRENT_ARCH))",
"-lgcc",
"-Wl,-e,__dyld_start",
"-Wl,-dylinker",
"-Wl,-dylinker_install_name,/usr/lib/dyld",
);
- PER_ARCH_CFLAGS_ppc = "";
PREBINDING = NO;
PRODUCT_NAME = dyld;
STRIPFLAGS = "-S";
F9D8C7E0087B087300E93EFB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ ARCHS = (
+ x86_64,
+ i386,
+ );
BASE_ADDRESS_armv4t = 0x2fe00000;
BASE_ADDRESS_armv5 = 0x2fe00000;
BASE_ADDRESS_armv6 = 0x2fe00000;
CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- EXCEPTION_LIB_armv6 = "-lgcc_eh";
- EXCEPTION_LIB_armv7 = "-lgcc_eh";
- EXCEPTION_LIB_i386 = /usr/local/lib/dyld/libunwind.a;
- EXCEPTION_LIB_ppc = /usr/local/lib/dyld/libunwind.a;
- EXCEPTION_LIB_x86_64 = /usr/local/lib/dyld/libunwind.a;
EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/dyld.exp";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_DYNAMIC_NO_PIC = NO;
"./launch-cache",
);
INSTALL_PATH = /usr/lib;
- LIBC_OVERRIDES_iphoneos = "";
- LIBC_OVERRIDES_macosx = "/usr/local/lib/system/libc-dyld.a";
- OTHER_CFLAGS = "";
+ ORDER_FILE = "$(SRCROOT)/src/dyld.order";
+ "OTHER_CFLAGS[arch=armv6]" = "-mthumb";
OTHER_LDFLAGS = (
"-seg1addr",
"$(BASE_ADDRESS_$(CURRENT_ARCH))",
- "-lstdc++-static",
- "$(LIBC_OVERRIDES_$(PLATFORM_NAME))",
+ "@$(DERIVED_SOURCES_DIR)/archives.txt",
"-nostdlib",
- /usr/local/lib/system/libc.a,
- /usr/local/lib/libCoreSymbolicationSharedWithDyld.a,
- "$(EXCEPTION_LIB_$(CURRENT_ARCH))",
"-lgcc",
"-Wl,-e,__dyld_start",
"-Wl,-dylinker",
"-Wl,-dylinker_install_name,/usr/lib/dyld",
);
- PER_ARCH_CFLAGS_ppc = "";
PREBINDING = NO;
PRODUCT_NAME = dyld;
STRIPFLAGS = "-S";
F9D8C7E2087B087300E93EFB /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(NATIVE_ARCH_ACTUAL)";
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
- HEADER_SEARCH_PATHS = ./include;
- INSTALL_PATH = /usr/local/lib/system;
- LIBRARY_STYLE = STATIC;
- PRODUCT_NAME = dyldapis;
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+ INSTALL_PATH = /usr/lib/system;
+ PREBINDING = NO;
+ PRODUCT_NAME = dyld;
WARNING_CFLAGS = (
"-Wmost",
"-Wno-four-char-constants",
F9D8C7E4087B087300E93EFB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- COPY_PHASE_STRIP = NO;
+ ARCHS = (
+ x86_64,
+ i386,
+ );
+ COPY_PHASE_STRIP = YES;
+ CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)";
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)";
+ EXECUTABLE_PREFIX = lib;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
- GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
- HEADER_SEARCH_PATHS = ./include;
- INSTALL_PATH = /usr/local/lib/system;
- LIBRARY_STYLE = STATIC;
- PRODUCT_NAME = dyldapis;
+ HEADER_SEARCH_PATHS = "$(SRCROOT)/include";
+ INSTALLHDRS_COPY_PHASE = YES;
+ INSTALL_PATH = /usr/lib/system;
+ OTHER_LDFLAGS = (
+ "-nodefaultlibs",
+ "-lSystem",
+ "-umbrella",
+ System,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = dyld;
+ SEPARATE_STRIP = YES;
+ STRIP_INSTALLED_PRODUCT = YES;
+ VERSIONING_SYSTEM = "apple-generic";
WARNING_CFLAGS = (
"-Wmost",
"-Wno-four-char-constants",
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
INSTALL_PATH = /usr/local/lib;
PREBINDING = NO;
PRODUCT_NAME = dsc;
F9F2A55B0F7AEE9900B7C9EB /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
- COPY_PHASE_STRIP = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ COPY_PHASE_STRIP = NO;
GCC_ENABLE_CPP_EXCEPTIONS = NO;
GCC_ENABLE_CPP_RTTI = NO;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = NO;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
GCC_MODEL_TUNING = G5;
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
- GCC_WARN_PEDANTIC = YES;
- GCC_WARN_SHADOW = YES;
+ GCC_WARN_PEDANTIC = NO;
+ GCC_WARN_SHADOW = NO;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- INSTALL_PATH = /usr/local/lib;
- PREBINDING = NO;
+ INSTALLHDRS_COPY_PHASE = YES;
+ INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib";
PRODUCT_NAME = dsc;
ZERO_LINK = NO;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F908134311D3ED0C00626CC1 /* Debug */,
+ F908134411D3ED0C00626CC1 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ F99B8E5D0FEC10F800701838 /* Build configuration list for PBXNativeTarget "dyld_shared_cache_util" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F99B8E580FEC10F600701838 /* Debug */,
+ F99B8E590FEC10F600701838 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F9B0912A11F11D3400096D49 /* Build configuration list for PBXNativeTarget "libdsc_slider" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F9B0912411F11D1700096D49 /* Debug */,
+ F9B0912511F11D1700096D49 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */ = {
isa = XCConfigurationList;
buildConfigurations = (
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld" */ = {
+ F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F9D8C7E2087B087300E93EFB /* Debug */,
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "dsc" */ = {
+ F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F9F2A55A0F7AEE9900B7C9EB /* Debug */,
/*
* _NSGetExecutablePath() copies the path of the main executable into the buffer. The bufsize parameter
- * should initially be the size of the buffer. The function returns 0 if the path was successfully copied.
- * It returns -1 if the buffer is not large enough, and *bufsize is set to the size required.
+ * should initially be the size of the buffer. The function returns 0 if the path was successfully copied,
+ * and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set
+ * to the size required.
*
* Note that _NSGetExecutablePath will return "a path" to the executable not a "real path" to the executable.
* That is the path may be a symbolic link and not the real file. With deep directories the total bufsize
/*
- * _dyld_moninit() and _dyld_func_lookup() are private interface between
- * dyld and libSystem.
-*/
+ * _dyld_moninit() is a private interface between dyld and libSystem.
+ */
extern void _dyld_moninit(void (*monaddition)(char *lowpc, char *highpc)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0);
-extern int _dyld_func_lookup(const char* dyld_func_name, void **address) __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_1,__MAC_10_6,__IPHONE_NA,__IPHONE_NA);
/*
- * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define _DYLD_IMAGES_
#include <stdbool.h>
+#include <unistd.h>
#include <mach/mach.h>
#ifdef __cplusplus
* be set to point to a C string message buffer containing the reason dyld terminate the process.
* The low bit of the terminationFlags will be set if dyld terminated the process before any user
* code ran, in which case there is no need for the crash log to contain the backtrace.
+ *
+ * When dyld terminates a process because some required dylib or symbol cannot be bound, in
+ * addition to the errorMessage field, it now sets the errorKind field and the corresponding
+ * fields: errorClientOfDylibPath, errorTargetDylibPath, errorSymbol.
+ *
*/
enum dyld_image_mode { dyld_image_adding=0, dyld_image_removing=1 };
/* then file has been modified since dyld loaded it */
};
+struct dyld_uuid_info {
+ const struct mach_header* imageLoadAddress; /* base address image is mapped into */
+ uuid_t imageUUID; /* UUID of image */
+};
+
typedef void (*dyld_image_notifier)(enum dyld_image_mode mode, uint32_t infoCount, const struct dyld_image_info info[]);
+/* for use in dyld_all_image_infos.errorKind field */
+enum { dyld_error_kind_none=0,
+ dyld_error_kind_dylib_missing=1,
+ dyld_error_kind_dylib_wrong_arch=2,
+ dyld_error_kind_dylib_version=3,
+ dyld_error_kind_symbol_missing=4
+ };
+
+
struct dyld_all_image_infos {
uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */
uint32_t infoArrayCount;
/* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */
bool libSystemInitialized;
const struct mach_header* dyldImageLoadAddress;
- /* the following field is only in version 3 (Mac OS X 10.6) and later */
+ /* the following field is only in version 3 (Mac OS X 10.6, iPhoneOS 3.0) and later */
void* jitInfo;
- /* the following fields are only in version 5 (Mac OS X 10.6) and later */
+ /* the following fields are only in version 5 (Mac OS X 10.6, iPhoneOS 3.0) and later */
const char* dyldVersion;
const char* errorMessage;
uintptr_t terminationFlags;
- /* the following field is only in version 6 (Mac OS X 10.6) and later */
+ /* the following field is only in version 6 (Mac OS X 10.6, iPhoneOS 3.1) and later */
void* coreSymbolicationShmPage;
- /* the following field is only in version 7 (Mac OS X 10.6) and later */
+ /* the following field is only in version 7 (Mac OS X 10.6, iPhoneOS 3.1) and later */
uintptr_t systemOrderFlag;
+ /* the following field is only in version 8 (Mac OS X 10.7, iPhoneOS 3.1) and later */
+ uintptr_t uuidArrayCount;
+ const struct dyld_uuid_info* uuidArray; /* only images not in dyld shared cache */
+ /* the following field is only in version 9 (Mac OS X 10.7, iOS 4.0) and later */
+ struct dyld_all_image_infos* dyldAllImageInfosAddress;
+ /* the following field is only in version 10 (Mac OS X 10.7, iOS 4.2) and later */
+ uintptr_t initialImageCount;
+ /* the following field is only in version 11 (Mac OS X 10.7, iOS 4.2) and later */
+ uintptr_t errorKind;
+ const char* errorClientOfDylibPath;
+ const char* errorTargetDylibPath;
+ const char* errorSymbol;
+ /* the following field is only in version 12 (Mac OS X 10.7, iOS 4.3) and later */
+ uintptr_t sharedCacheSlide;
};
extern struct dyld_all_image_infos dyld_all_image_infos;
-
/*
* Beginning in Mac OS X 10.6, rather than looking up the symbol "_dyld_all_image_infos"
* in dyld's symbol table, you can add DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET to the mach_header
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2003-2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#ifndef _MACH_O_DYLD_PRIV_H_
#define _MACH_O_DYLD_PRIV_H_
-
+#include <stdbool.h>
+#include <Availability.h>
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#endif /* __cplusplus */
+
+//
+// private interface between libSystem.dylib and dyld
+//
+extern int _dyld_func_lookup(const char* dyld_func_name, void **address);
+
+
+//
+// _dyld_moninit() is a private interface between libSystem.dylib and dyld
+//
+extern void _dyld_moninit(void (*monaddition)(char *lowpc, char *highpc));
+
+//
+// private interface between libSystem.dylib and dyld
+//
+extern void _dyld_fork_child();
+
+
//
// Possible state changes for which you can register to be notified
//
dyld_register_image_state_change_handler(enum dyld_image_states state, bool batch, dyld_image_state_change_handler handler);
+//
+// Possible thread-local variable state changes for which you can register to be notified
+//
+enum dyld_tlv_states {
+ dyld_tlv_state_allocated = 10, // TLV range newly allocated
+ dyld_tlv_state_deallocated = 20 // TLV range about to be deallocated
+};
+
+//
+// Info about thread-local variable storage.
+//
+typedef struct {
+ size_t info_size; // sizeof(dyld_tlv_info)
+ void * tlv_addr; // Base address of TLV storage
+ size_t tlv_size; // Byte size of TLV storage
+} dyld_tlv_info;
+
+#if __BLOCKS__
+
+//
+// Callback that notes changes to thread-local variable storage.
+//
+typedef void (^dyld_tlv_state_change_handler)(enum dyld_tlv_states state, const dyld_tlv_info *info);
+
+//
+// Register a handler to be called when a thread adds or removes storage for thread-local variables.
+// The registered handler will only be called from and on behalf of the thread that owns the storage.
+// The registered handler will NOT be called for any storage that was
+// already allocated before dyld_register_tlv_state_change_handler() was
+// called. Use dyld_enumerate_tlv_storage() to get that information.
+// Exists in Mac OS X 10.7 and later
+//
+extern void
+dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler);
+
+//
+// Enumerate the current thread-local variable storage allocated for the current thread.
+// Exists in Mac OS X 10.7 and later
+//
+extern void
+dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler);
+
+#endif
+
+
//
// get slide for a given loaded mach_header
// Mac OS X 10.6 and later
-
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+//
+// Returns if any OS dylib has overridden its copy in the shared cache
+//
+// Exists in iPhoneOS 3.1 and later
+extern bool dyld_shared_cache_some_image_overridden();
+#endif
const char* magic() const INLINE { return fields.magic; }
void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); }
- //uint32_t architecture() const INLINE { return E::get32(fields.architecture); }
- //void set_architecture(uint32_t value) INLINE { E::set32(fields.architecture, value); }
-
uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); }
void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); }
uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); }
void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); }
- //uint32_t dependenciesOffset() const INLINE { return E::get32(fields.dependenciesOffset); }
- //void set_dependenciesOffset(uint32_t value) INLINE { E::set32(fields.dependenciesOffset, value); }
+ uint64_t codeSignatureOffset() const INLINE { return E::get64(fields.codeSignatureOffset); }
+ void set_codeSignatureOffset(uint64_t value) INLINE { E::set64(fields.codeSignatureOffset, value); }
+
+ uint64_t codeSignatureSize() const INLINE { return E::get64(fields.codeSignatureSize); }
+ void set_codeSignatureSize(uint64_t value) INLINE { E::set64(fields.codeSignatureSize, value); }
+
+ uint64_t slideInfoOffset() const INLINE { return E::get64(fields.slideInfoOffset); }
+ void set_slideInfoOffset(uint64_t value) INLINE { E::set64(fields.slideInfoOffset, value); }
- //uint32_t dependenciesCount() const INLINE { return E::get32(fields.dependenciesCount); }
- //void set_dependenciesCount(uint32_t value) INLINE { E::set32(fields.dependenciesCount, value); }
+ uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); }
+ void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); }
private:
dyld_cache_header fields;
template <typename E>
class dyldCacheFileMapping {
public:
- uint64_t address() const INLINE { return E::get64(fields.sfm_address); }
- void set_address(uint64_t value) INLINE { E::set64(fields.sfm_address, value); }
+ uint64_t address() const INLINE { return E::get64(fields.address); }
+ void set_address(uint64_t value) INLINE { E::set64(fields.address, value); }
- uint64_t size() const INLINE { return E::get64(fields.sfm_size); }
- void set_size(uint64_t value) INLINE { E::set64(fields.sfm_size, value); }
+ uint64_t size() const INLINE { return E::get64(fields.size); }
+ void set_size(uint64_t value) INLINE { E::set64(fields.size, value); }
- uint64_t file_offset() const INLINE { return E::get64(fields.sfm_file_offset); }
- void set_file_offset(uint64_t value) INLINE { E::set64(fields.sfm_file_offset, value); }
+ uint64_t file_offset() const INLINE { return E::get64(fields.fileOffset); }
+ void set_file_offset(uint64_t value) INLINE { E::set64(fields.fileOffset, value); }
- uint32_t max_prot() const INLINE { return E::get32(fields.sfm_max_prot); }
- void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.sfm_max_prot, value); }
+ uint32_t max_prot() const INLINE { return E::get32(fields.maxProt); }
+ void set_max_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.maxProt, value); }
- uint32_t init_prot() const INLINE { return E::get32(fields.sfm_init_prot); }
- void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.sfm_init_prot, value); }
+ uint32_t init_prot() const INLINE { return E::get32(fields.initProt); }
+ void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.initProt, value); }
private:
- shared_file_mapping_np fields;
+ dyld_cache_mapping_info fields;
};
uint32_t pathFileOffset() const INLINE { return E::get32(fields.pathFileOffset); }
void set_pathFileOffset(uint32_t value) INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; }
- //uint32_t dependenciesStartOffset() const INLINE { return E::get32(fields.dependenciesStartOffset); }
- //void set_dependenciesStartOffset(uint32_t value) INLINE { E::set32(fields.dependenciesStartOffset, value); }
-
private:
dyld_cache_image_info fields;
};
+template <typename E>
+class dyldCacheSlideInfo {
+public:
+ uint32_t version() const INLINE { return E::get32(fields.version); }
+ void set_version(uint32_t value) INLINE { E::set32(fields.version, value); }
+
+ uint32_t toc_offset() const INLINE { return E::get32(fields.toc_offset); }
+ void set_toc_offset(uint32_t value) INLINE { E::set32(fields.toc_offset, value); }
+
+ uint32_t toc_count() const INLINE { return E::get32(fields.toc_count); }
+ void set_toc_count(uint32_t value) INLINE { E::set32(fields.toc_count, value); }
+
+ uint32_t entries_offset() const INLINE { return E::get32(fields.entries_offset); }
+ void set_entries_offset(uint32_t value) INLINE { E::set32(fields.entries_offset, value); }
+
+ uint32_t entries_count() const INLINE { return E::get32(fields.entries_count); }
+ void set_entries_count(uint32_t value) INLINE { E::set32(fields.entries_count, value); }
+
+ uint32_t entries_size() const INLINE { return E::get32(fields.entries_size); }
+ void set_entries_size(uint32_t value) INLINE { E::set32(fields.entries_size, value); }
+
+ uint16_t toc(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index]); }
+ void set_toc(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.toc_offset)))[index], value); }
+
+private:
+ dyld_cache_slide_info fields;
+};
+
+
+struct dyldCacheSlideInfoEntry {
+ uint8_t bits[4096/(8*4)]; // 128-byte bitmap
+};
+
#endif // __DYLD_CACHE_ABSTRACTION__
static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); }
static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, value); }
+
+ // Round to a P-size boundary
+ template <typename T>
+ static T round_up(T value) { return (value+3) & ~(T)3; }
+ template <typename T>
+ static T round_down(T value) { return value & ~(T)3; }
};
static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); }
static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); }
+
+ // Round to a P-size boundary
+ template <typename T>
+ static T round_up(T value) { return (value+7) & ~(T)7; }
+ template <typename T>
+ static T round_down(T value) { return value & ~(T)7; }
};
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2006-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <vector>
#include <set>
+#include <ext/hash_map>
+#include <ext/hash_set>
#include "MachOFileAbstraction.hpp"
#include "Architectures.hpp"
#include "MachORebaser.hpp"
#include "MachOTrie.hpp"
+#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
+ #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
+#endif
const char* getDylibID() const;
void setDependentBinders(const Map& map);
- void bind();
-
+ void bind(std::vector<void*>&);
+ void optimize();
+ void addResolverClient(Binder<A>* clientDylib, const char* symbolName);
+ void addResolverLazyPointerMappedAddress(const char* symbolName,
+ typename A::P::uint_t lpVMAddr);
private:
typedef typename A::P P;
typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
struct BinderAndReExportFlag { Binder<A>* binder; bool reExport; };
+ struct SymbolReExport { const char* exportName; int dylibOrdinal; const char* importName; };
typedef __gnu_cxx::hash_map<const char*, pint_t, __gnu_cxx::hash<const char*>, CStringEquals> NameToAddrMap;
+ typedef __gnu_cxx::hash_set<const char*, __gnu_cxx::hash<const char*>, CStringEquals> NameSet;
+ struct ClientAndSymbol { Binder<A>* client; const char* symbolName; };
+ struct SymbolAndLazyPointer { const char* symbolName; pint_t lpVMAddr; };
+ static bool isPublicLocation(const char* pth);
void doBindExternalRelocations();
void doBindIndirectSymbols();
void doSetUpDyldSection();
void doSetPreboundUndefines();
- void doBindDyldInfo();
- void doBindDyldLazyInfo();
+ void hoistPrivateRexports();
+ int ordinalOfDependentBinder(Binder<A>* dep);
+ void doBindDyldInfo(std::vector<void*>& pointersInData);
+ void doBindDyldLazyInfo(std::vector<void*>& pointersInData);
void bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type,
- int libraryOrdinal, int64_t addend, const char* symbolName);
+ int libraryOrdinal, int64_t addend,
+ const char* symbolName, bool lazyPointer,
+ std::vector<void*>& pointersInData);
pint_t resolveUndefined(const macho_nlist<P>* undefinedSymbol);
- bool findExportedSymbolAddress(const char* name, pint_t* result);
+ bool findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, bool* isResolverSymbol);
void bindStub(uint8_t elementSize, uint8_t* location, pint_t vmlocation, pint_t value);
const char* parentUmbrella();
+ pint_t runtimeAddressFromNList(const macho_nlist<P>* sym);
+ void optimizeStub(const char* symbolName, pint_t lpVMAddr);
+ void optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr);
+ pint_t findLazyPointerFor(const char* symbolName);
+
static uint8_t pointerRelocSize();
static uint8_t pointerRelocType();
std::vector<BinderAndReExportFlag> fDependentDylibs;
NameToAddrMap fHashTable;
+ NameSet fSymbolResolvers;
+ std::vector<SymbolReExport> fReExportedSymbols;
uint64_t fDyldBaseAddress;
const macho_nlist<P>* fSymbolTable;
const char* fStrings;
const macho_dylib_command<P>* fParentUmbrella;
const macho_dyld_info_command<P>* fDyldInfo;
bool fOriginallyPrebound;
+ bool fReExportedSymbolsResolved;
+ std::vector<ClientAndSymbol> fClientAndSymbols;
+ std::vector<SymbolAndLazyPointer> fSymbolAndLazyPointers;
};
+template <>
+uint32_t Binder<arm>::runtimeAddressFromNList(const macho_nlist<Pointer32<LittleEndian> >* sym)
+{
+ if (sym->n_desc() & N_ARM_THUMB_DEF)
+ return sym->n_value() + 1;
+ else
+ return sym->n_value();
+}
+
+template <typename A>
+typename A::P::uint_t Binder<A>::runtimeAddressFromNList(const macho_nlist<P>* sym)
+{
+ return sym->n_value();
+}
+
template <typename A>
Binder<A>::Binder(const MachOLayoutAbstraction& layout, uint64_t dyldBaseAddress)
: Rebaser<A>(layout), fDyldBaseAddress(dyldBaseAddress),
fSymbolTable(NULL), fStrings(NULL), fDynamicInfo(NULL),
fFristWritableSegment(NULL), fDylibID(NULL), fDyldInfo(NULL),
- fParentUmbrella(NULL)
+ fParentUmbrella(NULL), fReExportedSymbolsResolved(false)
{
fOriginallyPrebound = ((this->fHeader->flags() & MH_PREBOUND) != 0);
// update header flags so the cache looks prebound split-seg (0x80000000 is in-shared-cache bit)
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
((macho_dylib_command<P>*)cmd)->set_timestamp(0);
break;
case LC_SUB_FRAMEWORK:
// fprintf(stderr, "exports for %s\n", layout.getFilePath());
if ( fDyldInfo != NULL ) {
std::vector<mach_o::trie::Entry> exports;
- const uint8_t* exportsStart = &this->fLinkEditBase[fDyldInfo->export_off()];
+ const uint8_t* exportsStart = layout.getDyldInfoExports();
const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()];
mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
pint_t baseAddress = layout.getSegments()[0].newAddress();
for(std::vector<mach_o::trie::Entry>::iterator it = exports.begin(); it != exports.end(); ++it) {
- fHashTable[it->name] = it->address + baseAddress;
+ if ( (it->flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) {
+ if ( (it->flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
+ fSymbolResolvers.insert(it->name);
+ }
+ if ( it->flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ //fprintf(stderr, "found re-export %s in %s\n", sym.exportName, this->getDylibID());
+ SymbolReExport sym;
+ sym.exportName = it->name;
+ sym.dylibOrdinal = it->other;
+ sym.importName = it->importName;
+ if ( (sym.importName == NULL) || (sym.importName[0] == '\0') )
+ sym.importName = sym.exportName;
+ fReExportedSymbols.push_back(sym);
+ // fHashTable entry will be added in first call to findExportedSymbolAddress()
+ }
+ else {
+ fHashTable[it->name] = it->address + baseAddress;
+ }
+ }
+ else {
+ throwf("non-regular symbol binding not supported for %s in %s", it->name, layout.getFilePath());
+ }
//fprintf(stderr, "0x%08llX %s\n", it->address + baseAddress, it->name);
}
}
fHashTable.resize(fDynamicInfo->nextdefsym()); // set initial bucket count
for (const macho_nlist<P>* sym=start; sym < end; ++sym) {
const char* name = &fStrings[sym->n_strx()];
- fHashTable[name] = sym->n_value();
+ fHashTable[name] = runtimeAddressFromNList(sym);
//fprintf(stderr, " 0x%08llX %s\n", sym->n_value(), name);
}
}
const uint32_t index = E::get32(toc[i].symbol_index);
const macho_nlist<P>* sym = &fSymbolTable[index];
const char* name = &fStrings[sym->n_strx()];
- fHashTable[name] = sym->n_value();
+ fHashTable[name] = runtimeAddressFromNList(sym);
//fprintf(stderr, "- 0x%08llX %s\n", sym->n_value(), name);
}
}
}
+template <typename A>
+bool Binder<A>::isPublicLocation(const char* pth)
+{
+ // /usr/lib is a public location
+ if ( (strncmp(pth, "/usr/lib/", 9) == 0) && (strchr(&pth[9], '/') == NULL) )
+ return true;
+
+ // /System/Library/Frameworks/ is a public location
+ if ( strncmp(pth, "/System/Library/Frameworks/", 27) == 0 ) {
+ const char* frameworkDot = strchr(&pth[27], '.');
+ // but only top level framework
+ // /System/Library/Frameworks/Foo.framework/Versions/A/Foo ==> true
+ // /System/Library/Frameworks/Foo.framework/Resources/libBar.dylib ==> false
+ // /System/Library/Frameworks/Foo.framework/Frameworks/Bar.framework/Bar ==> false
+ // /System/Library/Frameworks/Foo.framework/Frameworks/Xfoo.framework/XFoo ==> false
+ if ( frameworkDot != NULL ) {
+ int frameworkNameLen = frameworkDot - &pth[27];
+ if ( strncmp(&pth[strlen(pth)-frameworkNameLen-1], &pth[26], frameworkNameLen+1) == 0 )
+ return true;
+ }
+ }
+
+ return false;
+}
template <typename A>
void Binder<A>::setDependentBinders(const Map& map)
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
const char* path = ((struct macho_dylib_command<P>*)cmd)->name();
typename Map::const_iterator pos = map.find(path);
if ( pos != map.end() ) {
}
}
}
+
+}
+
+template <typename A>
+int Binder<A>::ordinalOfDependentBinder(Binder<A>* dep)
+{
+ for (int i=0; i < fDependentDylibs.size(); ++i) {
+ if ( fDependentDylibs[i].binder == dep )
+ return i+1;
+ }
+ throw "dependend dylib not found";
+}
+
+template <typename A>
+void Binder<A>::hoistPrivateRexports()
+{
+ std::vector<Binder<A>*> privateReExportedDylibs;
+ for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
+ if ( it->reExport && ! isPublicLocation(it->binder->getDylibID()) )
+ privateReExportedDylibs.push_back(it->binder);
+ }
+ if ( privateReExportedDylibs.size() != 0 ) {
+ // parse export info into vector of exports
+ const uint8_t* exportsStart = this->fLayout.getDyldInfoExports();
+ const uint8_t* exportsEnd = &exportsStart[fDyldInfo->export_size()];
+ std::vector<mach_o::trie::Entry> exports;
+ mach_o::trie::parseTrie(exportsStart, exportsEnd, exports);
+ //fprintf(stderr, "%s exports %lu symbols from trie of size %u \n", this->fLayout.getFilePath(), exports.size(), fDyldInfo->export_size());
+
+ // add re-exports for each export from an re-exported dylib
+ for(typename std::vector<Binder<A>*>::iterator it = privateReExportedDylibs.begin(); it != privateReExportedDylibs.end(); ++it) {
+ Binder<A>* binder = *it;
+ int ordinal = ordinalOfDependentBinder(binder);
+ const uint8_t* aDylibsExportsStart = binder->fLayout.getDyldInfoExports();
+ const uint8_t* aDylibsExportsEnd = &aDylibsExportsStart[binder->fDyldInfo->export_size()];
+ std::vector<mach_o::trie::Entry> aDylibsExports;
+ mach_o::trie::parseTrie(aDylibsExportsStart, aDylibsExportsEnd, aDylibsExports);
+ //fprintf(stderr, "%s re-exports %lu symbols from %s\n", this->fLayout.getFilePath(), aDylibsExports.size(), binder->getDylibID());
+ for(std::vector<mach_o::trie::Entry>::iterator eit = aDylibsExports.begin(); eit != aDylibsExports.end(); ++eit) {
+ mach_o::trie::Entry entry = *eit;
+ entry.flags |= EXPORT_SYMBOL_FLAGS_REEXPORT;
+ entry.other = ordinal;
+ entry.importName = NULL;
+ exports.push_back(entry);
+ }
+ }
+ // rebuild new combined trie
+ std::vector<uint8_t> newExportTrieBytes;
+ newExportTrieBytes.reserve(fDyldInfo->export_size());
+ mach_o::trie::makeTrie(exports, newExportTrieBytes);
+ //fprintf(stderr, "%s now exports %lu symbols from trie of size %lu\n", this->fLayout.getFilePath(), exports.size(), newExportTrieBytes.size());
+
+ // allocate new buffer and set export_off to use new buffer instead
+ uint32_t newExportsSize = newExportTrieBytes.size();
+ uint8_t* sideTrie = new uint8_t[newExportsSize];
+ memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
+ this->fLayout.setDyldInfoExports(sideTrie);
+ ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
+ ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
+ }
}
+
template <typename A>
-void Binder<A>::bind()
+void Binder<A>::bind(std::vector<void*>& pointersInData)
{
this->doSetUpDyldSection();
if ( fDyldInfo != NULL ) {
- this->doBindDyldInfo();
- this->doBindDyldLazyInfo();
+ this->doBindDyldInfo(pointersInData);
+ this->doBindDyldLazyInfo(pointersInData);
+ this->hoistPrivateRexports();
// weak bind info is processed at launch time
}
else {
}
template <typename A>
-void Binder<A>::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal, int64_t addend, const char* symbolName)
+void Binder<A>::bindDyldInfoAt(uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, int libraryOrdinal,
+ int64_t addend, const char* symbolName, bool lazyPointer, std::vector<void*>& pointersInData)
{
//printf("%d 0x%08llX type=%d, lib=%d, addend=%lld, symbol=%s\n", segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
const std::vector<MachOLayoutAbstraction::Segment>& segments = this->fLayout.getSegments();
throw "bad segment index in rebase info";
if ( libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP )
- throw "flat_namespace linkage not allowed in dyld shared cache";
+ throw "dynamic lookup linkage not allowed in dyld shared cache";
if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE )
throw "linkage to main executable not allowed in dyld shared cache";
else
binder = fDependentDylibs[libraryOrdinal-1].binder;
pint_t targetSymbolAddress;
- if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress) )
- throwf("could not resolve %s expected in %s", symbolName, binder->getDylibID());
+ bool isResolverSymbol;
+ Binder<A>* foundIn;
+ if ( ! binder->findExportedSymbolAddress(symbolName, &targetSymbolAddress, &foundIn, &isResolverSymbol) )
+ throwf("could not bind symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID());
+
+ // don't bind lazy pointers to resolver stubs in shared cache
+ if ( lazyPointer && isResolverSymbol ) {
+ if ( foundIn == this ) {
+ // record location of lazy pointer for other dylibs to re-use
+ pint_t lpVMAddr = segments[segmentIndex].newAddress() + segmentOffset;
+ foundIn->addResolverLazyPointerMappedAddress(symbolName, lpVMAddr);
+ //fprintf(stderr, "resolver %s in %s has lazy pointer with segmentOffset=0x%08llX\n", symbolName, this->getDylibID(), segmentOffset);
+ }
+ else {
+ // record that this dylib has a lazy pointer to a resolver function
+ foundIn->addResolverClient(this, symbolName);
+ // fprintf(stderr, "have lazy pointer to resolver %s in %s\n", symbolName, this->getDylibID());
+ }
+ return;
+ }
// do actual update
const MachOLayoutAbstraction::Segment& seg = segments[segmentIndex];
default:
throw "bad bind type";
}
+ pointersInData.push_back(mappedAddr);
}
template <typename A>
-void Binder<A>::doBindDyldLazyInfo()
+void Binder<A>::doBindDyldLazyInfo(std::vector<void*>& pointersInData)
{
const uint8_t* p = &this->fLinkEditBase[fDyldInfo->lazy_bind_off()];
const uint8_t* end = &p[fDyldInfo->lazy_bind_size()];
segmentOffset = read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
+ bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, true, pointersInData);
segmentOffset += sizeof(pint_t);
break;
case BIND_OPCODE_SET_TYPE_IMM:
}
template <typename A>
-void Binder<A>::doBindDyldInfo()
+void Binder<A>::doBindDyldInfo(std::vector<void*>& pointersInData)
{
const uint8_t* p = &this->fLinkEditBase[fDyldInfo->bind_off()];
const uint8_t* end = &p[fDyldInfo->bind_size()];
segmentOffset += read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
+ bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData);
segmentOffset += sizeof(pint_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
+ bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData);
segmentOffset += read_uleb128(p, end) + sizeof(pint_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
+ bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData);
segmentOffset += immediate*sizeof(pint_t) + sizeof(pint_t);
break;
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName);
+ bindDyldInfoAt(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, false, pointersInData);
segmentOffset += skip + sizeof(pint_t);
}
break;
const macho_nlist<P>* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum()];
pint_t* location;
try {
- location = mappedAddressForNewAddress(reloc->r_address() + firstWritableSegmentBaseAddress);
+ location = this->mappedAddressForNewAddress(reloc->r_address() + firstWritableSegmentBaseAddress);
}
catch (const char* msg) {
throwf("%s processesing external relocation r_address 0x%08X", msg, reloc->r_address());
if ( (undefinedSymbol->n_type() & N_TYPE) == N_SECT ) {
if ( (undefinedSymbol->n_type() & N_PEXT) != 0 ) {
// is a multi-module private_extern internal reference that the linker did not optimize away
- return undefinedSymbol->n_value();
+ return runtimeAddressFromNList(undefinedSymbol);
}
if ( (undefinedSymbol->n_desc() & N_WEAK_DEF) != 0 ) {
// is a weak definition, we should prebind to this one in the same linkage unit
- return undefinedSymbol->n_value();
+ return runtimeAddressFromNList(undefinedSymbol);
}
}
const char* symbolName = &fStrings[undefinedSymbol->n_strx()];
binder = fDependentDylibs[ordinal-1].binder;
}
pint_t addr;
- if ( ! binder->findExportedSymbolAddress(symbolName, &addr) )
- throwf("could not resolve %s expected in %s", symbolName, binder->getDylibID());
+ bool isResolver;
+ Binder<A>* foundIn;
+ if ( ! binder->findExportedSymbolAddress(symbolName, &addr, &foundIn, &isResolver) )
+ throwf("could not resolve undefined symbol %s in %s expected in %s", symbolName, this->getDylibID(), binder->getDylibID());
return addr;
}
}
template <typename A>
-bool Binder<A>::findExportedSymbolAddress(const char* name, pint_t* result)
+bool Binder<A>::findExportedSymbolAddress(const char* name, pint_t* result, Binder<A>** foundIn, bool* isResolverSymbol)
{
+ *foundIn = NULL;
+ // since re-export chains can be any length, re-exports cannot be resolved in setDependencies()
+ // instead we lazily, recursively update
+ if ( !fReExportedSymbolsResolved ) {
+
+ // update fHashTable with any individual symbol re-exports
+ for (typename std::vector<SymbolReExport>::iterator it=fReExportedSymbols.begin(); it != fReExportedSymbols.end(); ++it) {
+ pint_t targetSymbolAddress;
+ bool isResolver;
+
+ if ( it->dylibOrdinal <= 0 )
+ throw "bad mach-o binary, special library ordinal not allowed in re-exported symbols in dyld shared cache";
+
+ Binder<A>* binder = fDependentDylibs[it->dylibOrdinal-1].binder;
+
+ if ( ! binder->findExportedSymbolAddress(it->importName, &targetSymbolAddress, foundIn, &isResolver) )
+ throwf("could not bind symbol %s in %s expected in %s", it->importName, this->getDylibID(), binder->getDylibID());
+
+ if ( isResolver )
+ throw "bad mach-o binary, re-export of resolvers symbols not supported in dyld shared cache";
+
+ fHashTable[it->exportName] = targetSymbolAddress;
+ }
+ // mark as done
+ fReExportedSymbolsResolved = true;
+ }
+
+ *isResolverSymbol = false;
+ if ( !fSymbolResolvers.empty() && fSymbolResolvers.count(name) ) {
+ // lazy pointers should be left unbound, rather than bind to resolver stub
+ *isResolverSymbol = true;
+ }
+
typename NameToAddrMap::iterator pos = fHashTable.find(name);
if ( pos != fHashTable.end() ) {
*result = pos->second;
//fprintf(stderr, "findExportedSymbolAddress(%s) => 0x%08llX in %s\n", name, (uint64_t)*result, this->getDylibID());
+ *foundIn = this;
return true;
}
// search re-exports
for (typename std::vector<BinderAndReExportFlag>::iterator it = fDependentDylibs.begin(); it != fDependentDylibs.end(); ++it) {
if ( it->reExport ) {
- if ( it->binder->findExportedSymbolAddress(name, result) )
+ if ( it->binder->findExportedSymbolAddress(name, result, foundIn, isResolverSymbol) )
return true;
}
}
return false;
}
+// record which dylibs will be using this dylibs lazy pointer
+template <typename A>
+void Binder<A>::addResolverClient(Binder<A>* clientDylib, const char* symbolName)
+{
+ ClientAndSymbol x;
+ x.client = clientDylib;
+ x.symbolName = symbolName;
+ fClientAndSymbols.push_back(x);
+}
+
+// Record that this dylib has an lazy pointer that points within itself for use
+// with a resolver function.
+template <typename A>
+void Binder<A>::addResolverLazyPointerMappedAddress(const char* symbolName, pint_t lpVMAddr)
+{
+ SymbolAndLazyPointer x;
+ x.symbolName = symbolName;
+ x.lpVMAddr = lpVMAddr;
+ fSymbolAndLazyPointers.push_back(x);
+}
+
+template <typename A>
+typename A::P::uint_t Binder<A>::findLazyPointerFor(const char* symbolName)
+{
+ for (typename std::vector<SymbolAndLazyPointer>::iterator it = fSymbolAndLazyPointers.begin(); it != fSymbolAndLazyPointers.end(); ++it) {
+ if ( strcmp(it->symbolName, symbolName) == 0 )
+ return it->lpVMAddr;
+ }
+ return 0;
+}
+
+// called after all binding is done to optimize lazy pointers
+template <typename A>
+void Binder<A>::optimize()
+{
+ for (typename std::vector<ClientAndSymbol>::iterator it = fClientAndSymbols.begin(); it != fClientAndSymbols.end(); ++it) {
+ pint_t lpVMAddr = findLazyPointerFor(it->symbolName);
+ if ( lpVMAddr != 0 ) {
+ it->client->optimizeStub(it->symbolName, lpVMAddr);
+ }
+ else {
+ fprintf(stderr, "not able to optimize lazy pointer for %s in %s\n", it->symbolName, it->client->getDylibID());
+ }
+
+ }
+}
+
+template <>
+void Binder<arm>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
+{
+ if ( stubSize != 16 ) {
+ fprintf(stderr, "could not optimize ARM stub to resolver function in %s because it is wrong size\n", this->getDylibID());
+ return;
+ }
+ uint32_t* instructions = (uint32_t*)stubMappedAddress;
+ if ( (E::get32(instructions[0]) != 0xe59fc004)
+ || (E::get32(instructions[1]) != 0xe08fc00c)
+ || (E::get32(instructions[2]) != 0xe59cf000)
+ ) {
+ fprintf(stderr, "could not optimize ARM stub to resolver function in %s because instructions are not as expected\n", this->getDylibID());
+ return;
+ }
+ // last .long in stub is: lazyPtr - (stub+8)
+ // alter to point to more optimal lazy pointer
+ uint32_t betterOffset = lpVMAddr - (stubVMAddress + 12);
+ E::set32(instructions[3], betterOffset);
+}
+
+
+template <>
+void Binder<x86_64>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddr)
+{
+ if ( stubSize != 6 ) {
+ fprintf(stderr, "could not optimize x86_64 stub to resolver function in %s because it is wrong size\n", this->getDylibID());
+ return;
+ }
+ if ( (stubMappedAddress[0] != 0xFF) || (stubMappedAddress[1] != 0x25) ) {
+ fprintf(stderr, "could not optimize stub to resolver function in %s because instructions are not as expected\n", this->getDylibID());
+ return;
+ }
+ // last four bytes in stub is RIP relative offset to lazy pointer
+ // alter to point to more optimal lazy pointer
+ uint32_t betterOffset = lpVMAddr - (stubVMAddress + 6);
+ E::set32(*((uint32_t*)(&stubMappedAddress[2])), betterOffset);
+}
+
+template <typename A>
+void Binder<A>::optimizeStub(uint8_t* stubMappedAddress, pint_t stubVMAddress, uint32_t stubSize, pint_t lpVMAddress)
+{
+ // Remaining architectures are not optimized
+ //fprintf(stderr, "optimize stub at %p in %s to use lazyPointer at 0x%llX\n", stubMappedAddress, this->getDylibID(), (uint64_t)lpVMAddress);
+}
+
+// search for stub in this image that call target symbol name and then optimize its lazy pointer
+template <typename A>
+void Binder<A>::optimizeStub(const char* stubName, pint_t lpVMAddr)
+{
+ // find named stub
+ const uint32_t* const indirectTable = (uint32_t*)&this->fLinkEditBase[fDynamicInfo->indirectsymoff()];
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this->fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = this->fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) {
+ pint_t stubsVMStart = sect->addr();
+ uint8_t* stubsMappingStart = (uint8_t*)this->mappedAddressForNewAddress(stubsVMStart);
+ const uint32_t indirectTableOffset = sect->reserved1();
+ const uint32_t stubSize = sect->reserved2();
+ uint32_t elementCount = sect->size() / stubSize;
+ pint_t stubVMAddr = stubsVMStart;
+ uint8_t* stubMappedAddr = stubsMappingStart;
+ for (uint32_t j=0; j < elementCount; ++j, stubMappedAddr += stubSize, stubVMAddr += stubSize) {
+ uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]);
+ switch ( symbolIndex ) {
+ case INDIRECT_SYMBOL_ABS:
+ case INDIRECT_SYMBOL_LOCAL:
+ break;
+ default:
+ {
+ const macho_nlist<P>* sym = &this->fSymbolTable[symbolIndex];
+ const char* symName = &fStrings[sym->n_strx()];
+ if ( strcmp(symName, stubName) == 0 )
+ this->optimizeStub(stubMappedAddr, stubVMAddr, stubSize, lpVMAddr);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
#endif // __MACHO_BINDER__
#define S_16BYTE_LITERALS 0xE
#endif
+#ifndef CPU_SUBTYPE_ARM_V5TEJ
+ #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7)
+#endif
+#ifndef CPU_SUBTYPE_ARM_XSCALE
+ #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8)
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7
+ #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
+#endif
+
+#ifndef LC_LOAD_UPWARD_DYLIB
+ #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
+#endif
+
+#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
+ #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
+#endif
+#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT
+ #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08
+#endif
+
#include "FileAbstraction.hpp"
#include "Architectures.hpp"
const macho_segment_command<P>* getSegment(const char *segname) const
{
- const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
- const uint32_t cmd_count = this->ncmds();
+ const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
+ uint32_t cmd_count = this->ncmds();
const macho_load_command<P>* cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
- const macho_segment_command<P>* segcmd =
- (macho_segment_command<P>*)cmd;
+ const macho_segment_command<P>* segcmd = (macho_segment_command<P>*)cmd;
if (0 == strncmp(segname, segcmd->segname(), 16)) {
return segcmd;
}
}
- cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
return NULL;
}
const macho_section<P>* getSection(const char *segname, const char *sectname) const
{
- const macho_segment_command<P>* const segcmd = getSegment(segname);
+ const macho_segment_command<P>* segcmd = getSegment(segname);
if (!segcmd) return NULL;
const macho_section<P>* sectcmd = (macho_section<P>*)(segcmd+1);
- const uint32_t section_count = segcmd->nsects();
+ uint32_t section_count = segcmd->nsects();
for (uint32_t j = 0; j < section_count; ++j) {
if (0 == ::strncmp(sectcmd[j].sectname(), sectname, 16)) {
return sectcmd+j;
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2006-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/errno.h>
#include <sys/mman.h>
#include <mach/mach.h>
#include <limits.h>
virtual uint32_t getFileType() const = 0;
virtual uint32_t getFlags() const = 0;
virtual Library getID() const = 0;
+ virtual bool isDylib() const = 0;
virtual bool isSplitSeg() const = 0;
virtual bool hasSplitSegInfo() const = 0;
virtual bool isRootOwned() const = 0;
virtual bool inSharableLocation() const = 0;
+ virtual bool hasDynamicLookupLinkage() const = 0;
+ virtual bool hasMainExecutableLookupLinkage() const = 0;
+ virtual bool isTwoLevelNamespace() const = 0;
+ virtual bool hasDyldInfo() const = 0;
virtual uint32_t getNameFileOffset() const = 0;
virtual time_t getLastModTime() const = 0;
virtual ino_t getInode() const = 0;
virtual uint64_t getExecutableVMSize() const = 0;
virtual uint64_t getWritableVMSize() const = 0;
virtual uint64_t getReadOnlyVMSize() const = 0;
+ // need getDyldInfoExports because export info uses ULEB encoding and size could grow
+ virtual const uint8_t* getDyldInfoExports() const = 0;
+ virtual void setDyldInfoExports(const uint8_t* newExports) const = 0;
};
virtual uint32_t getFileType() const { return fFileType; }
virtual uint32_t getFlags() const { return fFlags; }
virtual Library getID() const { return fDylibID; }
+ virtual bool isDylib() const { return fIsDylib; }
virtual bool isSplitSeg() const;
virtual bool hasSplitSegInfo() const { return fHasSplitSegInfo; }
virtual bool isRootOwned() const { return fRootOwned; }
virtual bool inSharableLocation() const { return fShareableLocation; }
+ virtual bool hasDynamicLookupLinkage() const { return fDynamicLookupLinkage; }
+ virtual bool hasMainExecutableLookupLinkage() const { return fMainExecutableLookupLinkage; }
+ virtual bool isTwoLevelNamespace() const { return (fFlags & MH_TWOLEVEL); }
+ virtual bool hasDyldInfo() const { return fHasDyldInfo; }
virtual uint32_t getNameFileOffset() const{ return fNameFileOffset; }
virtual time_t getLastModTime() const { return fMTime; }
virtual ino_t getInode() const { return fInode; }
virtual uint64_t getExecutableVMSize() const { return fVMExecutableSize; }
virtual uint64_t getWritableVMSize() const { return fVMWritablSize; }
virtual uint64_t getReadOnlyVMSize() const { return fVMReadOnlySize; }
+ virtual const uint8_t* getDyldInfoExports() const { return fDyldInfoExports; }
+ virtual void setDyldInfoExports(const uint8_t* newExports) const { fDyldInfoExports = newExports; }
private:
typedef typename A::P P;
bool fHasSplitSegInfo;
bool fRootOwned;
bool fShareableLocation;
+ bool fDynamicLookupLinkage;
+ bool fMainExecutableLookupLinkage;
+ bool fIsDylib;
+ bool fHasDyldInfo;
+ mutable const uint8_t* fDyldInfoExports;
};
};
typedef __gnu_cxx::hash_map<const char*, const UniversalMachOLayout*, __gnu_cxx::hash<const char*>, CStringEquals> PathToNode;
- static bool compatibleSubtype(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType);
- static bool bestSliceForArch(uint32_t sliceCount, const struct fat_arch* slices, ArchPair ap, uint32_t& bestSliceIndex);
- static const cpu_subtype_t* getArmSubtypeList(cpu_subtype_t s);
+ static bool requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType);
static PathToNode fgLayoutCache;
const char* fPath;
-// armv7 can run: v7, v6, v5, and v4
-static const cpu_subtype_t kARMV7compatibleSubTypes[] =
- { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0 };
-
-// armv6 can run: v6, v5, and v4
-static const cpu_subtype_t kARMV6compatibleSubTypes[] =
- { CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0};
-
-// xscale can run: xscale, v5, and v4
-static const cpu_subtype_t kARMXscaleCompatibleSubTypes[] =
- { CPU_SUBTYPE_ARM_XSCALE, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0};
-
-// armv5 can run: v5 and v4
-static const cpu_subtype_t kARMV5compatibleSubTypes[] =
- { CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0};
-
-// armv4 can run: v4
-static const cpu_subtype_t kARMV4compatibleSubTypes[] =
- { CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, 0 };
-
-const cpu_subtype_t* UniversalMachOLayout::getArmSubtypeList(cpu_subtype_t s)
-{
- switch ( s ) {
- case CPU_SUBTYPE_ARM_V7:
- return kARMV7compatibleSubTypes;
- case CPU_SUBTYPE_ARM_V6:
- return kARMV6compatibleSubTypes;
- case CPU_SUBTYPE_ARM_XSCALE:
- return kARMXscaleCompatibleSubTypes;
- case CPU_SUBTYPE_ARM_V5TEJ:
- return kARMV5compatibleSubTypes;
- case CPU_SUBTYPE_ARM_V4T:
- return kARMV4compatibleSubTypes;
- }
- return NULL;
-}
-
-
-
const MachOLayoutAbstraction* UniversalMachOLayout::getSlice(ArchPair ap) const
{
- switch ( ap.arch ) {
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_I386:
- case CPU_TYPE_X86_64:
- // use first matching cputype
- for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
- const MachOLayoutAbstraction* layout = *it;
- if ( layout->getArchPair().arch == ap.arch )
- return layout;
- }
- break;
- case CPU_TYPE_ARM:
- const cpu_subtype_t* list = getArmSubtypeList(ap.subtype);
- if ( list != NULL ) {
- // known subtype, find best match
- for(const cpu_subtype_t* s=list; *s != 0; ++s) {
- for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
- const MachOLayoutAbstraction* layout = *it;
- if ( (layout->getArchPair().arch == ap.arch) && (layout->getArchPair().subtype == *s) )
- return layout;
- }
- }
- }
- else {
- // unknown arm sub-type, must have exact match
- for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
- const MachOLayoutAbstraction* layout = *it;
- if ( (layout->getArchPair().arch == ap.arch) && (layout->getArchPair().subtype == ap.subtype) )
- return layout;
- }
- }
+ // use matching cputype and cpusubtype
+ for(std::vector<MachOLayoutAbstraction*>::const_iterator it=fLayouts.begin(); it != fLayouts.end(); ++it) {
+ const MachOLayoutAbstraction* layout = *it;
+ if ( layout->getArchPair().arch == ap.arch ) {
+ switch ( ap.arch ) {
+ case CPU_TYPE_ARM:
+ if ( layout->getArchPair().subtype == ap.subtype )
+ return layout;
+ break;
+ default:
+ return layout;
+ }
+ }
}
- throwf("no compatible slice found in %s", fPath);
+ return NULL;
}
return *result;
}
-bool UniversalMachOLayout::bestSliceForArch(uint32_t sliceCount, const struct fat_arch* slices, ArchPair ap, uint32_t& bestSliceIndex)
-{
- switch ( ap.arch ) {
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_I386:
- case CPU_TYPE_X86_64:
- // use first matching cputype
- for (uint32_t i=0; i < sliceCount; ++i) {
- if ( OSSwapBigToHostInt32(slices[i].cputype) == ap.arch ) {
- bestSliceIndex = i;
- return true;
- }
- }
- return false;
- case CPU_TYPE_ARM:
- // find best matching arch
- const cpu_subtype_t* list = getArmSubtypeList(ap.subtype);
- if ( list != NULL ) {
- for(const cpu_subtype_t* s=list; *s != 0; ++s) {
- for (uint32_t i=0; i < sliceCount; ++i) {
- if ( (OSSwapBigToHostInt32(slices[i].cputype) == ap.arch) && (OSSwapBigToHostInt32(slices[i].cpusubtype) == *s) ) {
- bestSliceIndex = i;
- return true;
- }
- }
- }
- return false;
- }
- // unknown arm sub-type, must have exact match
- for (uint32_t i=0; i < sliceCount; ++i) {
- if ( (OSSwapBigToHostInt32(slices[i].cputype) == ap.arch) && (OSSwapBigToHostInt32(slices[i].cpusubtype) == ap.subtype) ) {
- bestSliceIndex = i;
- return true;
- }
- }
- return false;
- }
- throw "unknown architecture";
-}
-bool UniversalMachOLayout::compatibleSubtype(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType)
+bool UniversalMachOLayout::requestedSlice(const std::set<ArchPair>* onlyArchs, cpu_type_t cpuType, cpu_subtype_t cpuSubType)
{
+ if ( onlyArchs == NULL )
+ return true;
+ // must match cputype and cpusubtype
for (std::set<ArchPair>::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) {
- if ( cpuType == it->arch ) {
- switch ( it->arch ) {
- case CPU_TYPE_POWERPC:
- case CPU_TYPE_I386:
- case CPU_TYPE_X86_64:
- // just match cpu type
- return true;
- case CPU_TYPE_ARM:
- {
- const cpu_subtype_t* list = getArmSubtypeList(it->subtype);
- if ( list != NULL ) {
- // see if mach-o file is supported by this ArchPair
- for(const cpu_subtype_t* s=list; *s != 0; ++s) {
- if ( *s == cpuSubType )
- return true;
- }
- }
- else {
- // unknown arm sub-type, must have exact match
- if ( it->subtype == cpuSubType )
- return true;
- }
- }
- }
- }
+ ArchPair anArch = *it;
+ if ( cpuType == anArch.arch ) {
+ switch ( cpuType ) {
+ case CPU_TYPE_ARM:
+ if ( cpuSubType == anArch.subtype )
+ return true;
+ break;
+ default:
+ return true;
+ }
+ }
}
return false;
}
{
// map in whole file
int fd = ::open(path, O_RDONLY, 0);
- if ( fd == -1 )
- throwf("can't open file, errno=%d", errno);
+ if ( fd == -1 ) {
+ int err = errno;
+ if ( err == ENOENT )
+ throwf("file not found");
+ else
+ throwf("can't open file, errno=%d", err);
+ }
struct stat stat_buf;
if ( fstat(fd, &stat_buf) == -1)
throwf("can't stat open file %s, errno=%d", path, errno);
// Fat header is always big-endian
const struct fat_arch* slices = (struct fat_arch*)(p + sizeof(struct fat_header));
const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch);
- std::set<uint32_t> slicesToUse;
- if ( onlyArchs == NULL ) {
- // no filter, so instantiate all slices
- for (uint32_t i=0; i < sliceCount; ++i)
- slicesToUse.insert(i);
- }
- else {
- // instantiate only slices that are best for each architecture
- for (std::set<ArchPair>::const_iterator it = onlyArchs->begin(); it != onlyArchs->end(); ++it) {
- uint32_t bestSliceIndex;
- if ( bestSliceForArch(sliceCount, slices, *it, bestSliceIndex) )
- slicesToUse.insert(bestSliceIndex);
- }
- }
for (uint32_t i=0; i < sliceCount; ++i) {
- if ( slicesToUse.count(i) ) {
+ if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(slices[i].cputype), OSSwapBigToHostInt32(slices[i].cpusubtype)) ) {
uint32_t fileOffset = OSSwapBigToHostInt32(slices[i].offset);
if ( fileOffset > stat_buf.st_size ) {
throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
i, OSSwapBigToHostInt32(slices[i].cputype), path);
}
+ if ( (fileOffset+OSSwapBigToHostInt32(slices[i].size)) > stat_buf.st_size ) {
+ throwf("malformed universal file, slice %u for architecture 0x%08X is beyond end of file: %s",
+ i, OSSwapBigToHostInt32(slices[i].cputype), path);
+ }
try {
switch ( OSSwapBigToHostInt32(slices[i].cputype) ) {
case CPU_TYPE_POWERPC:
else {
try {
if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC)) {
- if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
+ if ( requestedSlice(onlyArchs, OSSwapBigToHostInt32(mh->cputype), OSSwapBigToHostInt32(mh->cpusubtype)) )
fLayouts.push_back(new MachOLayout<ppc>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
}
else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_I386)) {
- if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
+ if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
fLayouts.push_back(new MachOLayout<x86>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
}
else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_X86_64)) {
- if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
+ if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
fLayouts.push_back(new MachOLayout<x86_64>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
}
else if ( (OSSwapLittleToHostInt32(mh->magic) == MH_MAGIC) && (OSSwapLittleToHostInt32(mh->cputype) == CPU_TYPE_ARM)) {
- if ( (onlyArchs == NULL) || compatibleSubtype(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
+ if ( requestedSlice(onlyArchs, OSSwapLittleToHostInt32(mh->cputype), OSSwapLittleToHostInt32(mh->cpusubtype)) )
fLayouts.push_back(new MachOLayout<arm>(mh, 0, fPath, stat_buf.st_ino, stat_buf.st_mtime, stat_buf.st_uid));
}
else if ( (OSSwapBigToHostInt32(mh->magic) == MH_MAGIC_64) && (OSSwapBigToHostInt32(mh->cputype) == CPU_TYPE_POWERPC64)) {
template <typename A>
MachOLayout<A>::MachOLayout(const void* machHeader, uint64_t offset, const char* path, ino_t inode, time_t modTime, uid_t uid)
: fPath(path), fOffset(offset), fArchPair(0,0), fMTime(modTime), fInode(inode), fHasSplitSegInfo(false), fRootOwned(uid==0),
- fShareableLocation(false)
+ fShareableLocation(false), fDynamicLookupLinkage(false), fMainExecutableLookupLinkage(false), fIsDylib(false),
+ fHasDyldInfo(false), fDyldInfoExports(NULL)
{
fDylibID.name = NULL;
fDylibID.currentVersion = 0;
throw "Layout object is wrong architecture";
switch ( mh->filetype() ) {
case MH_DYLIB:
+ fIsDylib = true;
+ break;
case MH_BUNDLE:
case MH_EXECUTE:
case MH_DYLIB_STUB:
fArchPair.arch = mh->cputype();
fArchPair.subtype = mh->cpusubtype();
+ const macho_dyld_info_command<P>* dyldInfo = NULL;
+ const macho_symtab_command<P>* symbolTableCmd = NULL;
+ const macho_dysymtab_command<P>* dynamicSymbolTableCmd = NULL;
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
const uint32_t cmd_count = mh->ncmds();
const macho_load_command<P>* cmd = cmds;
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
{
macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
Library lib;
segCmd->filesize(), segCmd->initprot(), segCmd->segname()));
}
break;
+ case LC_SYMTAB:
+ symbolTableCmd = (macho_symtab_command<P>*)cmd;
+ break;
+ case LC_DYSYMTAB:
+ dynamicSymbolTableCmd = (macho_dysymtab_command<P>*)cmd;
+ break;
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ fHasDyldInfo = true;
+ dyldInfo = (struct macho_dyld_info_command<P>*)cmd;
+ break;
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
if ( (fLowReadOnlySegment == NULL) || (seg.address() < fLowReadOnlySegment->address()) )
fLowReadOnlySegment = &seg;
fVMReadOnlySize += seg.size();
- }
+ }
}
if ( (highSegment != NULL) && (fLowSegment != NULL) )
fVMSize = (highSegment->address() + highSegment->size() - fLowSegment->address() + 4095) & (-4096);
+
+ // scan undefines looking, for magic ordinals
+ if ( (symbolTableCmd != NULL) && (dynamicSymbolTableCmd != NULL) ) {
+ const macho_nlist<P>* symbolTable = (macho_nlist<P>*)((uint8_t*)machHeader + symbolTableCmd->symoff());
+ const uint32_t startUndefs = dynamicSymbolTableCmd->iundefsym();
+ const uint32_t endUndefs = startUndefs + dynamicSymbolTableCmd->nundefsym();
+ for (uint32_t i=startUndefs; i < endUndefs; ++i) {
+ uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc());
+ if ( ordinal == DYNAMIC_LOOKUP_ORDINAL )
+ fDynamicLookupLinkage = true;
+ else if ( ordinal == EXECUTABLE_ORDINAL )
+ fMainExecutableLookupLinkage = true;
+ }
+ }
+
+ if ( dyldInfo != NULL ) {
+ if ( dyldInfo->export_off() != 0 ) {
+ fDyldInfoExports = (uint8_t*)machHeader + dyldInfo->export_off();
+ }
+ }
+
}
template <> cpu_type_t MachOLayout<ppc>::arch() { return CPU_TYPE_POWERPC; }
virtual cpu_type_t getArchitecture() const = 0;
virtual uint64_t getBaseAddress() const = 0;
virtual uint64_t getVMSize() const = 0;
- virtual void rebase() = 0;
+ virtual void rebase(std::vector<void*>&) = 0;
};
virtual cpu_type_t getArchitecture() const;
virtual uint64_t getBaseAddress() const;
virtual uint64_t getVMSize() const;
- virtual void rebase();
+ virtual void rebase(std::vector<void*>&);
protected:
typedef typename A::P P;
void calculateRelocBase();
void adjustLoadCommands();
void adjustSymbolTable();
+ void optimzeStubs();
+ void makeNoPicStub(uint8_t* stub, pint_t logicalAddress);
void adjustDATA();
void adjustCode();
- void applyRebaseInfo();
+ void applyRebaseInfo(std::vector<void*>& pointersInData);
void adjustExportInfo();
- void doRebase(int segIndex, uint64_t segOffset, uint8_t type);
+ void doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData);
void adjustSegmentLoadCommand(macho_segment_command<P>* seg);
pint_t getSlideForVMAddress(pint_t vmaddress);
pint_t* mappedAddressForVMAddress(pint_t vmaddress);
const macho_dyld_info_command<P>* fDyldInfo;
bool fSplittingSegments;
bool fOrignalVMRelocBaseAddressValid;
+ pint_t fSkipSplitSegInfoStart;
+ pint_t fSkipSplitSegInfoEnd;
};
template <typename A>
Rebaser<A>::Rebaser(const MachOLayoutAbstraction& layout)
: fLayout(layout), fOrignalVMRelocBaseAddress(NULL), fLinkEditBase(NULL),
- fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false), fOrignalVMRelocBaseAddressValid(false)
+ fSymbolTable(NULL), fDynamicSymbolTable(NULL), fDyldInfo(NULL), fSplittingSegments(false),
+ fOrignalVMRelocBaseAddressValid(false), fSkipSplitSegInfoStart(0), fSkipSplitSegInfoEnd(0)
{
fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress();
switch ( fHeader->filetype() ) {
template <typename A>
-void Rebaser<A>::rebase()
+void Rebaser<A>::rebase(std::vector<void*>& pointersInData)
{
// update writable segments that have internal pointers
if ( fDyldInfo != NULL )
- this->applyRebaseInfo();
+ this->applyRebaseInfo(pointersInData);
else
this->adjustDATA();
// update symbol table
this->adjustSymbolTable();
+ // optimize stubs
+ this->optimzeStubs();
+
// update export info
if ( fDyldInfo != NULL )
this->adjustExportInfo();
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
if ( (fHeader->flags() & MH_PREBOUND) != 0 ) {
// clear expected timestamps so that this image will load with invalid prebinding
macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
}
+template <>
+void Rebaser<arm>::makeNoPicStub(uint8_t* stub, pint_t logicalAddress)
+{
+ uint32_t* instructions = (uint32_t*)stub;
+ if ( (LittleEndian::get32(instructions[0]) == 0xE59FC004) &&
+ (LittleEndian::get32(instructions[1]) == 0xE08FC00C) &&
+ (LittleEndian::get32(instructions[2]) == 0xE59CF000) ) {
+ uint32_t lazyPtrAddress = instructions[3] + logicalAddress + 12;
+ LittleEndian::set32(instructions[0], 0xE59FC000); // ldr ip, [pc, #0]
+ LittleEndian::set32(instructions[1], 0xE59CF000); // ldr pc, [ip]
+ LittleEndian::set32(instructions[2], lazyPtrAddress); // .long L_foo$lazy_ptr
+ LittleEndian::set32(instructions[3], 0xE1A00000); // nop
+ }
+ else
+ fprintf(stderr, "unoptimized stub in %s at 0x%08X\n", fLayout.getFilePath(), logicalAddress);
+}
+
+
+#if 0
+// disable this optimization do allow cache to slide
+template <>
+void Rebaser<arm>::optimzeStubs()
+{
+ // convert pic stubs to no-pic stubs in dyld shared cache
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
+ const uint32_t cmd_count = fHeader->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS ) {
+ const uint32_t stubSize = sect->reserved2();
+ // ARM PIC stubs are 4 32-bit instructions long
+ if ( stubSize == 16 ) {
+ uint32_t stubCount = sect->size() / 16;
+ pint_t stubLogicalAddress = sect->addr();
+ uint8_t* stubMappedAddress = (uint8_t*)mappedAddressForNewAddress(stubLogicalAddress);
+ for(uint32_t s=0; s < stubCount; ++s) {
+ makeNoPicStub(stubMappedAddress, stubLogicalAddress);
+ stubLogicalAddress += 16;
+ stubMappedAddress += 16;
+ }
+ }
+ }
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+#endif
+
+template <typename A>
+void Rebaser<A>::optimzeStubs()
+{
+ // other architectures don't need stubs changed in shared cache
+}
+
template <typename A>
void Rebaser<A>::adjustSymbolTable()
{
// since export info addresses are offsets from mach_header, everything in __TEXT is fine
// only __DATA addresses need to be updated
- const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off()];
+ const uint8_t* start = fLayout.getDyldInfoExports();
const uint8_t* end = &start[fDyldInfo->export_size()];
std::vector<mach_o::trie::Entry> originalExports;
try {
while ( (newExportTrieBytes.size() % sizeof(pint_t)) != 0 )
newExportTrieBytes.push_back(0);
- // copy into place, zero pad
+ // allocate new buffer and set export_off to use new buffer instead
uint32_t newExportsSize = newExportTrieBytes.size();
- if ( newExportsSize > fDyldInfo->export_size() ) {
- // it is possible that the new export trie is larger than the old one
- // for those cases will malloc a block on the side and set up
- // export_off to point to it.
- uint8_t* sideTrie = new uint8_t[newExportsSize];
- memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
- //fprintf(stderr, "set_export_off()=%ld, fLinkEditBase=%p, sideTrie=%p\n", (long)(sideTrie - fLinkEditBase), fLinkEditBase, sideTrie);
- // warning, export_off is only 32-bits so if the trie grows it must be allocated with 32-bits of fLinkeditBase
- int64_t offset = sideTrie - fLinkEditBase;
- int32_t offset32 = (int32_t)offset;
- if ( offset != offset32 )
- throw "internal error, new trie allocated to far from fLinkeditBase";
- ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(offset32);
- ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
- }
- else {
- uint8_t* trie = (uint8_t*)&fLinkEditBase[fDyldInfo->export_off()];
- memcpy(trie, &newExportTrieBytes[0], newExportsSize);
- bzero(trie+newExportsSize, fDyldInfo->export_size() - newExportsSize);
- ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
- }
+ uint8_t* sideTrie = new uint8_t[newExportsSize];
+ memcpy(sideTrie, &newExportTrieBytes[0], newExportsSize);
+ fLayout.setDyldInfoExports(sideTrie);
+ ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_off(0); // invalidate old trie
+ ((macho_dyld_info_command<P>*)fDyldInfo)->set_export_size(newExportsSize);
}
template <typename A>
void Rebaser<A>::doCodeUpdate(uint8_t kind, uint64_t address, int64_t codeToDataDelta, int64_t codeToImportDelta)
{
- //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX)\n", kind, address, codeToDataDelta, codeToImportDelta);
+ // begin hack for <rdar://problem/8253549> split seg info wrong for x86_64 stub helpers
+ if ( (fSkipSplitSegInfoStart <= address) && (address < fSkipSplitSegInfoEnd) ) {
+ uint8_t* p = (uint8_t*)mappedAddressForVMAddress(address);
+ // only ignore split seg info for "push" instructions
+ if ( p[-1] == 0x68 )
+ return;
+ }
+ // end hack for <rdar://problem/8253549>
+
+ //fprintf(stderr, "doCodeUpdate(kind=%d, address=0x%0llX, dataDelta=0x%08llX, importDelta=0x%08llX, path=%s)\n",
+ // kind, address, codeToDataDelta, codeToImportDelta, fLayout.getFilePath());
uint32_t* p;
uint32_t instruction;
uint32_t value;
// get uleb128 compressed runs of code addresses to update
const uint8_t* infoStart = NULL;
const uint8_t* infoEnd = NULL;
+ const macho_segment_command<P>* seg;
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)fHeader + sizeof(macho_header<P>));
const uint32_t cmd_count = fHeader->ncmds();
const macho_load_command<P>* cmd = cmds;
infoEnd = &infoStart[segInfo->datasize()];
}
break;
+ // begin hack for <rdar://problem/8253549> split seg info wrong for x86_64 stub helpers
+ case macho_segment_command<P>::CMD:
+ seg = (macho_segment_command<P>*)cmd;
+ if ( (getArchitecture() == CPU_TYPE_X86_64) && (strcmp(seg->segname(), "__TEXT") == 0) ) {
+ const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ const macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for(const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( strcmp(sect->sectname(), "__stub_helper") == 0 ) {
+ fSkipSplitSegInfoStart = sect->addr();
+ fSkipSplitSegInfoEnd = sect->addr() + sect->size() - 16;
+ }
+ }
+ }
+ break;
+ // end hack for <rdar://problem/8253549> split seg info wrong for x86_64 stub helpers
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
codeToDataDelta = (dataSeg.newAddress() - codeSeg.newAddress()) - (dataSeg.address() - codeSeg.address());
}
// decompress and call doCodeUpdate() on each address
- for(const uint8_t* p = infoStart; *p != 0;) {
+ for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) {
uint8_t kind = *p++;
p = this->doCodeUpdateForEachULEB128Address(p, kind, orgBaseAddress, codeToDataDelta, codeToImportDelta);
}
}
template <typename A>
-void Rebaser<A>::doRebase(int segIndex, uint64_t segOffset, uint8_t type)
+void Rebaser<A>::doRebase(int segIndex, uint64_t segOffset, uint8_t type, std::vector<void*>& pointersInData)
{
const std::vector<MachOLayoutAbstraction::Segment>& segments = fLayout.getSegments();
if ( segIndex > segments.size() )
default:
throw "bad rebase type";
}
+ pointersInData.push_back(mappedAddr);
}
template <typename A>
-void Rebaser<A>::applyRebaseInfo()
+void Rebaser<A>::applyRebaseInfo(std::vector<void*>& pointersInData)
{
const uint8_t* p = &fLinkEditBase[fDyldInfo->rebase_off()];
const uint8_t* end = &p[fDyldInfo->rebase_size()];
break;
case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
for (int i=0; i < immediate; ++i) {
- doRebase(segIndex, segOffset, type);
+ doRebase(segIndex, segOffset, type, pointersInData);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES:
count = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- doRebase(segIndex, segOffset, type);
+ doRebase(segIndex, segOffset, type, pointersInData);
segOffset += sizeof(pint_t);
}
break;
case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
- doRebase(segIndex, segOffset, type);
+ doRebase(segIndex, segOffset, type, pointersInData);
segOffset += read_uleb128(p, end) + sizeof(pint_t);
break;
case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB:
count = read_uleb128(p, end);
skip = read_uleb128(p, end);
for (uint32_t i=0; i < count; ++i) {
- doRebase(segIndex, segOffset, type);
+ doRebase(segIndex, segOffset, type, pointersInData);
segOffset += skip + sizeof(pint_t);
}
break;
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#ifndef __MACH_O_TRIE__
#define __MACH_O_TRIE__
+#include <algorithm>
#include "MachOFileAbstraction.hpp"
+
namespace mach_o {
namespace trie {
struct Node
{
- Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), fOrdered(false),
+ Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0),
+ fOther(0), fImportedName(NULL), fOrdered(false),
fHaveExportInfo(false), fTrieOffset(0) {}
~Node() { }
const char* fCummulativeString;
std::vector<Edge> fChildren;
uint64_t fAddress;
- uint32_t fFlags;
+ uint64_t fFlags;
+ uint64_t fOther;
+ const char* fImportedName;
bool fOrdered;
bool fHaveExportInfo;
uint32_t fTrieOffset;
- void addSymbol(const char* fullStr, uint64_t address, uint32_t flags) {
+ void addSymbol(const char* fullStr, uint64_t address, uint64_t flags, uint64_t other, const char* importName) {
const char* partialStr = &fullStr[strlen(fCummulativeString)];
for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
Edge& e = *it;
int subStringLen = strlen(e.fSubString);
if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) {
// already have matching edge, go down that path
- e.fChild->addSymbol(fullStr, address, flags);
+ e.fChild->addSymbol(fullStr, address, flags, other, importName);
return;
}
else {
abEdge.fChild = bNode;
Edge bcEdge(bcEdgeStr, cNode);
bNode->fChildren.push_back(bcEdge);
- bNode->addSymbol(fullStr, address, flags);
+ bNode->addSymbol(fullStr, address, flags, other, importName);
return;
}
}
}
}
+
// no commonality with any existing child, make a new edge that is this whole string
Node* newNode = new Node(strdup(fullStr));
Edge newEdge(strdup(partialStr), newNode);
fChildren.push_back(newEdge);
newNode->fAddress = address;
newNode->fFlags = flags;
+ newNode->fOther = other;
+ if ( (flags & EXPORT_SYMBOL_FLAGS_REEXPORT) && (importName != NULL) && (strcmp(fullStr,importName) != 0) )
+ newNode->fImportedName = importName;
+ else
+ newNode->fImportedName = NULL;
newNode->fHaveExportInfo = true;
}
}
// byte for terminal node size in bytes, or 0x00 if not terminal node
- // teminal node (uleb128 flags, uleb128 addr)
+ // teminal node (uleb128 flags, uleb128 addr [uleb128 other])
// byte for child node count
// each child: zero terminated substring, uleb128 node offset
bool updateOffset(uint32_t& offset) {
- uint32_t nodeSize = 1; // byte for length of export info
- if ( fHaveExportInfo )
- nodeSize += uleb128_size(fFlags) + uleb128_size(fAddress);
-
+ uint32_t nodeSize = 1; // length of export info when no export info
+ if ( fHaveExportInfo ) {
+ if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ nodeSize = uleb128_size(fFlags) + uleb128_size(fOther); // ordinal
+ if ( fImportedName != NULL )
+ nodeSize += strlen(fImportedName);
+ ++nodeSize; // trailing zero in imported name
+ }
+ else {
+ nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress);
+ if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
+ nodeSize += uleb128_size(fOther);
+ }
+ // do have export info, overall node size so far is uleb128 of export info + export info
+ nodeSize += uleb128_size(nodeSize);
+ }
// add children
++nodeSize; // byte for count of chidren
for (std::vector<Edge>::iterator it = fChildren.begin(); it != fChildren.end(); ++it) {
void appendToStream(std::vector<uint8_t>& out) {
if ( fHaveExportInfo ) {
- // nodes with export info: size, flags, address
- out.push_back(uleb128_size(fFlags) + uleb128_size(fAddress));
- append_uleb128(fFlags, out);
- append_uleb128(fAddress, out);
+ if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ if ( fImportedName != NULL ) {
+ // nodes with re-export info: size, flags, ordinal, string
+ uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + strlen(fImportedName) + 1;
+ out.push_back(nodeSize);
+ append_uleb128(fFlags, out);
+ append_uleb128(fOther, out);
+ append_string(fImportedName, out);
+ }
+ else {
+ // nodes with re-export info: size, flags, ordinal, empty-string
+ uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fOther) + 1;
+ out.push_back(nodeSize);
+ append_uleb128(fFlags, out);
+ append_uleb128(fOther, out);
+ out.push_back(0);
+ }
+ }
+ else if ( fFlags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) {
+ // nodes with export info: size, flags, address, other
+ uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress) + uleb128_size(fOther);
+ out.push_back(nodeSize);
+ append_uleb128(fFlags, out);
+ append_uleb128(fAddress, out);
+ append_uleb128(fOther, out);
+ }
+ else {
+ // nodes with export info: size, flags, address
+ uint32_t nodeSize = uleb128_size(fFlags) + uleb128_size(fAddress);
+ out.push_back(nodeSize);
+ append_uleb128(fFlags, out);
+ append_uleb128(fAddress, out);
+ }
}
else {
- // no export info
+ // no export info uleb128 of zero is one byte of zero
out.push_back(0);
}
// write number of children
const char* name;
uint64_t address;
uint64_t flags;
+ uint64_t other;
+ const char* importName;
};
-inline void makeTrie(const std::vector<Entry>& input, std::vector<uint8_t>& output)
+
+inline void makeTrie(const std::vector<Entry>& entries, std::vector<uint8_t>& output)
{
Node start(strdup(""));
// make nodes for all exported symbols
- for (std::vector<Entry>::const_iterator it = input.begin(); it != input.end(); ++it) {
- start.addSymbol(it->name, it->address, it->flags);
+ for (std::vector<Entry>::const_iterator it = entries.begin(); it != entries.end(); ++it) {
+ start.addSymbol(it->name, it->address, it->flags, it->other, it->importName);
}
// create vector of nodes
std::vector<Node*> orderedNodes;
- orderedNodes.reserve(input.size()*2);
- for (std::vector<Entry>::const_iterator it = input.begin(); it != input.end(); ++it) {
+ orderedNodes.reserve(entries.size()*2);
+ for (std::vector<Entry>::const_iterator it = entries.begin(); it != entries.end(); ++it) {
start.addOrderedNodes(it->name, orderedNodes);
}
static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
- char* cummulativeString, int curStrOffset, std::vector<EntryWithOffset>& output)
+ char* cummulativeString, int curStrOffset,
+ std::vector<EntryWithOffset>& output)
{
if ( p >= end )
- throwf("malformed trie, node %p outside trie (%p -> %p)", p, start, end);
- const uint8_t terminalSize = *p++;
+ throw "malformed trie, node past end";
+ const uint8_t terminalSize = read_uleb128(p, end);
const uint8_t* children = p + terminalSize;
if ( terminalSize != 0 ) {
EntryWithOffset e;
e.nodeOffset = p-start;
e.entry.name = strdup(cummulativeString);
e.entry.flags = read_uleb128(p, end);
- e.entry.address = read_uleb128(p, end);
+ if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ e.entry.address = 0;
+ e.entry.other = read_uleb128(p, end); // dylib ordinal
+ e.entry.importName = (char*)p;
+ }
+ else {
+ e.entry.address = read_uleb128(p, end);
+ if ( e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
+ e.entry.other = read_uleb128(p, end);
+ else
+ e.entry.other = 0;
+ e.entry.importName = NULL;
+ }
output.push_back(e);
}
const uint8_t childrenCount = *children++;
// empty trie has no entries
if ( start == end )
return;
-
char cummulativeString[4000];
std::vector<EntryWithOffset> entries;
processExportNode(start, start, end, cummulativeString, 0, entries);
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
uint32_t ivar_layout; // const char *
uint32_t ext; // struct objc_class_ext *
- struct objc_class<A> *getIsa(SharedCache<A> *cache) const INLINE { return (struct objc_class<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(isa)); }
- struct objc_method_list<A> *getMethodList(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(methodList)); }
+ struct objc_class<A> *getIsa(SharedCache<A> *cache) const INLINE { return (struct objc_class<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(isa)); }
+ struct objc_method_list<A> *getMethodList(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(methodList)); }
};
template <typename A>
uint32_t size; // uint32_t
uint32_t instance_properties; // struct objc_property_list *
- struct objc_method_list<A> *getInstanceMethods(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(instance_methods)); }
- struct objc_method_list<A> *getClassMethods(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(class_methods)); }
+ struct objc_method_list<A> *getInstanceMethods(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(instance_methods)); }
+ struct objc_method_list<A> *getClassMethods(SharedCache<A> *cache) const INLINE { return (struct objc_method_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(class_methods)); }
};
template <typename A>
uint16_t getClassCount(void) const INLINE { return A::P::E::get16(cls_def_cnt); }
uint16_t getCategoryCount(void) const INLINE { return A::P::E::get16(cat_def_cnt); }
- struct objc_class<A> *getClass(SharedCache<A> *cache, int index) const INLINE { return (struct objc_class<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(defs[index])); }
- struct objc_category<A> *getCategory(SharedCache<A> *cache, int index) const INLINE { return (struct objc_category<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(defs[getClassCount() + index])); }
+ struct objc_class<A> *getClass(SharedCache<A> *cache, int index) const INLINE { return (struct objc_class<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(defs[index])); }
+ struct objc_category<A> *getCategory(SharedCache<A> *cache, int index) const INLINE { return (struct objc_category<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(defs[getClassCount() + index])); }
};
template <typename A>
uint32_t name; // char*
uint32_t symtab; // Symtab
- struct objc_symtab<A> *getSymtab(SharedCache<A> *cache) const INLINE { return (struct objc_symtab<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(symtab)); }
+ struct objc_symtab<A> *getSymtab(SharedCache<A> *cache) const INLINE { return (struct objc_symtab<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(symtab)); }
};
template <typename A>
uint32_t instance_methods; // struct objc_method_description_list *
uint32_t class_methods; // struct objc_method_description_list *
- struct objc_method_description_list<A> *getInstanceMethodDescriptions(SharedCache<A> *cache) const INLINE { return (struct objc_method_description_list<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(instance_methods)); }
- struct objc_method_description_list<A> *getClassMethodDescriptions(SharedCache<A> *cache) const INLINE { return (struct objc_method_description_list<A> *)cache->mappedCacheAddressForAddress(A::P::E::get32(class_methods)); }
+ struct objc_method_description_list<A> *getInstanceMethodDescriptions(SharedCache<A> *cache) const INLINE { return (struct objc_method_description_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(instance_methods)); }
+ struct objc_method_description_list<A> *getClassMethodDescriptions(SharedCache<A> *cache) const INLINE { return (struct objc_method_description_list<A> *)cache->mappedAddressForVMAddress(A::P::E::get32(class_methods)); }
};
header->getSection("__OBJC", "__image_info");
if (imageInfoSection) {
objc_image_info<A> *info = (objc_image_info<A> *)
- cache->mappedCacheAddressForAddress(imageInfoSection->addr());
+ cache->mappedAddressForVMAddress(imageInfoSection->addr());
info->setSelectorsPrebound();
}
}
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* @APPLE_LICENSE_HEADER_END@
*/
+#include <iterator>
+#include <deque>
+
+// iterate an entsize-based list
+// typedef entsize_iterator< A, type_t<A>, type_list_t<A> > type_iterator;
+template <typename A, typename T, typename Tlist>
+struct entsize_iterator {
+ uint32_t entsize;
+ uint32_t index; // keeping track of this saves a divide in operator-
+ T* current;
+
+ typedef std::random_access_iterator_tag iterator_category;
+ typedef T value_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef T& reference;
+
+ entsize_iterator() { }
+
+ entsize_iterator(const Tlist& list, uint32_t start = 0)
+ : entsize(list.getEntsize()), index(start), current(&list.get(start))
+ { }
+
+ const entsize_iterator<A,T,Tlist>& operator += (ptrdiff_t count) {
+ current = (T*)((uint8_t *)current + count*entsize);
+ index += count;
+ return *this;
+ }
+ const entsize_iterator<A,T,Tlist>& operator -= (ptrdiff_t count) {
+ current = (T*)((uint8_t *)current - count*entsize);
+ index -= count;
+ return *this;
+ }
+ const entsize_iterator<A,T,Tlist> operator + (ptrdiff_t count) const {
+ return entsize_iterator(*this) += count;
+ }
+ const entsize_iterator<A,T,Tlist> operator - (ptrdiff_t count) const {
+ return entsize_iterator(*this) -= count;
+ }
+
+ entsize_iterator<A,T,Tlist>& operator ++ () { *this += 1; return *this; }
+ entsize_iterator<A,T,Tlist>& operator -- () { *this -= 1; return *this; }
+ entsize_iterator<A,T,Tlist> operator ++ (int) {
+ entsize_iterator<A,T,Tlist> result(*this); *this += 1; return result;
+ }
+ entsize_iterator<A,T,Tlist> operator -- (int) {
+ entsize_iterator<A,T,Tlist> result(*this); *this -= 1; return result;
+ }
+
+ ptrdiff_t operator - (const entsize_iterator<A,T,Tlist>& rhs) const {
+ return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
+ }
+
+ T& operator * () { return *current; }
+ const T& operator * () const { return *current; }
+ T& operator -> () { return *current; }
+ const T& operator -> () const { return *current; }
+
+ operator T& () const { return *current; }
+
+ bool operator == (const entsize_iterator<A,T,Tlist>& rhs) {
+ return this->current == rhs.current;
+ }
+ bool operator != (const entsize_iterator<A,T,Tlist>& rhs) {
+ return this->current != rhs.current;
+ }
+
+ bool operator < (const entsize_iterator<A,T,Tlist>& rhs) {
+ return this->current < rhs.current;
+ }
+ bool operator > (const entsize_iterator<A,T,Tlist>& rhs) {
+ return this->current > rhs.current;
+ }
+
+
+ static void overwrite(entsize_iterator<A,T,Tlist>& dst, const Tlist* srcList)
+ {
+ entsize_iterator<A,T,Tlist> src;
+ uint32_t ee = srcList->getEntsize();
+ for (src = srcList->begin(); src != srcList->end(); ++src) {
+ memcpy(&*dst, &*src, ee);
+ ++dst;
+ }
+ }
+};
+
+template <typename A> class objc_method_list_t; // forward reference
+
template <typename A>
class objc_method_t {
typename A::P::uint_t name; // SEL
typename A::P::uint_t types; // const char *
typename A::P::uint_t imp; // IMP
-
+ friend class objc_method_list_t<A>;
public:
typename A::P::uint_t getName() const { return A::P::getP(name); }
void setName(typename A::P::uint_t newName) { A::P::setP(name, newName); }
+
+ struct SortBySELAddress :
+ public std::binary_function<const objc_method_t<A>&,
+ const objc_method_t<A>&, bool>
+ {
+ bool operator() (const objc_method_t<A>& lhs,
+ const objc_method_t<A>& rhs)
+ {
+ return lhs.getName() < rhs.getName();
+ }
+ };
};
template <typename A>
uint32_t count;
objc_method_t<A> first;
- uint32_t getEntsize() const { return A::P::E::get32(entsize) & ~(uint32_t)3; }
+ // use newMethodList instead
+ void* operator new (size_t) { return NULL; }
+ void* operator new (size_t, void* buf) { return buf; }
public:
- typename A::P::uint_t getCount() const { return A::P::E::get32(count); }
- objc_method_t<A>& get(typename A::P::uint_t i) const { return *(objc_method_t<A> *)((uint8_t *)&first + i * getEntsize()); }
+ typedef entsize_iterator< A, objc_method_t<A>, objc_method_list_t<A> > method_iterator;
+
+ uint32_t getCount() const { return A::P::E::get32(count); }
+
+ uint32_t getEntsize() const {return A::P::E::get32(entsize)&~(uint32_t)3;}
+
+ objc_method_t<A>& get(uint32_t i) const { return *(objc_method_t<A> *)((uint8_t *)&first + i * getEntsize()); }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount(), getEntsize());
+ }
+
+ static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t<A>)) {
+ return sizeof(objc_method_list_t<A>) - sizeof(objc_method_t<A>) + c*e;
+ }
+
+ method_iterator begin() { return method_iterator(*this, 0); }
+ method_iterator end() { return method_iterator(*this, getCount()); }
+ const method_iterator begin() const { return method_iterator(*this, 0); }
+ const method_iterator end() const { return method_iterator(*this, getCount()); }
void setFixedUp() { A::P::E::set32(entsize, getEntsize() | 3); }
+
+ void getPointers(std::set<void*>& pointersToRemove) {
+ for(method_iterator it = begin(); it != end(); ++it) {
+ objc_method_t<A>& entry = *it;
+ pointersToRemove.insert(&(entry.name));
+ pointersToRemove.insert(&(entry.types));
+ pointersToRemove.insert(&(entry.imp));
+ }
+ }
+
+ static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
+ objc_method_list_t<A>* mlist = (objc_method_list_t<A>*)methodList;
+ for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
+ objc_method_t<A>& entry = *it;
+ pointersToAdd.push_back(&(entry.name));
+ pointersToAdd.push_back(&(entry.types));
+ pointersToAdd.push_back(&(entry.imp));
+ }
+ }
+
+ static objc_method_list_t<A>* newMethodList(size_t newCount, uint32_t newEntsize) {
+ void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+ return new (buf) objc_method_list_t<A>(newCount, newEntsize);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_method_list_t(uint32_t newCount,
+ uint32_t newEntsize = sizeof(objc_method_t<A>))
+ : entsize(newEntsize), count(newCount)
+ { }
};
template <typename A>
uint32_t count;
objc_ivar_t<A> first;
+ // use newIvarList instead
+ void* operator new (size_t) { return NULL; }
+ void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+ typedef entsize_iterator< A, objc_ivar_t<A>, objc_ivar_list_t<A> > ivar_iterator;
+
+ uint32_t getCount() const { return A::P::E::get32(count); }
+
+ uint32_t getEntsize() const { return A::P::E::get32(entsize); }
+
+ objc_ivar_t<A>& get(typename A::P::pint_t i) const { return *(objc_ivar_t<A> *)((uint8_t *)&first + i * A::P::E::get32(entsize)); }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount(), getEntsize());
+ }
+
+ static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t<A>)) {
+ return sizeof(objc_ivar_list_t<A>) - sizeof(objc_ivar_t<A>) + c*e;
+ }
+
+ ivar_iterator begin() { return ivar_iterator(*this, 0); }
+ ivar_iterator end() { return ivar_iterator(*this, getCount()); }
+ const ivar_iterator begin() const { return ivar_iterator(*this, 0); }
+ const ivar_iterator end() const { return ivar_iterator(*this, getCount()); }
+
+ static objc_ivar_list_t<A>* newIvarList(size_t newCount, uint32_t newEntsize) {
+ void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+ return new (buf) objc_ivar_list_t<A>(newCount, newEntsize);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_ivar_list_t(uint32_t newCount,
+ uint32_t newEntsize = sizeof(objc_ivar_t<A>))
+ : entsize(newEntsize), count(newCount)
+ { }
+
+};
+
+
+template <typename A> class objc_property_list_t; // forward
+
+template <typename A>
+class objc_property_t {
+ typename A::P::uint_t name;
+ typename A::P::uint_t attributes;
+ friend class objc_property_list_t<A>;
public:
- objc_ivar_t<A>& getIvarAtIndex(typename A::P::pint_t i) const { return *(objc_ivar_t<A> *)((uint8_t *)&first + i * A::P::E::get32(entsize)); }
+
+ const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
+
+ const char * getAttributes(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(attributes)); }
+};
+
+template <typename A>
+class objc_property_list_t {
+ uint32_t entsize;
+ uint32_t count;
+ objc_property_t<A> first;
+
+ // use newPropertyList instead
+ void* operator new (size_t) { return NULL; }
+ void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+ typedef entsize_iterator< A, objc_property_t<A>, objc_property_list_t<A> > property_iterator;
+
+ uint32_t getCount() const { return A::P::E::get32(count); }
+
+ uint32_t getEntsize() const { return A::P::E::get32(entsize); }
+
+ objc_property_t<A>& get(uint32_t i) const { return *(objc_property_t<A> *)((uint8_t *)&first + i * getEntsize()); }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount(), getEntsize());
+ }
+
+ static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t<A>)) {
+ return sizeof(objc_property_list_t<A>) - sizeof(objc_property_t<A>) + c*e;
+ }
+
+ property_iterator begin() { return property_iterator(*this, 0); }
+ property_iterator end() { return property_iterator(*this, getCount()); }
+ const property_iterator begin() const { return property_iterator(*this, 0); }
+ const property_iterator end() const { return property_iterator(*this, getCount()); }
+
+ void getPointers(std::set<void*>& pointersToRemove) {
+ for(property_iterator it = begin(); it != end(); ++it) {
+ objc_property_t<A>& entry = *it;
+ pointersToRemove.insert(&(entry.name));
+ pointersToRemove.insert(&(entry.attributes));
+ }
+ }
+
+ static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
+ objc_property_list_t<A>* plist = (objc_property_list_t<A>*)propertyList;
+ for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
+ objc_property_t<A>& entry = *it;
+ pointersToAdd.push_back(&(entry.name));
+ pointersToAdd.push_back(&(entry.attributes));
+ }
+ }
+
+ static objc_property_list_t<A>* newPropertyList(size_t newCount, uint32_t newEntsize) {
+ void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
+ return new (buf) objc_property_list_t<A>(newCount, newEntsize);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_property_list_t(uint32_t newCount,
+ uint32_t newEntsize = sizeof(objc_property_t<A>))
+ : entsize(newEntsize), count(newCount)
+ { }
+
};
template <typename A>
typename A::P::uint_t instanceProperties;
public:
- objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(instanceMethods)); }
+ objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
- objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(classMethods)); }
+ objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
- objc_method_list_t<A> *getOptionalInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(optionalInstanceMethods)); }
+ objc_method_list_t<A> *getOptionalInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalInstanceMethods)); }
- objc_method_list_t<A> *getOptionalClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(optionalClassMethods)); }
+ objc_method_list_t<A> *getOptionalClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); }
};
template <typename A>
class objc_protocol_list_t {
- typename A::P::uint_t count;
- typename A::P::uint_t list[0];
-};
+ typedef typename A::P::uint_t pint_t;
+ pint_t count;
+ pint_t list[0];
+
+ // use newProtocolList instead
+ void* operator new (size_t) { return NULL; }
+ void* operator new (size_t, void* buf) { return buf; }
+
+public:
+
+ pint_t getCount() const { return A::P::getP(count); }
+
+ objc_protocol_t<A>* get(SharedCache<A>* cache, pint_t i) {
+ return (objc_protocol_t<A>*)cache->mappedAddressForVMAddress(A::P::getP(list[i]));
+ }
+
+ void overwrite(pint_t& index, const objc_protocol_list_t<A>* src) {
+ pint_t srcCount = src->getCount();
+ memcpy(list+index, src->list, srcCount * sizeof(pint_t));
+ index += srcCount;
+ }
+
+ uint32_t byteSize() const {
+ return byteSizeForCount(getCount());
+ }
+ static uint32_t byteSizeForCount(pint_t c) {
+ return sizeof(objc_protocol_list_t<A>) + c*sizeof(pint_t);
+ }
-template < typename P, typename E >
-struct pad { };
+ void getPointers(std::set<void*>& pointersToRemove) {
+ for(int i=0 ; i < count; ++i) {
+ pointersToRemove.insert(&list[i]);
+ }
+ }
+
+ static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
+ objc_protocol_list_t<A>* plist = (objc_protocol_list_t<A>*)protocolList;
+ for(int i=0 ; i < plist->count; ++i) {
+ pointersToAdd.push_back(&plist->list[i]);
+ }
+ }
+
+ static objc_protocol_list_t<A>* newProtocolList(pint_t newCount) {
+ void *buf = ::calloc(byteSizeForCount(newCount), 1);
+ return new (buf) objc_protocol_list_t<A>(newCount);
+ }
+
+ void operator delete(void * p) {
+ ::free(p);
+ }
+
+ objc_protocol_list_t(uint32_t newCount) : count(newCount) { }
+
+};
-template < typename E >
-struct pad< Pointer64<E>, E > { uint32_t unused; };
template <typename A>
class objc_class_data_t {
uint32_t flags;
uint32_t instanceStart;
- uint32_t instanceSize;
- pad<typename A::P, typename A::P::E> reserved; // ILP32=0 bytes, LP64=4 bytes
-
+ // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
+ // on 64-bit archs, but no padding on 32-bit archs.
+ // This union is a way to model that.
+ union {
+ uint32_t instanceSize;
+ typename A::P::uint_t pad;
+ } instanceSize;
typename A::P::uint_t ivarLayout;
typename A::P::uint_t name;
typename A::P::uint_t baseMethods;
typename A::P::uint_t baseProperties;
public:
- objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(baseMethods)); }
+ objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseMethods)); }
+
+ objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProtocols)); }
+
+ objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProperties)); }
+
+ const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
+
+ void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
+ A::P::setP(baseMethods, cache->VMAddressForMappedAddress(mlist));
+ }
+
+ void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
+ A::P::setP(baseProtocols, cache->VMAddressForMappedAddress(protolist));
+ }
+
+ void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
+ A::P::setP(baseProperties, cache->VMAddressForMappedAddress(proplist));
+ }
+
+ void addMethodListPointer(std::vector<void*>& pointersToAdd) {
+ pointersToAdd.push_back(&this->baseMethods);
+ }
+
+ void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
+ pointersToAdd.push_back(&this->baseProperties);
+ }
+
+ void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
+ pointersToAdd.push_back(&this->baseProtocols);
+ }
};
template <typename A>
typename A::P::uint_t data;
public:
- objc_class_t<A> *getIsa(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(isa)); }
+ objc_class_t<A> *getIsa(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(isa)); }
- objc_class_data_t<A> *getData(SharedCache<A>* cache) const { return (objc_class_data_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(data)); }
+ objc_class_data_t<A> *getData(SharedCache<A>* cache) const { return (objc_class_data_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(data)); }
objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return getData(cache)->getMethodList(cache); }
+
+ objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return getData(cache)->getProtocolList(cache); }
+
+ objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return getData(cache)->getPropertyList(cache); }
+
+ const char * getName(SharedCache<A>* cache) const {
+ return getData(cache)->getName(cache);
+ }
+
+ void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
+ getData(cache)->setMethodList(cache, mlist);
+ }
+
+ void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
+ getData(cache)->setProtocolList(cache, protolist);
+ }
+
+ void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
+ getData(cache)->setPropertyList(cache, proplist);
+ }
+
+ void addMethodListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
+ getData(cache)->addMethodListPointer(pointersToAdd);
+ }
+
+ void addPropertyListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
+ getData(cache)->addPropertyListPointer(pointersToAdd);
+ }
+
+ void addProtocolListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
+ getData(cache)->addProtocolListPointer(pointersToAdd);
+ }
+
};
typename A::P::uint_t instanceProperties;
public:
- objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(instanceMethods)); }
- objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedCacheAddressForAddress(A::P::getP(classMethods)); }
+ const char * getName(SharedCache<A> *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
+
+ objc_class_t<A> *getClass(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(cls)); }
+
+ objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
+
+ objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
+
+ objc_protocol_list_t<A> *getProtocols(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); }
+
+ objc_property_list_t<A> *getInstanceProperties(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); }
+
+ void getPointers(std::set<void*>& pointersToRemove) {
+ pointersToRemove.insert(&name);
+ pointersToRemove.insert(&cls);
+ pointersToRemove.insert(&instanceMethods);
+ pointersToRemove.insert(&classMethods);
+ pointersToRemove.insert(&protocols);
+ pointersToRemove.insert(&instanceProperties);
+ }
+
+
};
template <typename A>
void setName(typename A::P::uint_t newName) { A::P::setP(sel, newName); }
};
+
+// Call visitor.visitMethodList(mlist) on every method list in a header.
template <typename A, typename V>
-class SelectorUpdater {
+class MethodListWalker {
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
- static void visitMethodList(objc_method_list_t<A> *mlist, V& visitor)
- {
- for (pint_t m = 0; m < mlist->getCount(); m++) {
- pint_t oldValue = mlist->get(m).getName();
- pint_t newValue = visitor.visit(oldValue);
- mlist->get(m).setName(newValue);
- }
- mlist->setFixedUp();
- }
+ V& mVisitor;
-public:
+public:
+
+ MethodListWalker(V& visitor) : mVisitor(visitor) { }
- static void update(SharedCache<A>* cache, const macho_header<P>* header,
- V& visitor)
- {
+ void walk(SharedCache<A>* cache, const macho_header<P>* header)
+ {
// Method lists in classes
PointerSection<A, objc_class_t<A> *>
classes(cache, header, "__DATA", "__objc_classlist");
+
for (pint_t i = 0; i < classes.count(); i++) {
objc_class_t<A> *cls = classes.get(i);
objc_method_list_t<A> *mlist;
if ((mlist = cls->getMethodList(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
}
objc_category_t<A> *cat = cats.get(i);
objc_method_list_t<A> *mlist;
if ((mlist = cat->getInstanceMethods(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
if ((mlist = cat->getClassMethods(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
}
objc_protocol_t<A> *proto = protocols.get(i);
objc_method_list_t<A> *mlist;
if ((mlist = proto->getInstanceMethods(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
if ((mlist = proto->getClassMethods(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
if ((mlist = proto->getOptionalInstanceMethods(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
if ((mlist = proto->getOptionalClassMethods(cache))) {
- visitMethodList(mlist, visitor);
+ mVisitor.visitMethodList(mlist);
}
}
+ }
+};
+
+// Update selector references. The visitor performs recording and uniquing.
+template <typename A, typename V>
+class SelectorOptimizer {
+
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+
+ V& mVisitor;
+
+ friend class MethodListWalker< A, SelectorOptimizer<A,V> >;
+ void visitMethodList(objc_method_list_t<A> *mlist)
+ {
+ // Gather selectors. Update method names.
+ for (pint_t m = 0; m < mlist->getCount(); m++) {
+ pint_t oldValue = mlist->get(m).getName();
+ pint_t newValue = mVisitor.visit(oldValue);
+ mlist->get(m).setName(newValue);
+ }
+ // Do not setFixedUp: the methods are not yet sorted.
+ }
+
+public:
+
+ SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
+
+ void optimize(SharedCache<A>* cache, const macho_header<P>* header)
+ {
+ // method lists of all kinds
+ MethodListWalker< A, SelectorOptimizer<A,V> > mw(*this);
+ mw.walk(cache, header);
+
// @selector references
PointerSection<A, const char *>
selrefs(cache, header, "__DATA", "__objc_selrefs");
for (pint_t i = 0; i < selrefs.count(); i++) {
pint_t oldValue = selrefs.getUnmapped(i);
- pint_t newValue = visitor.visit(oldValue);
+ pint_t newValue = mVisitor.visit(oldValue);
selrefs.set(i, newValue);
}
for (pint_t i = 0; i < msgrefs.count(); i++) {
objc_message_ref_t<A>& msg = msgrefs.get(i);
pint_t oldValue = msg.getName();
- pint_t newValue = visitor.visit(oldValue);
+ pint_t newValue = mVisitor.visit(oldValue);
msg.setName(newValue);
}
header->getSection("__DATA", "__objc_imageinfo");
if (imageInfoSection) {
objc_image_info<A> *info = (objc_image_info<A> *)
- cache->mappedCacheAddressForAddress(imageInfoSection->addr());
+ cache->mappedAddressForVMAddress(imageInfoSection->addr());
info->setSelectorsPrebound();
}
}
};
+
+
+// Sort methods in place by selector.
+template <typename A>
+class MethodListSorter {
+
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+
+ friend class MethodListWalker<A, MethodListSorter<A> >;
+ void visitMethodList(objc_method_list_t<A> *mlist)
+ {
+ typename objc_method_t<A>::SortBySELAddress sorter;
+ std::stable_sort(mlist->begin(), mlist->end(), sorter);
+ mlist->setFixedUp();
+ }
+
+public:
+
+ void optimize(SharedCache<A>* cache, macho_header<P>* header)
+ {
+ MethodListWalker<A, MethodListSorter<A> > mw(*this);
+ mw.walk(cache, header);
+ }
+};
+
+
+// Attach categories to classes in the same framework.
+// Merge method and protocol and property lists.
+template <typename A>
+class CategoryAttacher {
+
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
+
+ uint8_t *mBytes;
+ ssize_t mBytesFree;
+ ssize_t mBytesUsed;
+
+ size_t mCategoriesAttached;
+
+ bool segmentContainsPointer(SharedCache<A>* cache,
+ const macho_segment_command<P>* seg, void *ptr)
+ {
+ if (!seg) return false;
+ void *start = (void*)
+ cache->mappedAddressForVMAddress(seg->vmaddr());
+ void *end = (uint8_t *)start + seg->filesize();
+ return (ptr >= start && ptr < end);
+ }
+
+ bool headerContainsPointer(SharedCache<A>* cache,
+ macho_header<P>* header, void *ptr)
+ {
+ return
+ segmentContainsPointer(cache, header->getSegment("__DATA"), ptr) ||
+ segmentContainsPointer(cache, header->getSegment("__TEXT"), ptr) ||
+ segmentContainsPointer(cache, header->getSegment("__OBJC"), ptr);
+ }
+
+ struct pointer_hash {
+ size_t operator () (void* ptr) const {
+ return __gnu_cxx::hash<long>()((long)ptr);
+ }
+ };
+
+ typedef std::deque<objc_category_t<A>*> CategoryList;
+ typedef std::vector<uint64_t> CategoryRefs;
+
+ struct ClassChanges {
+ CategoryList categories;
+ CategoryRefs catrefs;
+
+ objc_method_list_t<A>* instanceMethods;
+ objc_method_list_t<A>* classMethods;
+ objc_protocol_list_t<A>* protocols;
+ objc_property_list_t<A>* instanceProperties;
+
+ ClassChanges()
+ : instanceMethods(NULL), classMethods(NULL),
+ protocols(NULL), instanceProperties(NULL)
+ { }
+
+ ~ClassChanges() {
+ if (instanceMethods) delete instanceMethods;
+ if (classMethods) delete classMethods;
+ if (protocols) delete protocols;
+ if (instanceProperties) delete instanceProperties;
+ }
+ };
+
+ typedef __gnu_cxx::hash_map<objc_class_t<A>*, ClassChanges, pointer_hash> ClassMap;
+
+ class RangeArray {
+ typedef std::pair<uint8_t*,uint32_t> Range;
+ std::deque<Range> ranges;
+
+ class SizeFits {
+ private:
+ uint32_t mSize;
+ public:
+ SizeFits(uint32_t size) : mSize(size) { }
+ bool operator() (const Range& r) {
+ return r.second >= mSize;
+ }
+ };
+
+ struct AddressComp {
+ bool operator() (const Range& lhs, const Range& rhs) {
+ return (lhs.first < rhs.first);
+ }
+ };
+ public:
+ RangeArray() { }
+ void add(void* p, uint32_t size) {
+ add(Range((uint8_t *)p, size));
+ }
+ void add(const Range& r) {
+ // find insertion point
+ std::deque<Range>::iterator iter;
+ iter = upper_bound(ranges.begin(), ranges.end(), r, AddressComp());
+ // coalesce
+ // fixme doesn't fully coalesce if new range exactly fills a gap
+ if (iter != ranges.begin()) {
+ std::deque<Range>::iterator prev = iter - 1;
+ if ((*prev).first + (*prev).second == r.first) {
+ (*prev).second += r.second;
+ return;
+ }
+ }
+ if (iter != ranges.end() && iter+1 != ranges.end()) {
+ std::deque<Range>::iterator next = iter + 1;
+ if (r.first + r.second == (*next).first) {
+ (*next).second += r.second;
+ (*next).first = r.first;
+ return;
+ }
+ }
+ ranges.insert(iter, r);
+ }
+
+ uint8_t* remove(uint32_t size) {
+ // first-fit search
+ // this saves 50-75% of space overhead;
+ // a better algorithm might do better
+
+ std::deque<Range>::iterator iter;
+ iter = find_if(ranges.begin(), ranges.end(), SizeFits(size));
+ if (iter == ranges.end()) {
+ return NULL;
+ }
+
+ Range& found = *iter;
+ uint8_t *result = found.first;
+ if (found.second > size) {
+ // keep leftovers
+ found.first += size;
+ found.second -= size;
+ } else {
+ ranges.erase(iter);
+ }
+
+ return result;
+ }
+ };
+
+ void copyMethods(typename objc_method_list_t<A>::method_iterator& dst,
+ const objc_method_list_t<A>* srcList)
+ {
+ objc_method_list_t<A>::method_iterator::
+ overwrite(dst, srcList);
+ }
+
+ void copyProperties(typename objc_property_list_t<A>::property_iterator& dst,
+ const objc_property_list_t<A>* srcList)
+ {
+ objc_property_list_t<A>::property_iterator::
+ overwrite(dst, srcList);
+ }
+
+ void copyProtocols(objc_protocol_list_t<A>* dst, pint_t& dstIndex,
+ const objc_protocol_list_t<A>* src)
+ {
+ dst->overwrite(dstIndex, src);
+ }
+
+ class InSet
+ {
+ public:
+ InSet(std::set<void*>& deadPointers) : _deadPointers(deadPointers) {}
+
+ bool operator()(void* ptr) const {
+ return ( _deadPointers.count(ptr) != 0 );
+ }
+
+ private:
+ std::set<void*>& _deadPointers;
+ };
+
+public:
+
+ CategoryAttacher(uint8_t *bytes, ssize_t bytesFree)
+ : mBytes(bytes), mBytesFree(bytesFree)
+ , mBytesUsed(0), mCategoriesAttached(0)
+ { }
+
+ size_t count() const { return mCategoriesAttached; }
+
+ const char *optimize(SharedCache<A>* cache, macho_header<P>* header, std::vector<void*>& pointersInData)
+ {
+ // Build class=>cateories mapping.
+ // Disregard target classes that aren't in this binary.
+
+ ClassMap map;
+
+ PointerSection<A, objc_category_t<A> *>
+ nlcatsect(cache, header, "__DATA", "__objc_nlcatlist");
+ PointerSection<A, objc_category_t<A> *>
+ catsect(cache, header, "__DATA", "__objc_catlist");
+ for (pint_t i = 0; i < catsect.count(); i++) {
+ objc_category_t<A> *cat = catsect.get(i);
+ objc_class_t<A> *cls = cat->getClass(cache);
+ if (!cls) continue;
+ if (!headerContainsPointer(cache, header, cls)) continue;
+ if ( nlcatsect.count() !=0 ) {
+ // don't optimize categories also in __objc_nlcatlist
+ bool alsoInNlcatlist = false;
+ for (pint_t nli = 0; nli < nlcatsect.count(); nli++) {
+ if ( nlcatsect.get(nli) == cat ) {
+ //fprintf(stderr, "skipping cat in __objc_nlcatlist for mh=%p\n", header);
+ alsoInNlcatlist = true;
+ break;
+ }
+ }
+ if ( alsoInNlcatlist )
+ continue;
+ }
+
+ // The LAST category found is the FIRST to be processed later.
+ map[cls].categories.push_front(cat);
+
+ // We don't care about the category reference order.
+ map[cls].catrefs.push_back(i);
+ }
+
+ if (map.size() == 0) {
+ // No attachable categories.
+ return NULL;
+ }
+
+ // Process each class.
+ // Each class is all-or-nothing: either all of its categories
+ // are attached successfully, or none of them are. This preserves
+ // cache validity if we run out of space for more reallocations.
+
+ // unusedMemory stores memory ranges evacuated by now-unused metadata.
+ // It is available for re-use by other newly-added metadata.
+ // fixme could packing algorithm be improved?
+ RangeArray unusedMemory;
+
+ ssize_t reserve = 0;
+
+ // First: build new aggregated lists on the heap.
+ // Require enough space in mBytes for all of it.
+
+ std::set<void*> pointersToRemove;
+ for (typename ClassMap::iterator i = map.begin();
+ i != map.end();
+ ++i)
+ {
+ objc_class_t<A>* cls = i->first;
+ objc_class_t<A>* meta = cls->getIsa(cache);
+ ClassChanges& changes = i->second;
+ CategoryList& cats = changes.categories;
+
+ // Count memory needed for all categories on this class.
+
+ uint32_t methodEntsize = 0;
+ uint32_t propertyEntsize = 0;
+ objc_method_list_t<A>* mlist;
+ objc_property_list_t<A>* proplist;
+ objc_protocol_list_t<A>* protolist;
+ uint32_t instanceMethodsCount = 0;
+ uint32_t classMethodsCount = 0;
+ uint32_t instancePropertyCount = 0;
+ pint_t protocolCount = 0;
+ bool addedInstanceMethods = false;
+ bool addedClassMethods = false;
+ bool addedInstanceProperties = false;
+ bool addedProtocols = false;
+
+ mlist = cls->getMethodList(cache);
+ if (mlist) {
+ instanceMethodsCount = mlist->getCount();
+ methodEntsize =
+ std::max(methodEntsize, mlist->getEntsize());
+ }
+
+ mlist = meta->getMethodList(cache);
+ if (mlist) {
+ classMethodsCount = mlist->getCount();
+ methodEntsize =
+ std::max(methodEntsize, mlist->getEntsize());
+ }
+
+ proplist = cls->getPropertyList(cache);
+ if (proplist) {
+ instancePropertyCount = proplist->getCount();
+ propertyEntsize =
+ std::max(propertyEntsize, proplist->getEntsize());
+ }
+
+ protolist = cls->getProtocolList(cache);
+ if (protolist) {
+ protocolCount = protolist->getCount();
+ }
+
+ typename CategoryList::iterator j;
+ for (j = cats.begin(); j != cats.end(); ++j) {
+ objc_category_t<A>* cat = *j;
+
+ mlist = cat->getInstanceMethods(cache);
+ if (mlist && mlist->getCount() > 0) {
+ addedInstanceMethods = true;
+ instanceMethodsCount += mlist->getCount();
+ methodEntsize =
+ std::max(methodEntsize, mlist->getEntsize());
+ }
+
+ mlist = cat->getClassMethods(cache);
+ if (mlist && mlist->getCount() > 0) {
+ addedClassMethods = true;
+ classMethodsCount += mlist->getCount();
+ methodEntsize =
+ std::max(methodEntsize, mlist->getEntsize());
+ }
+
+ proplist = cat->getInstanceProperties(cache);
+ if (proplist && proplist->getCount() > 0) {
+ addedInstanceProperties = true;
+ instancePropertyCount += proplist->getCount();
+ propertyEntsize =
+ std::max(propertyEntsize, proplist->getEntsize());
+ }
+
+ protolist = cat->getProtocols(cache);
+ if (protolist && protolist->getCount() > 0) {
+ addedProtocols = true;
+ protocolCount += protolist->getCount();
+ }
+ }
+
+ // Allocate memory for aggregated lists.
+ // Reserve the same amount of space from mBytes.
+
+ if (addedInstanceMethods) {
+ changes.instanceMethods = objc_method_list_t<A>::newMethodList(instanceMethodsCount, methodEntsize);
+ reserve = P::round_up(reserve + changes.instanceMethods->byteSize());
+ }
+ if (addedClassMethods) {
+ changes.classMethods = objc_method_list_t<A>::newMethodList(classMethodsCount, methodEntsize);
+ reserve = P::round_up(reserve + changes.classMethods->byteSize());
+ }
+ if (addedInstanceProperties) {
+ changes.instanceProperties = objc_property_list_t<A>::newPropertyList(instancePropertyCount, propertyEntsize);
+ reserve = P::round_up(reserve + changes.instanceProperties->byteSize());
+ }
+ if (addedProtocols) {
+ changes.protocols = objc_protocol_list_t<A>::newProtocolList(protocolCount);
+ reserve = P::round_up(reserve + changes.protocols->byteSize());
+ }
+
+ // Merge. The LAST category's contents ends up FIRST in each list.
+ // The aggregated lists are not sorted; a future pass does that.
+
+ typename objc_method_list_t<A>::method_iterator newInstanceMethods;
+ typename objc_method_list_t<A>::method_iterator newClassMethods;
+ typename objc_property_list_t<A>::property_iterator newInstanceProperties;
+ pint_t newProtocolIndex;
+
+ if (addedInstanceMethods) {
+ newInstanceMethods = changes.instanceMethods->begin();
+ }
+ if (addedClassMethods) {
+ newClassMethods = changes.classMethods->begin();
+ }
+ if (addedInstanceProperties) {
+ newInstanceProperties = changes.instanceProperties->begin();
+ }
+ if (addedProtocols) {
+ newProtocolIndex = 0;
+ }
+
+ for (j = cats.begin(); j != cats.end(); ++j) {
+ objc_category_t<A>* cat = *j;
+
+ mlist = cat->getInstanceMethods(cache);
+ if (mlist) {
+ copyMethods(newInstanceMethods, mlist);
+ mlist->getPointers(pointersToRemove);
+ unusedMemory.add(mlist, mlist->byteSize());
+ }
+
+ mlist = cat->getClassMethods(cache);
+ if (mlist) {
+ copyMethods(newClassMethods, mlist);
+ mlist->getPointers(pointersToRemove);
+ unusedMemory.add(mlist, mlist->byteSize());
+ }
+
+ proplist = cat->getInstanceProperties(cache);
+ if (proplist) {
+ copyProperties(newInstanceProperties, proplist);
+ proplist->getPointers(pointersToRemove);
+ unusedMemory.add(proplist, proplist->byteSize());
+ }
+
+ protolist = cat->getProtocols(cache);
+ if (protolist) {
+ copyProtocols(changes.protocols, newProtocolIndex, protolist);
+ protolist->getPointers(pointersToRemove);
+ unusedMemory.add(protolist, protolist->byteSize());
+ }
+
+ cat->getPointers(pointersToRemove);
+ unusedMemory.add(cat, sizeof(*cat));
+ }
+
+ if (addedInstanceMethods && (mlist = cls->getMethodList(cache))) {
+ copyMethods(newInstanceMethods, mlist);
+ mlist->getPointers(pointersToRemove);
+ unusedMemory.add(mlist, mlist->byteSize());
+ }
+ if (addedClassMethods && (mlist = meta->getMethodList(cache))) {
+ copyMethods(newClassMethods, mlist);
+ mlist->getPointers(pointersToRemove);
+ unusedMemory.add(mlist, mlist->byteSize());
+ }
+ if (addedInstanceProperties && (proplist = cls->getPropertyList(cache))) {
+ copyProperties(newInstanceProperties, proplist);
+ proplist->getPointers(pointersToRemove);
+ unusedMemory.add(proplist, proplist->byteSize());
+ }
+ if (addedProtocols && (protolist = cls->getProtocolList(cache))) {
+ copyProtocols(changes.protocols, newProtocolIndex, protolist);
+ protolist->getPointers(pointersToRemove);
+ unusedMemory.add(protolist, protolist->byteSize());
+ }
+ }
+
+ if (reserve > mBytesFree) {
+ return "insufficient space for category data (metadata not optimized)";
+ }
+
+ // update cache slide info and remove areas now longer containing pointers
+ //fprintf(stderr, "found %lu pointers in objc structures being moved\n", pointersToRemove.size());
+ pointersInData.erase(std::remove_if(pointersInData.begin(), pointersInData.end(), InSet(pointersToRemove)), pointersInData.end());
+
+
+ // All lists are now built.
+ // mBytes is big enough to hold everything if necessary.
+ // Everything in unusedMemory is now available for re-use.
+ // The original metadata is still untouched.
+
+ // Second: write lists into mBytes and unusedMemory,
+ // then disconnect categories.
+
+ for (typename ClassMap::iterator i = map.begin();
+ i != map.end();
+ ++i)
+ {
+ objc_class_t<A>* cls = i->first;
+ objc_class_t<A>* meta = cls->getIsa(cache);
+ ClassChanges& changes = i->second;
+
+ // Write lists.
+
+ if (changes.instanceMethods) {
+ uint8_t *bytes;
+ uint32_t size = changes.instanceMethods->byteSize();
+ if (! (bytes = unusedMemory.remove(size))) {
+ bytes = mBytes + mBytesUsed;
+ mBytesFree -= size;
+ mBytesUsed += size;
+ }
+ memcpy(bytes, changes.instanceMethods, size);
+ objc_method_list_t<A>::addPointers(bytes, pointersInData);
+ cls->setMethodList(cache, (objc_method_list_t<A> *)bytes);
+ cls->addMethodListPointer(cache, pointersInData);
+ }
+
+ if (changes.classMethods) {
+ uint8_t *bytes;
+ uint32_t size = changes.classMethods->byteSize();
+ if (! (bytes = unusedMemory.remove(size))) {
+ bytes = mBytes + mBytesUsed;
+ mBytesFree -= size;
+ mBytesUsed += size;
+ }
+ memcpy(bytes, changes.classMethods, size);
+ objc_method_list_t<A>::addPointers(bytes, pointersInData);
+ meta->setMethodList(cache, (objc_method_list_t<A> *)bytes);
+ meta->addMethodListPointer(cache, pointersInData);
+ }
+
+ if (changes.instanceProperties) {
+ uint8_t *bytes;
+ uint32_t size = changes.instanceProperties->byteSize();
+ if (! (bytes = unusedMemory.remove(size))) {
+ bytes = mBytes + mBytesUsed;
+ mBytesFree -= size;
+ mBytesUsed += size;
+ }
+ memcpy(bytes, changes.instanceProperties, size);
+ objc_property_list_t<A>::addPointers(bytes, pointersInData);
+ cls->setPropertyList(cache, (objc_property_list_t<A> *)bytes);
+ cls->addPropertyListPointer(cache, pointersInData);
+ }
+
+ if (changes.protocols) {
+ uint8_t *bytes;
+ uint32_t size = changes.protocols->byteSize();
+ if (! (bytes = unusedMemory.remove(size))) {
+ bytes = mBytes + mBytesUsed;
+ mBytesFree -= size;
+ mBytesUsed += size;
+ }
+ memcpy(bytes, changes.protocols, size);
+ cls->setProtocolList(cache, (objc_protocol_list_t<A> *)bytes);
+ objc_protocol_list_t<A>::addPointers(bytes, pointersInData);
+ cls->addProtocolListPointer(cache, pointersInData);
+ meta->setProtocolList(cache, (objc_protocol_list_t<A> *)bytes);
+ meta->addProtocolListPointer(cache, pointersInData);
+ }
+
+ // Disavow all knowledge of the categories.
+
+ for (typename CategoryRefs::iterator j = changes.catrefs.begin();
+ j != changes.catrefs.end();
+ ++j)
+ {
+ catsect.set(*j, 0);
+ }
+
+ mCategoriesAttached += changes.categories.size();
+ }
+
+ catsect.removeNulls();
+
+ return NULL;
+ }
+
+ ssize_t bytesUsed() { return mBytesUsed; }
+};
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syslimits.h>
+#include <libkern/OSByteOrder.h>
+#include <mach-o/fat.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
+#include <Availability.h>
+
+#define NO_ULEB
+#include "Architectures.hpp"
+#include "MachOFileAbstraction.hpp"
+
+#include "dsc_iterator.h"
+#include "dsc_extractor.h"
+
+#include <vector>
+#include <map>
+#include <ext/hash_map>
+#include <algorithm>
+#include <dispatch/dispatch.h>
+
+struct seg_info
+{
+ seg_info(const char* n, uint64_t o, uint64_t s)
+ : segName(n), offset(o), sizem(s) { }
+ const char* segName;
+ uint64_t offset;
+ uint64_t sizem;
+};
+
+class CStringEquals {
+public:
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+};
+typedef __gnu_cxx::hash_map<const char*, std::vector<seg_info>, __gnu_cxx::hash<const char*>, CStringEquals> NameToSegments;
+
+
+template <typename A>
+int optimize_linkedit(macho_header<typename A::P>* mh, const void* mapped_cache, uint64_t* newSize)
+{
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ typedef typename A::P::uint_t pint_t;
+
+ // update header flags
+ mh->set_flags(mh->flags() & 0x7FFFFFFF); // remove in-cache bit
+
+ // update load commands
+ uint64_t cumulativeFileSize = 0;
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
+ const uint32_t cmd_count = mh->ncmds();
+ const macho_load_command<P>* cmd = cmds;
+ macho_segment_command<P>* linkEditSegCmd = NULL;
+ macho_symtab_command<P>* symtab = NULL;
+ macho_dysymtab_command<P>* dynamicSymTab = NULL;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
+ // update segment/section file offsets
+ macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
+ segCmd->set_fileoff(cumulativeFileSize);
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( sect->offset() != 0 )
+ sect->set_offset(cumulativeFileSize+sect->addr()-segCmd->vmaddr());
+ }
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ linkEditSegCmd = segCmd;
+ }
+ cumulativeFileSize += segCmd->filesize();
+ }
+ else if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) {
+ // zero out all dyld info
+ macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ dyldInfo->set_rebase_off(0);
+ dyldInfo->set_rebase_size(0);
+ dyldInfo->set_bind_off(0);
+ dyldInfo->set_bind_size(0);
+ dyldInfo->set_weak_bind_off(0);
+ dyldInfo->set_weak_bind_size(0);
+ dyldInfo->set_lazy_bind_off(0);
+ dyldInfo->set_lazy_bind_size(0);
+ dyldInfo->set_export_off(0);
+ dyldInfo->set_export_size(0);
+ }
+ else if ( cmd->cmd() == LC_SYMTAB ) {
+ symtab = (macho_symtab_command<P>*)cmd;
+ }
+ else if ( cmd->cmd() == LC_DYSYMTAB ) {
+ dynamicSymTab = (macho_dysymtab_command<P>*)cmd;
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+
+ // rebuild symbol table
+ if ( linkEditSegCmd == NULL ) {
+ fprintf(stderr, "__LINKEDIT not found\n");
+ return -1;
+ }
+ if ( symtab == NULL ) {
+ fprintf(stderr, "LC_SYMTAB not found\n");
+ return -1;
+ }
+ if ( dynamicSymTab == NULL ) {
+ fprintf(stderr, "LC_DYSYMTAB not found\n");
+ return -1;
+ }
+ // copy symbol entries and strings from original cache file to new mapped dylib file
+ const uint32_t newSymTabOffset = linkEditSegCmd->fileoff();
+ const uint32_t newIndSymTabOffset = newSymTabOffset + symtab->nsyms()*sizeof(macho_nlist<P>);
+ const uint32_t newStringPoolOffset = newIndSymTabOffset + dynamicSymTab->nindirectsyms()*sizeof(uint32_t);
+ macho_nlist<P>* const newSymTabStart = (macho_nlist<P>*)(((uint8_t*)mh) + newSymTabOffset);
+ char* const newStringPoolStart = (char*)mh + newStringPoolOffset;
+ uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset);
+ const uint32_t* mergedIndSymTab = (uint32_t*)((char*)mapped_cache + dynamicSymTab->indirectsymoff());
+ const macho_nlist<P>* const mergedSymTabStart = (macho_nlist<P>*)(((uint8_t*)mapped_cache) + symtab->symoff());
+ const macho_nlist<P>* const mergedSymTabend = &mergedSymTabStart[symtab->nsyms()];
+ const char* mergedStringPoolStart = (char*)mapped_cache + symtab->stroff();
+ macho_nlist<P>* t = newSymTabStart;
+ int poolOffset = 0;
+ newStringPoolStart[poolOffset++] = '\0'; // first pool entry is always empty string
+ for (const macho_nlist<P>* s = mergedSymTabStart; s != mergedSymTabend; ++s) {
+ *t = *s;
+ t->set_n_strx(poolOffset);
+ strcpy(&newStringPoolStart[poolOffset], &mergedStringPoolStart[s->n_strx()]);
+ poolOffset += (strlen(&newStringPoolStart[poolOffset]) + 1);
+ ++t;
+ }
+ // pointer align string pool size
+ while ( (poolOffset % sizeof(pint_t)) != 0 )
+ ++poolOffset;
+ // copy indirect symbol table
+ memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t));
+
+ // update load commands
+ symtab->set_symoff(newSymTabOffset);
+ symtab->set_stroff(newStringPoolOffset);
+ symtab->set_strsize(poolOffset);
+ dynamicSymTab->set_extreloff(0);
+ dynamicSymTab->set_nextrel(0);
+ dynamicSymTab->set_locreloff(0);
+ dynamicSymTab->set_nlocrel(0);
+ dynamicSymTab->set_indirectsymoff(newIndSymTabOffset);
+ linkEditSegCmd->set_filesize(symtab->stroff()+symtab->strsize() - linkEditSegCmd->fileoff());
+ linkEditSegCmd->set_vmsize( (linkEditSegCmd->filesize()+4095) & (-4096) );
+
+ // return new size
+ *newSize = (symtab->stroff()+symtab->strsize()+4095) & (-4096);
+
+ return 0;
+}
+
+
+
+static void make_dirs(const char* file_path)
+{
+ //printf("make_dirs(%s)\n", file_path);
+ char dirs[strlen(file_path)+1];
+ strcpy(dirs, file_path);
+ char* lastSlash = strrchr(dirs, '/');
+ if ( lastSlash == NULL )
+ return;
+ lastSlash[1] = '\0';
+ struct stat stat_buf;
+ if ( stat(dirs, &stat_buf) != 0 ) {
+ const char* afterSlash = &dirs[1];
+ char* slash;
+ while ( (slash = strchr(afterSlash, '/')) != NULL ) {
+ *slash = '\0';
+ ::mkdir(dirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH);
+ //printf("mkdir(%s)\n", dirs);
+ *slash = '/';
+ afterSlash = slash+1;
+ }
+ }
+}
+
+
+
+template <typename A>
+size_t dylib_maker(const void* mapped_cache, std::vector<uint8_t> &dylib_data, const std::vector<seg_info>& segments) {
+ typedef typename A::P P;
+
+ size_t additionalSize = 0;
+ for(std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+ additionalSize += it->sizem;
+ }
+
+ dylib_data.reserve(dylib_data.size() + additionalSize);
+
+ uint32_t nfat_archs = 0;
+ uint32_t offsetInFatFile = 4096;
+ uint8_t *base_ptr = &dylib_data.front();
+
+#define FH reinterpret_cast<fat_header*>(base_ptr)
+#define FA reinterpret_cast<fat_arch*>(base_ptr + (8 + (nfat_archs - 1) * sizeof(fat_arch)))
+
+ if(dylib_data.size() >= 4096 && OSSwapBigToHostInt32(FH->magic) == FAT_MAGIC) {
+ // have fat header, append new arch to end
+ nfat_archs = OSSwapBigToHostInt32(FH->nfat_arch);
+ offsetInFatFile = OSSwapBigToHostInt32(FA->offset) + OSSwapBigToHostInt32(FA->size);
+ }
+
+ dylib_data.resize(offsetInFatFile);
+ base_ptr = &dylib_data.front();
+
+ FH->magic = OSSwapHostToBigInt32(FAT_MAGIC);
+ FH->nfat_arch = OSSwapHostToBigInt32(++nfat_archs);
+
+ FA->cputype = 0; // filled in later
+ FA->cpusubtype = 0; // filled in later
+ FA->offset = OSSwapHostToBigInt32(offsetInFatFile);
+ FA->size = 0; // filled in later
+ FA->align = OSSwapHostToBigInt32(12);
+
+ // Write regular segments into the buffer
+ uint32_t totalSize = 0;
+
+ for( std::vector<seg_info>::const_iterator it=segments.begin(); it != segments.end(); ++it) {
+
+ if(strcmp(it->segName, "__TEXT") == 0 ) {
+ const macho_header<P> *textMH = reinterpret_cast<macho_header<P>*>((uint8_t*)mapped_cache+it->offset);
+ FA->cputype = OSSwapHostToBigInt32(textMH->cputype());
+ FA->cpusubtype = OSSwapHostToBigInt32(textMH->cpusubtype());
+
+ // if this cputype/subtype already exist in fat header, then return immediately
+ for(uint32_t i=0; i < nfat_archs-1; ++i) {
+ fat_arch *afa = reinterpret_cast<fat_arch*>(base_ptr+8)+i;
+
+ if( afa->cputype == FA->cputype
+ && afa->cpusubtype == FA->cpusubtype) {
+ fprintf(stderr, "arch already exists in fat dylib\n");
+ dylib_data.resize(offsetInFatFile);
+ return offsetInFatFile;
+ }
+ }
+ }
+
+ //printf("segName=%s, offset=0x%llX, size=0x%0llX\n", it->segName, it->offset, it->sizem);
+ std::copy(((uint8_t*)mapped_cache)+it->offset, ((uint8_t*)mapped_cache)+it->offset+it->sizem, std::back_inserter(dylib_data));
+ base_ptr = &dylib_data.front();
+ totalSize += it->sizem;
+ }
+
+ FA->size = OSSwapHostToBigInt32(totalSize);
+
+ // optimize linkedit
+ uint64_t newSize = dylib_data.size();
+ optimize_linkedit<A>(((macho_header<P>*)(base_ptr+offsetInFatFile)), mapped_cache, &newSize);
+
+ // update fat header with new file size
+ dylib_data.resize(offsetInFatFile+newSize);
+ base_ptr = &dylib_data.front();
+ FA->size = OSSwapHostToBigInt32(newSize);
+#undef FH
+#undef FA
+ return offsetInFatFile;
+}
+
+
+extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path,
+ void (^progress)(unsigned current, unsigned total))
+{
+ struct stat statbuf;
+ if (stat(shared_cache_file_path, &statbuf)) {
+ fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_file_path);
+ return -1;
+ }
+
+ int cache_fd = open(shared_cache_file_path, O_RDONLY);
+ if (cache_fd < 0) {
+ fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_file_path);
+ return -1;
+ }
+
+ void* mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+ if (mapped_cache == MAP_FAILED) {
+ fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_file_path, errno);
+ return -1;
+ }
+
+ close(cache_fd);
+
+ // instantiate arch specific dylib maker
+ size_t (*dylib_create_func)(const void*, std::vector<uint8_t>&, const std::vector<seg_info>&) = NULL;
+ if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 )
+ dylib_create_func = dylib_maker<x86>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 )
+ dylib_create_func = dylib_maker<x86_64>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 ppc") == 0 )
+ dylib_create_func = dylib_maker<ppc>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 )
+ dylib_create_func = dylib_maker<arm>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 )
+ dylib_create_func = dylib_maker<arm>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 )
+ dylib_create_func = dylib_maker<arm>;
+ else {
+ fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
+ munmap(mapped_cache, statbuf.st_size);
+ return -1;
+ }
+
+ // iterate through all images in cache and build map of dylibs and segments
+ __block NameToSegments map;
+ dyld_shared_cache_iterate_segments_with_slide(mapped_cache,
+ ^(const char* dylib, const char* segName, uint64_t offset, uint64_t sizem,
+ uint64_t mappedddress, uint64_t slide) {
+ map[dylib].push_back(seg_info(segName, offset, sizem));
+ });
+
+ // for each dylib instantiate a dylib file
+ dispatch_group_t group = dispatch_group_create();
+ dispatch_semaphore_t sema = dispatch_semaphore_create(4);
+ dispatch_queue_t process_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
+ dispatch_queue_t writer_queue = dispatch_queue_create("dyld writer queue", 0);
+
+ __block int cumulativeResult = 0;
+ __block unsigned count = 0;
+
+ for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) {
+ dispatch_group_async(group, process_queue, ^{
+ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
+
+ char dylib_path[PATH_MAX];
+ strcpy(dylib_path, extraction_root_path);
+ strcat(dylib_path, "/");
+ strcat(dylib_path, it->first);
+
+ //printf("%s with %lu segments\n", dylib_path, segments.size());
+ // make sure all directories in this path exist
+ make_dirs(dylib_path);
+
+ // open file, create if does not already exist
+ int fd = ::open(dylib_path, O_CREAT | O_EXLOCK | O_RDWR, 0644);
+ if ( fd == -1 ) {
+ fprintf(stderr, "can't open or create dylib file %s, errnor=%d\n", dylib_path, errno);
+ cumulativeResult = -1;
+ return;
+ }
+
+ struct stat statbuf;
+ if (fstat(fd, &statbuf)) {
+ fprintf(stderr, "Error: stat failed for dyld file %s, errnor=%d\n", dylib_path, errno);
+ close(fd);
+ cumulativeResult = -1;
+ return;
+ }
+
+ std::vector<uint8_t> *vec = new std::vector<uint8_t>(statbuf.st_size);
+ if(pread(fd, &vec->front(), vec->size(), 0) != (long)vec->size()) {
+ fprintf(stderr, "can't read dylib file %s, errnor=%d\n", dylib_path, errno);
+ close(fd);
+ cumulativeResult = -1;
+ return;
+ }
+
+ const size_t offset = dylib_create_func(mapped_cache, *vec, it->second);
+
+ dispatch_group_async(group, writer_queue, ^{
+ progress(count++, map.size());
+
+ if(offset != vec->size()) {
+ //Write out the first page, and everything after offset
+ if( pwrite(fd, &vec->front(), 4096, 0) == -1
+ || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) {
+ fprintf(stderr, "error writing, errnor=%d\n", errno);
+ cumulativeResult = -1;
+ }
+ }
+
+ delete vec;
+ close(fd);
+ dispatch_semaphore_signal(sema);
+ });
+ });
+ }
+
+ dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+ dispatch_release(group);
+ dispatch_release(writer_queue);
+
+ munmap(mapped_cache, statbuf.st_size);
+ return cumulativeResult;
+}
+
+
+
+int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path)
+{
+ return dyld_shared_cache_extract_dylibs_progress(shared_cache_file_path, extraction_root_path,
+ ^(unsigned , unsigned) {} );
+}
+
+
+#if 0
+int main(int argc, const char* argv[])
+{
+ if ( argc != 3 ) {
+ fprintf(stderr, "usage: dsc_extractor <path-to-cache-file> <path-to-device-dir>\n");
+ return 1;
+ }
+
+ int result = dyld_shared_cache_extract_dylibs_progress(argv[1], argv[2], ^(unsigned c, unsigned total) { printf("%d/%d\n", c, total); } );
+ fprintf(stderr, "dyld_shared_cache_extract_dylibs_progress() => %d\n", result);
+ return 0;
+}
+#endif
+
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#ifndef _DYLD_SHARED_CACHE_EXTRACTOR_
+#define _DYLD_SHARED_CACHE_EXTRACTOR_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int dyld_shared_cache_extract_dylibs(const char* shared_cache_file_path, const char* extraction_root_path);
+extern int dyld_shared_cache_extract_dylibs_progress(const char* shared_cache_file_path, const char* extraction_root_path,
+ void (^progress)(unsigned current, unsigned total));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _DYLD_SHARED_CACHE_EXTRACTOR_
+
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
#include <stdlib.h>
+#include <stdio.h>
+#include <Availability.h>
+
#include "dsc_iterator.h"
#include "dyld_cache_format.h"
#include "CacheFileAbstraction.hpp"
+
namespace dyld {
+
// convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped
template <typename E>
const uint8_t* mappedAddress(const uint8_t* cache, uint64_t addr)
// call the callback block on each segment in this image
template <typename A>
- void walkSegments(const uint8_t* cache, const char* dylibPath, const uint8_t* machHeader, dyld_shared_cache_iterator_t callback)
+ void walkSegments(const uint8_t* cache, const char* dylibPath, const uint8_t* machHeader, uint64_t slide, dyld_shared_cache_iterator_slide_t callback)
{
typedef typename A::P P;
typedef typename A::P::E E;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
- const uint8_t* segStartInCache = mappedAddress<E>(cache, segCmd->vmaddr());
- uint64_t fileOffset = segStartInCache - cache;
- callback(dylibPath, segCmd->segname(), fileOffset, segCmd->vmsize());
+ uint64_t fileOffset = segCmd->fileoff();
+ // work around until <rdar://problem/7022345> is fixed
+ if ( fileOffset == 0 ) {
+ fileOffset = (machHeader - cache);
+ }
+ uint64_t sizem = segCmd->vmsize();
+ if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
+ // clip LINKEDIT size if bigger than cache file
+ const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
+ const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
+ if ( mappings[2].file_offset() <= fileOffset ) {
+ if ( sizem > mappings[2].size() )
+ sizem = mappings[2].file_offset() + mappings[2].size() - fileOffset;
+ }
+ }
+ callback(dylibPath, segCmd->segname(), fileOffset, sizem, segCmd->vmaddr()+slide, slide);
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
}
-
+
// call walkSegments on each image in the cache
template <typename A>
- int walkImages(const uint8_t* cache, dyld_shared_cache_iterator_t callback)
+ int walkImages(const uint8_t* cache, dyld_shared_cache_iterator_slide_t callback)
{
- typedef typename A::P::E E;
+ typedef typename A::P::E E;
+ typedef typename A::P P;
+ typedef typename A::P::uint_t pint_t;
const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
+ uint64_t slide = 0;
+ if ( header->mappingOffset() >= 0x48 ) {
+ const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
+ uint64_t storedPointerToHeader = P::getP(*((pint_t*)&cache[mappings[1].file_offset()]));
+ slide = storedPointerToHeader - mappings[0].address();
+ }
const dyldCacheImageInfo<E>* dylibs = (dyldCacheImageInfo<E>*)&cache[header->imagesOffset()];
for (uint32_t i=0; i < header->imagesCount(); ++i) {
const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset();
const uint8_t* machHeader = mappedAddress<E>(cache, dylibs[i].address());
- walkSegments<A>(cache, dylibPath, machHeader, callback);
+ walkSegments<A>(cache, dylibPath, machHeader, slide, callback);
}
return 0;
}
// this routine will call the callback block once for each segment
// in each dylib in the shared cache file.
// Returns -1 if there was an error, otherwise 0.
-int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback)
+int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback)
{
const uint8_t* cache = (uint8_t*)shared_cache_file;
if ( strcmp((char*)cache, "dyld_v1 i386") == 0 )
return dyld::walkImages<x86_64>(cache, callback);
else if ( strcmp((char*)cache, "dyld_v1 ppc") == 0 )
return dyld::walkImages<ppc>(cache, callback);
+ else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 )
+ return dyld::walkImages<arm>(cache, callback);
+ else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 )
+ return dyld::walkImages<arm>(cache, callback);
+ else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 )
+ return dyld::walkImages<arm>(cache, callback);
else
return -1;
}
+// implement non-block version by calling block version
+int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData)
+{
+ return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName,
+ uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) {
+ (*func)(dylibName, segName, offset, size, mappedddress, slide, userData);
+ });
+}
+
+
+// implement non-slide version by wrapping slide version in block
+int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback)
+{
+ dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset,
+ uint64_t size, uint64_t mappedddress, uint64_t slide) {
+ callback(dylibName, segName, offset, size, mappedddress);
+ };
+ return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb);
+}
+
+// implement non-slide,non-block version by wrapping slide version in block
+int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData)
+{
+ return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName,
+ uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) {
+ (*func)(dylibName, segName, offset, size, mappedddress, userData);
+ });
+}
+
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
extern "C" {
#endif
- typedef void (^dyld_shared_cache_iterator_t)(const char* dylib, const char* segName, uint64_t offset, uint64_t size);
- // Given a pointer to an in-memory copy of a dyld shared cache file,
- // this routine will call the callback block once for each segment
- // in each dylib in the shared cache file.
- // Returns -1 if there was an error, otherwise 0.
- extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback);
+typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress);
+typedef void (^dyld_shared_cache_iterator_slide_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide);
+typedef void (*dyld_shared_cache_iterator_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, void* userData);
+typedef void (*dyld_shared_cache_iterator_slide_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, uint64_t slide, void* userData);
+
+// Given a pointer to an in-memory copy of a dyld shared cache file,
+// this routine will call the callback block once for each segment
+// in each dylib in the shared cache file.
+// Returns -1 if there was an error, otherwise 0.
+extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback);
+extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback);
+extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData);
+extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData);
+
-
#ifdef __cplusplus
}
#endif
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <Availability.h>
+
+#define NO_ULEB 1
+#include "dyld_cache_format.h"
+#include "Architectures.hpp"
+#include "MachOFileAbstraction.hpp"
+#include "CacheFileAbstraction.hpp"
+
+#include "dsc_slider.h"
+
+int update_dyld_shared_cache_load_address(const char* path, void (*logProc)(const char* format, ...) )
+{
+ int fd = open(path, O_RDONLY, 0);
+ if ( fd == -1 ) {
+ (*logProc)("open(%s) failed, errno=%d\n", path, errno);
+ return -1;
+ }
+ (void)fcntl(fd, F_NOCACHE, 1); // tell kernel to not cache file content
+
+ uint8_t buffer[4096];
+ if ( read(fd, buffer, 4096) != 4096 ) {
+ (*logProc)("read(%s) failed, errno=%d\n", path, errno);
+ close(fd);
+ return -1;
+ }
+ bool has64BitPointers = false;
+ bool isBigEndian = false;
+ uint64_t startReadOnlySharedRegion = 0;
+ uint64_t endReadOnlySharedRegion = 0;
+ uint64_t startReadWriteSharedRegion = 0;
+ uint64_t endReadWriteSharedRegion = 0;
+ if ( strcmp((char*)buffer, "dyld_v1 i386") == 0 ) {
+ has64BitPointers = false;
+ isBigEndian = false;
+ }
+ else if ( strcmp((char*)buffer, "dyld_v1 x86_64") == 0 ) {
+ has64BitPointers = true;
+ isBigEndian = false;
+ startReadOnlySharedRegion = 0x7FFF80000000LL;
+ endReadOnlySharedRegion = 0x7FFFC0000000LL;
+ startReadWriteSharedRegion = 0x7FFF70000000LL;
+ endReadWriteSharedRegion = 0x7FFF80000000LL;
+ }
+ else if ( strcmp((char*)buffer, "dyld_v1 ppc") == 0 ) {
+ has64BitPointers = false;
+ isBigEndian = true;
+ }
+ else if ( strcmp((char*)buffer, "dyld_v1 armv5") == 0 ) {
+ has64BitPointers = false;
+ isBigEndian = false;
+ startReadOnlySharedRegion = 0x30000000LL;
+ endReadOnlySharedRegion = 0x3E000000LL;
+ startReadWriteSharedRegion = 0x3E000000LL;
+ endReadWriteSharedRegion = 0x40000000LL;
+ }
+ else if ( strcmp((char*)buffer, "dyld_v1 armv6") == 0 ) {
+ has64BitPointers = false;
+ isBigEndian = false;
+ startReadOnlySharedRegion = 0x30000000LL;
+ endReadOnlySharedRegion = 0x3E000000LL;
+ startReadWriteSharedRegion = 0x3E000000LL;
+ endReadWriteSharedRegion = 0x40000000LL;
+ }
+ else if ( strcmp((char*)buffer, "dyld_v1 armv7") == 0 ) {
+ has64BitPointers = false;
+ isBigEndian = false;
+ startReadOnlySharedRegion = 0x30000000LL;
+ endReadOnlySharedRegion = 0x3E000000LL;
+ startReadWriteSharedRegion = 0x3E000000LL;
+ endReadWriteSharedRegion = 0x40000000LL;
+ }
+ else {
+ (*logProc)("file %s is not a known dyld shared cache file\n", path);
+ close(fd);
+ return -1;
+ }
+#if __BIG_ENDIAN__
+ bool swap = !isBigEndian;
+#else
+ bool swap = isBigEndian;
+#endif
+
+ const dyld_cache_header* header = (dyld_cache_header*)buffer;
+ uint32_t mappingOffset = swap ? OSSwapInt32(header->mappingOffset) : header->mappingOffset;
+ if ( mappingOffset < 0x48 ) {
+ (*logProc)("dyld shared cache file %s is old format\n", path);
+ close(fd);
+ return -1;
+ }
+
+ uint32_t mappingCount = swap ? OSSwapInt32(header->mappingCount) : header->mappingCount;
+ if ( mappingCount != 3 ) {
+ (*logProc)("dyld shared cache file %s has wrong mapping count\n", path);
+ close(fd);
+ return -1;
+ }
+
+ uint64_t slidePointersOffset = swap ? OSSwapInt64(header->slidePointersOffset) : header->slidePointersOffset;
+ uint64_t slidePointersSize = swap ? OSSwapInt64(header->slidePointersSize) : header->slidePointersSize;
+ if ( (slidePointersOffset == 0) || (slidePointersSize == 0) ) {
+ (*logProc)("dyld shared cache file %s is missing slide information\n", path);
+ close(fd);
+ return -1;
+ }
+
+ // read slide info
+ void* slideInfo = malloc(slidePointersSize);
+ if ( slideInfo == NULL ) {
+ (*logProc)("malloc(%llu) failed\n", slidePointersSize);
+ close(fd);
+ return -1;
+ }
+ int64_t amountRead = pread(fd, slideInfo, slidePointersSize, slidePointersOffset);
+ if ( amountRead != (int64_t)slidePointersSize ) {
+ (*logProc)("slide info pread(fd, buf, %llu, %llu) failed\n", slidePointersSize, slidePointersOffset);
+ close(fd);
+ return -1;
+ }
+
+ // read all DATA
+ const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)&buffer[mappingOffset];
+ uint64_t dataFileOffset = swap ? OSSwapInt64(mappings[1].fileOffset) : mappings[1].fileOffset;
+ uint64_t dataSize = swap ? OSSwapInt64(mappings[1].size) : mappings[1].size;
+ uint8_t* data = (uint8_t*)malloc(dataSize);
+ amountRead = pread(fd, data, dataSize, dataFileOffset);
+ if ( amountRead != (int64_t)dataSize ) {
+ (*logProc)("data pread(fd, buf, %llu, %llu) failed\n", data, dataSize);
+ close(fd);
+ return -1;
+ }
+
+ // close read-only file
+ close(fd);
+
+ // extract current slide
+ uint64_t headerDataUses;
+ if ( has64BitPointers ) {
+ headerDataUses = *((uint64_t*)data);
+ if ( swap )
+ headerDataUses = OSSwapInt64(headerDataUses);
+ }
+ else {
+ uint32_t temp = *((uint32_t*)data);
+ if ( swap )
+ temp = OSSwapInt32(temp);
+ headerDataUses = temp;
+ }
+ uint64_t textAddress = swap ? OSSwapInt64(mappings[0].address) : mappings[0].address;
+ uint32_t currentSlide = headerDataUses - textAddress;
+ (*logProc)("currentSlide=0x%08X\n", currentSlide);
+
+ // find read-only space
+ uint64_t linkeditAddress = swap ? OSSwapInt64(mappings[2].address) : mappings[2].address;
+ uint64_t linkeditSize = swap ? OSSwapInt64(mappings[2].size) : mappings[2].size;
+ uint64_t linkeditEnd = linkeditAddress + linkeditSize;
+ uint64_t roSpace = endReadOnlySharedRegion - linkeditEnd;
+ (*logProc)("ro space=0x%08llX\n", roSpace);
+
+ // find read-write space
+ uint64_t dataAddress = swap ? OSSwapInt64(mappings[1].address) : mappings[1].address;
+ uint64_t dataEnd = dataAddress + dataSize;
+ uint64_t rwSpace = endReadWriteSharedRegion - dataEnd;
+ (*logProc)("rw space=0x%08llX\n", rwSpace);
+
+ // choose new random slide
+ uint32_t slideSpace = rwSpace;
+ if ( roSpace < rwSpace )
+ slideSpace = roSpace;
+ uint32_t newSlide = (arc4random() % slideSpace) & (-4096);
+ (*logProc)("newSlide=0x%08X\n", newSlide);
+ int32_t slideAdjustment = newSlide - currentSlide;
+
+ // update DATA with slide info
+ uint64_t offsetInCacheFile = 0;
+ const uint8_t* infoStart = (uint8_t*)slideInfo;
+ const uint8_t* infoEnd = infoStart + slidePointersSize;
+ for(const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd); ) {
+ uint64_t delta = 0;
+ uint32_t shift = 0;
+ bool more = true;
+ do {
+ uint8_t byte = *p++;
+ delta |= ((byte & 0x7F) << shift);
+ shift += 7;
+ if ( byte < 0x80 ) {
+ offsetInCacheFile += delta;
+ // verify in DATA range
+ if ( (offsetInCacheFile < dataFileOffset) || (offsetInCacheFile > (dataFileOffset+dataSize)) ) {
+ (*logProc)("pointer offset 0x%llX outside DATA range\n", offsetInCacheFile);
+ return -1;
+ }
+ uint32_t offsetInData = offsetInCacheFile - dataFileOffset;
+ if ( has64BitPointers ) {
+ uint64_t value64 = *((uint64_t*)(&data[offsetInData]));
+ if ( swap )
+ value64 = OSSwapInt64(value64);
+ value64 += slideAdjustment;
+ if ( swap )
+ value64 = OSSwapInt64(value64);
+ *((uint64_t*)(&data[offsetInData])) = value64;
+ }
+ else {
+ uint64_t value32 = *((uint32_t*)(&data[offsetInData]));
+ if ( swap )
+ value32 = OSSwapInt32(value32);
+ value32 += slideAdjustment;
+ if ( swap )
+ value32 = OSSwapInt32(value32);
+ *((uint32_t*)(&data[offsetInData])) = value32;
+ }
+ //(*logProc)("update pointer at offset 0x%08X", offsetInData);
+ more = false;
+ }
+ } while (more);
+ }
+ free(slideInfo);
+ slideInfo = NULL;
+
+ // re-open cache file and overwrite DATA range
+ fd = open(path, O_RDWR, 0);
+ if ( fd == -1 ) {
+ (*logProc)("open(%s) failed, errno=%d\n", path, errno);
+ return -1;
+ }
+ (void)fcntl(fd, F_NOCACHE, 1); // tell kernel to not cache file content
+ uint64_t amountWrote = pwrite(fd, data, dataSize, dataFileOffset);
+ if ( amountWrote != dataSize ) {
+ (*logProc)("data pwrite(fd, buf, %llu, %llu) failed\n", data, dataSize);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ // re-open and re-read to verify write
+ fd = open(path, O_RDONLY, 0);
+ if ( fd == -1 ) {
+ (*logProc)("verify open(%s) failed, errno=%d\n", path, errno);
+ return -1;
+ }
+ (void)fcntl(fd, F_NOCACHE, 1); // tell kernel to not cache file content
+ uint8_t* dataVerify = (uint8_t*)malloc(dataSize);
+ amountRead = pread(fd, dataVerify, dataSize, dataFileOffset);
+ if ( amountRead != (int64_t)dataSize ) {
+ (*logProc)("verify data pread(fd, buf, %llu, %llu) failed\n", data, dataSize);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ if ( memcmp(data, dataVerify, dataSize) != 0 ) {
+ (*logProc)("data update verification failed\n");
+ return -1;
+ }
+ free(data);
+ free(dataVerify);
+
+
+ // success
+ return 0;
+}
+
+
+#if 0
+static void logger(const char* format, ...)
+{
+ va_list list;
+ va_start(list, format);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, format, list);
+}
+
+
+int main(int argc, const char* argv[])
+{
+ return update_dyld_shared_cache_load_address(argv[1], logger);
+}
+#endif
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// This function is called with a path to a dyld shared cache file. It will update the shared cache file
+// in place. The update randomizes the load address when the shared cache file is later used by dyld.
+//
+// On success, the return value is zero.
+// On failure the return value is non-zero and an explanation error was written to the logProc callback.
+//
+extern int update_dyld_shared_cache_load_address(const char* path, void (*logProc)(const char* format, ...) );
+
+
+#ifdef __cplusplus
+}
+#endif
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <mach/shared_region.h>
- struct dyld_cache_header
+struct dyld_cache_header
{
char magic[16]; // e.g. "dyld_v0 ppc"
- uint32_t mappingOffset; // file offset to first shared_file_mapping_np
- uint32_t mappingCount; // number of shared_file_mapping_np entries
+ uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
+ uint32_t mappingCount; // number of dyld_cache_mapping_info entries
uint32_t imagesOffset; // file offset to first dyld_cache_image_info
uint32_t imagesCount; // number of dyld_cache_image_info entries
uint64_t dyldBaseAddress; // base address of dyld when cache was built
+ uint64_t codeSignatureOffset; // file offset of code signature blob
+ uint64_t codeSignatureSize; // size of code signature blob (zero means to end of file)
+ uint64_t slideInfoOffset; // file offset of kernel slid info
+ uint64_t slideInfoSize; // size of kernel slid info
+};
+
+struct dyld_cache_mapping_info {
+ uint64_t address;
+ uint64_t size;
+ uint64_t fileOffset;
+ uint32_t maxProt;
+ uint32_t initProt;
};
struct dyld_cache_image_info
uint32_t pad;
};
-#define DYLD_SHARED_CACHE_DIR "/var/db/dyld/"
+struct dyld_cache_slide_info
+{
+ uint32_t version; // currently 1
+ uint32_t toc_offset;
+ uint32_t toc_count;
+ uint32_t entries_offset;
+ uint32_t entries_count;
+ uint32_t entries_size; // currently 128
+ // uint16_t toc[toc_count];
+ // entrybitmap entries[entries_count];
+};
+
+
+
+#define MACOSX_DYLD_SHARED_CACHE_DIR "/var/db/dyld/"
+#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/"
#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_"
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/syslimits.h>
+#include <mach-o/arch.h>
+#include <mach-o/loader.h>
+
+#include <vector>
+#include <map>
+#include <set>
+
+#include "dsc_iterator.h"
+#include "dyld_cache_format.h"
+#include "Architectures.hpp"
+#include "MachOFileAbstraction.hpp"
+#include "CacheFileAbstraction.hpp"
+
+
+#define OP_NULL 0
+#define OP_LIST_DEPENDENCIES 1
+#define OP_LIST_DYLIBS 2
+#define OP_LIST_LINKEDIT 3
+
+#define UUID_BYTES 16
+
+// Define this here so we can work with or without block support
+typedef void (*segment_callback_t)(const char* dylib, const char* segName, uint64_t offset, uint64_t sizem,
+ uint64_t mappedddress, uint64_t slide, void* userData);
+
+struct seg_callback_args {
+ char *target_path;
+ uint32_t target_found;
+ void *mapped_cache;
+ uint32_t op;
+ uint8_t print_uuids;
+ uint8_t print_vmaddrs;
+ uint8_t print_dylib_versions;
+};
+
+void usage() {
+ fprintf(stderr, "Usage: dscutil -list [ -uuid ] [-vmaddr] | -dependents <dylib-path> [ -versions ] | -linkedit [ shared-cache-file ]\n");
+}
+
+/*
+ * Get the path to the native shared cache for this host
+ */
+static const char* default_shared_cache_path() {
+#if __i386__
+ return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386";
+#elif __x86_64__
+ return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64";
+#elif __ppc__
+ return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "rosetta";
+#elif __ARM_ARCH_5TEJ__
+ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5";
+#elif __ARM_ARCH_6K__
+ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6";
+#elif __ARM_ARCH_7A__
+ return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7";
+#else
+ #error unsupported architecture
+#endif
+}
+
+/*
+ * Get a vector of all the load commands from the header pointed to by headerAddr
+ */
+template <typename A>
+std::vector<macho_load_command<typename A::P>* > get_load_cmds(void *headerAddr) {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+
+ std::vector<macho_load_command<P>* > cmd_vector;
+
+ const macho_header<P>* mh = (const macho_header<P>*)headerAddr;
+ uint32_t ncmds = mh->ncmds();
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((long)mh + sizeof(macho_header<P>));
+ const macho_load_command<P>* cmd = cmds;
+
+ for (uint32_t i = 0; i < ncmds; i++) {
+ cmd_vector.push_back((macho_load_command<P>*)cmd);
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+ return cmd_vector;
+}
+
+/*
+ * List dependencies from the mach-o header at headerAddr
+ * in the same format as 'otool -L'
+ */
+template <typename A>
+void list_dependencies(const char *dylib, void *headerAddr, uint8_t print_dylib_versions) {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+
+ std::vector< macho_load_command<P>* > cmds;
+ cmds = get_load_cmds<A>(headerAddr);
+ for(typename std::vector<macho_load_command<P>*>::iterator it = cmds.begin(); it != cmds.end(); ++it) {
+ uint32_t cmdType = (*it)->cmd();
+ if (cmdType == LC_LOAD_DYLIB ||
+ cmdType == LC_ID_DYLIB ||
+ cmdType == LC_LOAD_WEAK_DYLIB ||
+ cmdType == LC_REEXPORT_DYLIB ||
+ cmdType == LC_LOAD_UPWARD_DYLIB) {
+ macho_dylib_command<P>* dylib_cmd = (macho_dylib_command<P>*)*it;
+ const char *name = dylib_cmd->name();
+ uint32_t compat_vers = dylib_cmd->compatibility_version();
+ uint32_t current_vers = dylib_cmd->current_version();
+
+ if (print_dylib_versions) {
+ printf("\t%s (compatibility version %u.%u.%u, current version %u.%u.%u)\n",
+ name,
+ (compat_vers >> 16),
+ (compat_vers >> 8) & 0xff,
+ (compat_vers) & 0xff,
+ (current_vers >> 16),
+ (current_vers >> 8) & 0xff,
+ (current_vers) & 0xff);
+ } else {
+ printf("\t%s\n", name);
+ }
+ }
+ }
+}
+
+/*
+ * Print out a dylib from the shared cache, optionally including the UUID
+ */
+template <typename A>
+void print_dylib(const char *dylib, void *headerAddr, uint64_t slide, struct seg_callback_args *args) {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ char uuid_str[UUID_BYTES*3];
+ uint8_t got_uuid = 0;
+ uint64_t vmaddr = 0;
+
+ std::vector< macho_load_command<P>* > cmds;
+ cmds = get_load_cmds<A>(headerAddr);
+ for(typename std::vector<macho_load_command<P>*>::iterator it = cmds.begin(); it != cmds.end(); ++it) {
+ uint32_t cmdType = (*it)->cmd();
+ if (cmdType == LC_UUID) {
+ macho_uuid_command<P>* uuid_cmd = (macho_uuid_command<P>*)*it;
+ const uint8_t *uuid = uuid_cmd->uuid();
+ sprintf(uuid_str, "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X>",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+ got_uuid = 1;
+ }
+ else if (cmdType == LC_SEGMENT) {
+ macho_segment_command<P>* seg_cmd = (macho_segment_command<P>*)*it;
+ if (strcmp(seg_cmd->segname(), "__TEXT") == 0) {
+ vmaddr = seg_cmd->vmaddr();
+ }
+ }
+ }
+
+ if (args->print_vmaddrs)
+ printf("0x%08llX ", vmaddr+slide);
+ if (args->print_uuids) {
+ if (got_uuid)
+ printf("%s ", uuid_str);
+ else
+ printf("< no uuid in dylib > ");
+ }
+ printf("%s\n", dylib);
+}
+
+
+uint64_t sLinkeditBase = 0;
+std::map<uint32_t, char*> sPageToContent;
+
+
+
+
+static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, char* message)
+{
+ for (uint32_t p = pageStart; p <= pageEnd; p += 4096) {
+ std::map<uint32_t, char*>::iterator pos = sPageToContent.find(p);
+ if ( pos == sPageToContent.end() ) {
+ sPageToContent[p] = strdup(message);
+ }
+ else {
+ char* oldMessage = pos->second;
+ char* newMesssage;
+ asprintf(&newMesssage, "%s, %s", oldMessage, message);
+ sPageToContent[p] = newMesssage;
+ free(oldMessage);
+ }
+ }
+}
+
+
+/*
+ * get LINKEDIT info for dylib
+ */
+template <typename A>
+void process_linkedit(const char* dylib, void* headerAddr) {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ // filter out symlinks by only handling first path found for each mach header
+ static std::set<void*> seenImages;
+ if ( seenImages.count(headerAddr) != 0 )
+ return;
+ seenImages.insert(headerAddr);
+ const macho_header<P>* mh = (const macho_header<P>*)headerAddr;
+ uint32_t ncmds = mh->ncmds();
+ const macho_load_command<P>* const cmds = (macho_load_command<P>*)((long)mh + sizeof(macho_header<P>));
+ const macho_load_command<P>* cmd = cmds;
+ for (uint32_t i = 0; i < ncmds; i++) {
+ if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) {
+ macho_dyld_info_command<P>* dyldInfo = (macho_dyld_info_command<P>*)cmd;
+ char message[1000];
+ const char* shortName = strrchr(dylib, '/') + 1;
+ // add export trie info
+ if ( dyldInfo->export_size() != 0 ) {
+ //printf("export_off=0x%X\n", dyldInfo->export_off());
+ uint32_t exportPageOffsetStart = dyldInfo->export_off() & (-4096);
+ uint32_t exportPageOffsetEnd = (dyldInfo->export_off() + dyldInfo->export_size()) & (-4096);
+ sprintf(message, "exports from %s", shortName);
+ add_linkedit(exportPageOffsetStart, exportPageOffsetEnd, message);
+ }
+ // add binding info
+ if ( dyldInfo->bind_size() != 0 ) {
+ uint32_t bindPageOffsetStart = dyldInfo->bind_off() & (-4096);
+ uint32_t bindPageOffsetEnd = (dyldInfo->bind_off() + dyldInfo->bind_size()) & (-4096);
+ sprintf(message, "bindings from %s", shortName);
+ add_linkedit(bindPageOffsetStart, bindPageOffsetEnd, message);
+ }
+ // add lazy binding info
+ if ( dyldInfo->lazy_bind_size() != 0 ) {
+ uint32_t lazybindPageOffsetStart = dyldInfo->lazy_bind_off() & (-4096);
+ uint32_t lazybindPageOffsetEnd = (dyldInfo->lazy_bind_off() + dyldInfo->lazy_bind_size()) & (-4096);
+ sprintf(message, "lazy bindings from %s", shortName);
+ add_linkedit(lazybindPageOffsetStart, lazybindPageOffsetEnd, message);
+ }
+ // add weak binding info
+ if ( dyldInfo->weak_bind_size() != 0 ) {
+ uint32_t weakbindPageOffsetStart = dyldInfo->weak_bind_off() & (-4096);
+ uint32_t weakbindPageOffsetEnd = (dyldInfo->weak_bind_off() + dyldInfo->weak_bind_size()) & (-4096);
+ sprintf(message, "weak bindings from %s", shortName);
+ add_linkedit(weakbindPageOffsetStart, weakbindPageOffsetEnd, message);
+ }
+ }
+ cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
+ }
+}
+
+
+
+/*
+ * This callback is used with dsc_iterator, and called once for each segment in the target shared cache
+ */
+template <typename A>
+void segment_callback(const char *dylib, const char *segName, uint64_t offset, uint64_t sizem,
+ uint64_t mappedAddress, uint64_t slide, void *userData) {
+ typedef typename A::P P;
+ typedef typename A::P::E E;
+ struct seg_callback_args *args = (struct seg_callback_args *)userData;
+ if (strncmp(segName, "__TEXT", 6) == 0) {
+ int target_match = args->target_path ? (strcmp(args->target_path, dylib) == 0) : 0;
+ if (!args->target_path || target_match) {
+ if (target_match) {
+ args->target_found = 1;
+ }
+ void *headerAddr = (void*)((long)args->mapped_cache + (long)offset);
+ switch (args->op) {
+ case OP_LIST_DEPENDENCIES:
+ list_dependencies<A>(dylib, headerAddr, args->print_dylib_versions);
+ break;
+ case OP_LIST_DYLIBS:
+ print_dylib<A>(dylib, headerAddr, slide, args);
+ break;
+ case OP_LIST_LINKEDIT:
+ process_linkedit<A>(dylib, headerAddr);
+ default:
+ break;
+ }
+ }
+ }
+ else if (strncmp(segName, "__LINKEDIT", 6) == 0) {
+ sLinkeditBase = mappedAddress - offset;
+ }
+}
+
+
+
+
+int main (int argc, char **argv) {
+ struct seg_callback_args args;
+ const char *shared_cache_path = NULL;
+ void *mapped_cache;
+ struct stat statbuf;
+ int cache_fd;
+ char c;
+ bool print_slide_info = false;
+
+ args.target_path = NULL;
+ args.op = OP_NULL;
+ args.print_uuids = 0;
+ args.print_vmaddrs = 0;
+ args.print_dylib_versions = 0;
+ args.target_found = 0;
+
+ for (uint32_t optind = 1; optind < argc; optind++) {
+ char *opt = argv[optind];
+ if (opt[0] == '-') {
+ if (strcmp(opt, "-list") == 0) {
+ if (args.op) {
+ fprintf(stderr, "Error: select one of -list or -dependents\n");
+ usage();
+ exit(1);
+ }
+ args.op = OP_LIST_DYLIBS;
+ } else if (strcmp(opt, "-dependents") == 0) {
+ if (args.op) {
+ fprintf(stderr, "Error: select one of -list or -dependents\n");
+ usage();
+ exit(1);
+ }
+ if (!(++optind < argc)) {
+ fprintf(stderr, "Error: option -depdendents requires an argument\n");
+ usage();
+ exit(1);
+ }
+ args.op = OP_LIST_DEPENDENCIES;
+ args.target_path = argv[optind];
+ } else if (strcmp(opt, "-uuid") == 0) {
+ args.print_uuids = 1;
+ } else if (strcmp(opt, "-versions") == 0) {
+ args.print_dylib_versions = 1;
+ } else if (strcmp(opt, "-vmaddr") == 0) {
+ args.print_vmaddrs = 1;
+ } else if (strcmp(opt, "-linkedit") == 0) {
+ args.op = OP_LIST_LINKEDIT;
+ } else if (strcmp(opt, "-slide_info") == 0) {
+ print_slide_info = true;
+ } else {
+ fprintf(stderr, "Error: unrecognized option %s\n", opt);
+ usage();
+ exit(1);
+ }
+ } else {
+ shared_cache_path = opt;
+ }
+ }
+
+ if ( !print_slide_info ) {
+ if (args.op == OP_NULL) {
+ fprintf(stderr, "Error: select one of -list or -dependents\n");
+ usage();
+ exit(1);
+ }
+
+ if (args.print_uuids && args.op != OP_LIST_DYLIBS)
+ fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n");
+ if (args.print_vmaddrs && args.op != OP_LIST_DYLIBS)
+ fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n");
+ if (args.print_dylib_versions && args.op != OP_LIST_DEPENDENCIES)
+ fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n");
+
+ if (args.op == OP_LIST_DEPENDENCIES && !args.target_path) {
+ fprintf(stderr, "Error: -dependents given, but no dylib path specified\n");
+ usage();
+ exit(1);
+ }
+ }
+
+ if (!shared_cache_path)
+ shared_cache_path = default_shared_cache_path();
+
+ if (stat(shared_cache_path, &statbuf)) {
+ fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", shared_cache_path);
+ exit(1);
+ }
+
+ cache_fd = open(shared_cache_path, O_RDONLY);
+ if (cache_fd < 0) {
+ fprintf(stderr, "Error: failed to open shared cache file at %s\n", shared_cache_path);
+ exit(1);
+ }
+ mapped_cache = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0);
+ if (mapped_cache == MAP_FAILED) {
+ fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", shared_cache_path, errno);
+ exit(1);
+ }
+
+ if ( print_slide_info ) {
+ const dyldCacheHeader<LittleEndian>* header = (dyldCacheHeader<LittleEndian>*)mapped_cache;
+ if ( (strcmp(header->magic(), "dyld_v1 x86_64") != 0)
+ && (strcmp(header->magic(), "dyld_v1 armv6") != 0)
+ && (strcmp(header->magic(), "dyld_v1 armv7") != 0) ) {
+ fprintf(stderr, "Error: unrecognized dyld shared cache magic or arch does not support sliding\n");
+ exit(1);
+ }
+ if ( header->slideInfoOffset() == 0 ) {
+ fprintf(stderr, "Error: dyld shared cache does not contain slide info\n");
+ exit(1);
+ }
+ const dyldCacheFileMapping<LittleEndian>* mappings = (dyldCacheFileMapping<LittleEndian>*)((char*)mapped_cache + header->mappingOffset());
+ const dyldCacheFileMapping<LittleEndian>* dataMapping = &mappings[1];
+ uint64_t dataStartAddress = dataMapping->address();
+ uint64_t dataSize = dataMapping->size();
+ const dyldCacheSlideInfo<LittleEndian>* slideInfoHeader = (dyldCacheSlideInfo<LittleEndian>*)((char*)mapped_cache+header->slideInfoOffset());
+ printf("slide info version=%d\n", slideInfoHeader->version());
+ printf("toc_count=%d, data page count=%lld\n", slideInfoHeader->toc_count(), dataSize/4096);
+ const dyldCacheSlideInfoEntry* entries = (dyldCacheSlideInfoEntry*)((char*)slideInfoHeader + slideInfoHeader->entries_offset());
+ for(int i=0; i < slideInfoHeader->toc_count(); ++i) {
+ printf("0x%08llX: [% 5d,% 5d] ", dataStartAddress + i*4096, i, slideInfoHeader->toc(i));
+ const dyldCacheSlideInfoEntry* entry = &entries[slideInfoHeader->toc(i)];
+ for(int j=0; j < slideInfoHeader->entries_size(); ++j)
+ printf("%02X", entry->bits[j]);
+ printf("\n");
+ }
+
+
+ }
+ else {
+ segment_callback_t callback;
+ if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 )
+ callback = segment_callback<x86>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 )
+ callback = segment_callback<x86_64>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 ppc") == 0 )
+ callback = segment_callback<ppc>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 )
+ callback = segment_callback<arm>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 )
+ callback = segment_callback<arm>;
+ else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 )
+ callback = segment_callback<arm>;
+ else {
+ fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n");
+ exit(1);
+ }
+
+ args.mapped_cache = mapped_cache;
+
+ #if __BLOCKS__
+ // Shim to allow building for the host
+ void *argsPtr = &args;
+ dyld_shared_cache_iterate_segments_with_slide(mapped_cache,
+ ^(const char* dylib, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide ) {
+ (callback)(dylib, segName, offset, size, mappedddress, slide, argsPtr);
+ });
+ #else
+ dyld_shared_cache_iterate_segments_with_slide_nb(mapped_cache, callback, &args);
+ #endif
+
+ if (args.op == OP_LIST_LINKEDIT) {
+ // dump -linkedit information
+ for (std::map<uint32_t, char*>::iterator it = sPageToContent.begin(); it != sPageToContent.end(); ++it) {
+ printf("0x%0llX %s\n", sLinkeditBase+it->first, it->second);
+ }
+ }
+
+
+ if (args.target_path && !args.target_found) {
+ fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", args.target_path, shared_cache_path);
+ exit(1);
+ }
+ }
+ return 0;
+}
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2006-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2006-2011 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <servers/bootstrap.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
-#include <vproc.h>
-#include <vproc_priv.h>
#include "dyld_cache_format.h"
#define SELOPT_WRITE
#include <objc/objc-selopt.h>
+#define FIRST_DYLIB_TEXT_OFFSET 0x5000
+#define FIRST_DYLIB_DATA_OFFSET 0x1000
+
+#ifndef LC_FUNCTION_STARTS
+ #define LC_FUNCTION_STARTS 0x26
+#endif
static bool verbose = false;
static bool progress = false;
+static bool iPhoneOS = false;
static std::vector<const char*> warnings;
class ArchGraph
{
public:
+ struct CStringEquals {
+ bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
+ };
+ typedef __gnu_cxx::hash_map<const char*, const char*, __gnu_cxx::hash<const char*>, CStringEquals> StringToString;
+
static void addArchPair(ArchPair ap);
static void addRoot(const char* vpath, const std::set<ArchPair>& archs);
static void findSharedDylibs(ArchPair ap);
ArchPair getArchPair() { return fArchPair; }
std::set<const class MachOLayoutAbstraction*>& getSharedDylibs() { return fSharedDylibs; }
+ StringToString& getDylibAliases() { return fAliasesMap; }
const char* archName() { return archName(fArchPair); }
private:
std::set<DependencyNode*> fRootsDependentOnThis;
};
- struct CStringEquals {
- bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); }
- };
typedef __gnu_cxx::hash_map<const char*, class DependencyNode*, __gnu_cxx::hash<const char*>, CStringEquals> PathToNode;
DependencyNode* getNode(const char* path);
DependencyNode* getNodeForVirtualPath(const char* vpath);
static bool canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set<const MachOLayoutAbstraction*>& possibleLibs, std::map<const MachOLayoutAbstraction*, bool>& shareableMap);
+ static bool sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg);
static std::map<ArchPair, ArchGraph*> fgPerArchGraph;
static const char* fgFileSystemRoot;
std::set<DependencyNode*> fRoots;
PathToNode fNodes;
std::set<const MachOLayoutAbstraction*> fSharedDylibs; // use set to avoid duplicates when installname!=realpath
+ StringToString fAliasesMap;
};
std::map<ArchPair, ArchGraph*> ArchGraph::fgPerArchGraph;
const char* ArchGraph::fgFileSystemRoot = "";
for(std::set<ArchPair>::iterator ait = onlyArchs.begin(); ait != onlyArchs.end(); ++ait) {
try {
const MachOLayoutAbstraction* layout = uni.getSlice(*ait);
- fgPerArchGraph[*ait]->addRoot(path, layout);
+ if ( layout != NULL )
+ fgPerArchGraph[*ait]->addRoot(path, layout);
}
catch (const char* msg) {
if ( verbose )
- fprintf(stderr, "update_dyld_shared_cache: warning for %s can't use root %s: %s\n", fgPerArchGraph[*ait]->archName(), path, msg);
+ fprintf(stderr, "update_dyld_shared_cache: warning for %s can't use root '%s': %s\n", fgPerArchGraph[*ait]->archName(), path, msg);
}
}
}
catch (const char* msg) {
- fprintf(stderr, "update_dyld_shared_cache: warning can't use root %s: %s\n", path, msg);
+ fprintf(stderr, "update_dyld_shared_cache: warning can't use root '%s': %s\n", path, msg);
}
}
node->markNeededByRoot(NULL);
}
+// a virtual path does not have the fgFileSystemRoot prefix
// a virtual path does not have the fgFileSystemRoot prefix
ArchGraph::DependencyNode* ArchGraph::getNodeForVirtualPath(const char* vpath)
{
strcpy(completePath, fgFileSystemRoot);
strcat(completePath, vpath); // assumes vpath starts with '/'
if ( fgUsesOverlay ) {
- // using -overlay means if /overlay/usr/lib exists use it, otherwise use original path
+ // using -overlay means if /overlay/path/dylib exists use it, otherwise use /path/dylib
struct stat stat_buf;
if ( stat(completePath, &stat_buf) == 0 )
return this->getNode(completePath);
- else
+ else {
+ // <rdar://problem/9279770> support when install name is a symlink
+ if ( (lstat(vpath, &stat_buf) == 0) && S_ISLNK(stat_buf.st_mode) ) {
+ // requested path did not exist in /overlay, but leaf of path is a symlink in /
+ char pathInSymLink[MAXPATHLEN];
+ size_t res = readlink(vpath, pathInSymLink, sizeof(pathInSymLink));
+ if ( res != -1 ) {
+ pathInSymLink[res] = '\0';
+ if ( pathInSymLink[0] != '/' ) {
+ char symFullPath[MAXPATHLEN];
+ strcpy(symFullPath, vpath);
+ char* lastSlash = strrchr(symFullPath, '/');
+ if ( lastSlash != NULL ) {
+ strcpy(lastSlash+1, pathInSymLink);
+ // (re)try looking for what symlink points to, but in /overlay
+ return this->getNodeForVirtualPath(symFullPath);
+ }
+ }
+ }
+ }
return this->getNode(vpath);
+ }
}
else {
// using -root means always use /rootpath/usr/lib
// look up real path to see if node already exists
pos = fNodes.find(realPath);
- if ( pos != fNodes.end() )
+ if ( pos != fNodes.end() ) {
+ // update fAliasesMap with symlinks found
+ const char* aliasPath = path;
+ if ( (fgFileSystemRoot != NULL) && (strncmp(path, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
+ aliasPath = &path[strlen(fgFileSystemRoot)];
+ }
+ if ( fAliasesMap.find(aliasPath) == fAliasesMap.end() ) {
+ if ( strcmp(aliasPath, pos->second->getLayout()->getID().name) != 0 ) {
+ fAliasesMap[strdup(aliasPath)] = pos->second->getLayout()->getID().name;
+ //fprintf(stderr, "getNode() %s: added alias %s -> %s\n", archName(fArchPair), aliasPath, fAliasesMap[aliasPath]);
+ }
+ }
return pos->second;
+ }
// still does not exist, so create a new node
const UniversalMachOLayout& uni = UniversalMachOLayout::find(realPath);
fNodes[node->getPath()] = node;
// if install name is not real path, add install name to node map
if ( (node->getLayout()->getFileType() == MH_DYLIB) && (strcmp(realPath, node->getLayout()->getID().name) != 0) ) {
- //fprintf(stderr, "adding node alias 0x%08X %s for %s\n", fArch, node->getLayout()->getID().name, realPath);
- fNodes[node->getLayout()->getID().name] = node;
+ //fprintf(stderr, "adding %s node alias %s for %s\n", archName(fArchPair), node->getLayout()->getID().name, realPath);
+ pos = fNodes.find(node->getLayout()->getID().name);
+ if ( pos != fNodes.end() ) {
+ // <rdar://problem/8305479> warn if two dylib in cache have same install_name
+ char* msg;
+ asprintf(&msg, "update_dyld_shared_cache: warning, found two dylibs with same install path: %s\n\t%s\n\t%s\n",
+ node->getLayout()->getID().name, pos->second->getPath(), node->getPath());
+ fprintf(stderr, "%s", msg);
+ warnings.push_back(msg);
+ }
+ else
+ fNodes[node->getLayout()->getID().name] = node;
+ // update fAliasesMap with symlinks found
+ const char* aliasPath = realPath;
+ if ( (fgFileSystemRoot != NULL) && (strncmp(realPath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
+ aliasPath = &realPath[strlen(fgFileSystemRoot)];
+ }
+ if ( fAliasesMap.find(aliasPath) == fAliasesMap.end() ) {
+ if ( strcmp(aliasPath, node->getLayout()->getID().name) != 0 ) {
+ fAliasesMap[strdup(aliasPath)] = node->getLayout()->getID().name;
+ //fprintf(stderr, "getNode() %s: added alias %s -> %s\n", archName(fArchPair), aliasPath, fAliasesMap[aliasPath]);
+ }
+ }
}
return node;
}
// expand @executable_path path prefix
const char* executablePath = mainExecutableLayout->getFilePath();
char newPath[strlen(executablePath) + strlen(dependentPath)+2];
- strcpy(newPath, executablePath);
+ if ( (fgFileSystemRoot != NULL) && (strncmp(executablePath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
+ // executablePath already has rootPath prefix, need to remove that to get to base virtual path
+ strcpy(newPath, &executablePath[strlen(fgFileSystemRoot)]);
+ }
+ else {
+ strcpy(newPath, executablePath);
+ }
char* addPoint = strrchr(newPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &dependentPath[17]);
else if ( strncmp(dependentPath, "@loader_path/", 13) == 0 ) {
// expand @loader_path path prefix
char newPath[strlen(fPath) + strlen(dependentPath)+2];
- strcpy(newPath, fPath);
+ if ( (fgFileSystemRoot != NULL) && (strncmp(fPath, fgFileSystemRoot, strlen(fgFileSystemRoot)) == 0) ) {
+ // fPath already has rootPath prefix, need to remove that to get to base virtual path
+ strcpy(newPath, &fPath[strlen(fgFileSystemRoot)]);
+ }
+ else {
+ strcpy(newPath, fPath);
+ }
char* addPoint = strrchr(newPath,'/');
if ( addPoint != NULL )
strcpy(&addPoint[1], &dependentPath[13]);
else if ( strncmp(dependentPath, "@rpath/", 7) == 0 ) {
throw "@rpath not supported in dyld shared cache";
}
- fDependsOn.insert(fGraph->getNodeForVirtualPath(dependentPath));
+ // <rdar://problem/9161945> silently ignore dependents from main executables that can't be in shared cache
+ bool addDependent = true;
+ if ( fLayout->getFileType() == MH_EXECUTE ) {
+ if ( (strncmp(dependentPath, "/usr/lib/", 9) != 0) && (strncmp(dependentPath, "/System/Library/", 16) != 0) ) {
+ addDependent = false;
+ }
+ }
+ if ( addDependent )
+ fDependsOn.insert(fGraph->getNodeForVirtualPath(dependentPath));
}
catch (const char* msg) {
if ( it->weakImport && ! fLayout->hasSplitSegInfo() ) {
{
const PathToNode& nodes = fgPerArchGraph[ap]->fNodes;
std::set<const MachOLayoutAbstraction*> possibleLibs;
- //fprintf(stderr, "shared for arch 0x%08X\n", arch);
+ //fprintf(stderr, "shared for arch %s\n", archName(ap));
for(PathToNode::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
DependencyNode* node = it->second;
// <rdar://problem/6127437> put all dylibs in shared cache - not just ones used by more than one app
if ( node->allDependentsFound() /*&& (node->useCount() > 1)*/ ) {
const MachOLayoutAbstraction* layout = node->getLayout();
- if ( layout->hasSplitSegInfo() && layout->isRootOwned() && layout->inSharableLocation() )
- possibleLibs.insert(layout);
- //fprintf(stderr, "\t%s\n", it->first);
+ if ( layout->isDylib() ) {
+ char* msg;
+ if ( sharable(layout, ap, &msg) ) {
+ possibleLibs.insert(layout);
+ }
+ else {
+ if ( layout->getID().name[0] == '@' ) {
+ // <rdar://problem/7770139> update_dyld_shared_cache should suppress warnings for embedded frameworks
+ }
+ else {
+ warnings.push_back(msg);
+ fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg);
+ }
+ }
+ }
}
}
}
}
+bool ArchGraph::sharable(const MachOLayoutAbstraction* layout, ArchPair ap, char** msg)
+{
+ if ( ! layout->isTwoLevelNamespace() )
+ asprintf(msg, "can't put %s in shared cache because it was built -flat_namespace", layout->getID().name);
+ else if ( ! layout->hasSplitSegInfo() )
+ asprintf(msg, "can't put %s in shared cache because it was not built for %s or later", layout->getID().name, (iPhoneOS ? "iPhoneOS 3.1" : "MacOSX 10.5"));
+ else if ( ! layout->isRootOwned() )
+ asprintf(msg, "can't put %s in shared cache because it is not owned by root", layout->getID().name);
+ else if ( ! layout->inSharableLocation() )
+ asprintf(msg, "can't put %s in shared cache because it is not in /usr/lib or /System/Library", layout->getID().name);
+ else if ( layout->hasDynamicLookupLinkage() )
+ asprintf(msg, "can't put %s in shared cache because it was built with '-undefined dynamic_lookup'", layout->getID().name);
+ else if ( layout->hasMainExecutableLookupLinkage() )
+ asprintf(msg, "can't put %s in shared cache because it was built with '-bundle_loader'", layout->getID().name);
+ //else if ( ! layout->hasDyldInfo() )
+ // asprintf(msg, "can't put %s in shared cache because it was built for older OS", layout->getID().name);
+ else
+ return true;
+ return false;
+}
+
bool ArchGraph::canBeShared(const MachOLayoutAbstraction* layout, ArchPair ap, const std::set<const MachOLayoutAbstraction*>& possibleLibs, std::map<const MachOLayoutAbstraction*, bool>& shareableMap)
{
// check map which is a cache of results
if ( possibleLibs.count(layout) == 0 ) {
shareableMap[layout] = false;
char* msg;
- if ( ! layout->hasSplitSegInfo() )
- asprintf(&msg, "can't put %s in shared cache because it was not built for 10.5 or later", layout->getID().name);
- else if ( ! layout->isRootOwned() )
- asprintf(&msg, "can't put %s in shared cache because it is not owned by root", layout->getID().name);
- else if ( ! layout->inSharableLocation() )
- asprintf(&msg, "can't put %s in shared cache because it is not in /usr/lib or /System/Library", layout->getID().name);
- else
- asprintf(&msg, "can't put %s in shared cache", layout->getID().name);
+ if ( sharable(layout, ap, &msg) )
+ asprintf(&msg, "can't put %s in shared cache, unknown reason", layout->getID().name);
warnings.push_back(msg);
if ( verbose )
fprintf(stderr, "update_dyld_shared_cache: for arch %s, %s\n", archName(ap), msg);
class SharedCache
{
public:
- SharedCache(ArchGraph* graph, const char* rootPath, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress);
+ SharedCache(ArchGraph* graph, const char* rootPath, const char* cacheDir, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress);
bool update(bool usesOverlay, bool force, bool optimize, bool deleteExistingFirst, int archIndex,
int archCount, bool keepSignatures);
static const char* cacheFileSuffix(bool optimized, const char* archName);
- uint64_t mappedCacheAddressForAddress(uint64_t addr);
+ // vm address = address AS WRITTEN into the cache
+ // mapped address = address AS MAPPED into the update process only
+ // file offset = offset relative to start of cache file
+ void * mappedAddressForVMAddress(uint64_t vmaddr);
+ uint64_t VMAddressForMappedAddress(const void *mapaddr);
+ uint64_t cacheFileOffsetForVMAddress(uint64_t addr) const;
+ uint64_t VMAddressForCacheFileOffset(uint64_t addr) const;
private:
typedef typename A::P P;
typedef typename A::P::E E;
typedef typename A::P::uint_t pint_t;
- bool notUpToDate(const char* path);
- bool notUpToDate(const void* cache);
+ bool notUpToDate(const char* path, unsigned int aliasCount);
+ bool notUpToDate(const void* cache, unsigned int aliasCount);
uint8_t* optimizeLINKEDIT(bool keepSignatures);
- void optimizeObjC();
+ void optimizeObjC(std::vector<void*>& pointersInData);
static void getSharedCacheBasAddresses(cpu_type_t arch, uint64_t* baseReadOnly, uint64_t* baseWritable);
static cpu_type_t arch();
static uint64_t sharedRegionReadOnlySize();
static uint64_t sharedRegionWritableSize();
static uint64_t getWritableSegmentNewAddress(uint64_t proposedNewAddress, uint64_t originalAddress, uint64_t executableSlide);
+ static bool addCacheSlideInfo();
-
- void assignNewBaseAddresses();
- uint64_t cacheFileOffsetForAddress(uint64_t addr);
+ void assignNewBaseAddresses(bool verify);
struct LayoutInfo {
const MachOLayoutAbstraction* layout;
+ std::vector<const char*> aliases;
dyld_cache_image_info info;
};
// one image has no segments
return segs_l.size() > segs_r.size();
}
- const macho_header<P> *mh_l = (const macho_header<P>*)segs_l[0].mappedAddress();
- const macho_header<P> *mh_r = (const macho_header<P>*)segs_r[0].mappedAddress();
+ const macho_header<P> *mh_l = (macho_header<P>*)segs_l[0].mappedAddress();
+ const macho_header<P> *mh_r = (macho_header<P>*)segs_r[0].mappedAddress();
const macho_section<P> *cstring_l = mh_l->getSection("__TEXT", "__cstring");
const macho_section<P> *cstring_r = mh_r->getSection("__TEXT", "__cstring");
if (!cstring_l || !cstring_r) {
std::map<const MachOLayoutAbstraction*, uint32_t>& fMap;
};
-
+
ArchGraph* fArchGraph;
const bool fVerify;
bool fExistingIsNotUpToDate;
+ bool fCacheFileInFinalLocation;
const char* fCacheFilePath;
uint8_t* fExistingCacheForVerification;
std::vector<LayoutInfo> fDylibs;
+ std::vector<LayoutInfo> fDylibAliases;
std::vector<shared_file_mapping_np> fMappings;
uint32_t fHeaderSize;
uint8_t* fInMemoryCache;
uint32_t fOffsetOfLazyBindInfoInCombinedLinkedit;
uint32_t fOffsetOfExportInfoInCombinedLinkedit;
uint32_t fOffsetOfOldSymbolTableInfoInCombinedLinkedit;
+ uint32_t fSizeOfOldSymbolTableInfoInCombinedLinkedit;
+ uint32_t fOffsetOfOldExternalRelocationsInCombinedLinkedit;
+ uint32_t fSizeOfOldExternalRelocationsInCombinedLinkedit;
+ uint32_t fOffsetOfOldIndirectSymbolsInCombinedLinkedit;
+ uint32_t fSizeOfOldIndirectSymbolsInCombinedLinkedit;
+ uint32_t fOffsetOfOldStringPoolInCombinedLinkedit;
+ uint32_t fSizeOfOldStringPoolInCombinedLinkedit;
+ uint32_t fOffsetOfFunctionStartsInCombinedLinkedit;
+ uint32_t fSizeOfFunctionStartsInCombinedLinkedit;
uint32_t fLinkEditsTotalOptimizedSize;
};
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
- SharedCache<A>* const fCache;
- const macho_section<P>* const fSection;
- pint_t * const fBase;
- uint64_t const fCount;
+ SharedCache<A>* const fCache;
+ const macho_section<P>* const fSection;
+ pint_t * const fBase;
+ uint64_t fCount;
public:
PointerSection(SharedCache<A>* cache, const macho_header<P>* header,
const char *segname, const char *sectname)
: fCache(cache)
, fSection(header->getSection(segname, sectname))
- , fBase(fSection ? (pint_t *)cache->mappedCacheAddressForAddress(fSection->addr()) : 0)
+ , fBase(fSection ? (pint_t *)cache->mappedAddressForVMAddress(fSection->addr()) : 0)
, fCount(fSection ? fSection->size() / sizeof(pint_t) : 0)
{
}
}
T get(uint64_t index) const {
- return (T)fCache->mappedCacheAddressForAddress(getUnmapped(index));
+ return (T)fCache->mappedAddressForVMAddress(getUnmapped(index));
}
void set(uint64_t index, uint64_t value) {
if (index >= fCount) throwf("index out of range");
P::setP(fBase[index], value);
}
+
+ void removeNulls() {
+ uint64_t shift = 0;
+ for (uint64_t i = 0; i < fCount; i++) {
+ pint_t value = fBase[i];
+ if (value) {
+ fBase[i-shift] = value;
+ } else {
+ shift++;
+ }
+ }
+ fCount -= shift;
+ const_cast<macho_section<P>*>(fSection)->set_size(fCount * sizeof(pint_t));
+ }
};
// Access a section containing an array of structures
const char *segname, const char *sectname)
: fCache(cache)
, fSection(header->getSection(segname, sectname))
- , fBase(fSection ? (T *)cache->mappedCacheAddressForAddress(fSection->addr()) : 0)
+ , fBase(fSection ? (T *)cache->mappedAddressForVMAddress(fSection->addr()) : 0)
, fCount(fSection ? fSection->size() / sizeof(T) : 0)
{
}
template <> uint64_t SharedCache<arm>::sharedRegionReadOnlyStartAddress() { return 0x30000000; }
template <> uint64_t SharedCache<ppc>::sharedRegionWritableStartAddress() { return 0xA0000000; }
-template <> uint64_t SharedCache<x86>::sharedRegionWritableStartAddress() { return 0xA0000000; }
+template <> uint64_t SharedCache<x86>::sharedRegionWritableStartAddress() { return 0xAC000000; }
template <> uint64_t SharedCache<x86_64>::sharedRegionWritableStartAddress() { return 0x7FFF70000000LL; }
-template <> uint64_t SharedCache<arm>::sharedRegionWritableStartAddress() { return 0x38000000; }
+template <> uint64_t SharedCache<arm>::sharedRegionWritableStartAddress() { return 0x3E000000; }
template <> uint64_t SharedCache<ppc>::sharedRegionReadOnlySize() { return 0x10000000; }
-template <> uint64_t SharedCache<x86>::sharedRegionReadOnlySize() { return 0x10000000; }
-template <> uint64_t SharedCache<x86_64>::sharedRegionReadOnlySize() { return 0x7FE00000; }
-template <> uint64_t SharedCache<arm>::sharedRegionReadOnlySize() { return 0x08000000; }
+template <> uint64_t SharedCache<x86>::sharedRegionReadOnlySize() { return 0x1C000000; }
+template <> uint64_t SharedCache<x86_64>::sharedRegionReadOnlySize() { return 0x40000000; }
+template <> uint64_t SharedCache<arm>::sharedRegionReadOnlySize() { return 0x0E000000; }
template <> uint64_t SharedCache<ppc>::sharedRegionWritableSize() { return 0x10000000; }
-template <> uint64_t SharedCache<x86>::sharedRegionWritableSize() { return 0x10000000; }
-template <> uint64_t SharedCache<x86_64>::sharedRegionWritableSize() { return 0x20000000; }
-template <> uint64_t SharedCache<arm>::sharedRegionWritableSize() { return 0x08000000; }
+template <> uint64_t SharedCache<x86>::sharedRegionWritableSize() { return 0x04000000; }
+template <> uint64_t SharedCache<x86_64>::sharedRegionWritableSize() { return 0x10000000; }
+template <> uint64_t SharedCache<arm>::sharedRegionWritableSize() { return 0x02000000; }
template <> const char* SharedCache<ppc>::archName() { return "ppc"; }
template <> const char* SharedCache<arm>::cacheFileSuffix(bool, const char* archName) { return archName; }
template <typename A>
-SharedCache<A>::SharedCache(ArchGraph* graph, const char* rootPath, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress)
- : fArchGraph(graph), fVerify(verify), fExistingIsNotUpToDate(true), fCacheFilePath(NULL),
- fExistingCacheForVerification(NULL), fDyldBaseAddress(dyldBaseAddress)
+SharedCache<A>::SharedCache(ArchGraph* graph, const char* rootPath, const char* cacheDir, bool alphaSort, bool verify, bool optimize, bool overlay, uint64_t dyldBaseAddress)
+ : fArchGraph(graph), fVerify(verify), fExistingIsNotUpToDate(true),
+ fCacheFileInFinalLocation(rootPath[0] == '\0'), fCacheFilePath(NULL),
+ fExistingCacheForVerification(NULL), fDyldBaseAddress(dyldBaseAddress),
+ fOffsetOfBindInfoInCombinedLinkedit(0), fOffsetOfWeakBindInfoInCombinedLinkedit(0),
+ fOffsetOfLazyBindInfoInCombinedLinkedit(0), fOffsetOfExportInfoInCombinedLinkedit(0),
+ fOffsetOfOldSymbolTableInfoInCombinedLinkedit(0), fSizeOfOldSymbolTableInfoInCombinedLinkedit(0),
+ fOffsetOfOldExternalRelocationsInCombinedLinkedit(0), fSizeOfOldExternalRelocationsInCombinedLinkedit(0),
+ fOffsetOfOldIndirectSymbolsInCombinedLinkedit(0), fSizeOfOldIndirectSymbolsInCombinedLinkedit(0),
+ fOffsetOfOldStringPoolInCombinedLinkedit(0), fSizeOfOldStringPoolInCombinedLinkedit(0),
+ fOffsetOfFunctionStartsInCombinedLinkedit(0), fSizeOfFunctionStartsInCombinedLinkedit(0)
{
if ( fArchGraph->getArchPair().arch != arch() )
throwf("SharedCache object is wrong architecture: 0x%08X vs 0x%08X", fArchGraph->getArchPair().arch, arch());
// build vector of all shared dylibs
+ unsigned int aliasCount = 0;
std::set<const MachOLayoutAbstraction*>& dylibs = fArchGraph->getSharedDylibs();
+ ArchGraph::StringToString& aliases = fArchGraph->getDylibAliases();
for(std::set<const MachOLayoutAbstraction*>::iterator it = dylibs.begin(); it != dylibs.end(); ++it) {
const MachOLayoutAbstraction* lib = *it;
LayoutInfo temp;
temp.info.address = 0;
temp.info.modTime = lib->getLastModTime();
temp.info.inode = lib->getInode();
- temp.info.pathFileOffset = lib->getNameFileOffset();
+ temp.info.pathFileOffset = lib->getNameFileOffset(); // for now this is the offset within the dylib
+ for(ArchGraph::StringToString::iterator ait = aliases.begin(); ait != aliases.end(); ++ait) {
+ if ( strcmp(ait->second, lib->getID().name) == 0 ) {
+ temp.aliases.push_back(ait->first);
+ ++aliasCount;
+ }
+ }
fDylibs.push_back(temp);
}
- // examine the existing shared cache file
+ // create path to cache file
+ char cachePathNonOverlay[1024];
+ strcpy(cachePathNonOverlay, cacheDir);
+ if ( cachePathNonOverlay[strlen(cachePathNonOverlay)-1] != '/' )
+ strcat(cachePathNonOverlay, "/");
+ strcat(cachePathNonOverlay, DYLD_SHARED_CACHE_BASE_NAME);
+ strcat(cachePathNonOverlay, cacheFileSuffix(optimize, fArchGraph->archName()));
char cachePath[1024];
strcpy(cachePath, rootPath);
- strcat(cachePath, DYLD_SHARED_CACHE_DIR);
- strcat(cachePath, DYLD_SHARED_CACHE_BASE_NAME);
- strcat(cachePath, cacheFileSuffix(optimize, fArchGraph->archName()));
- fCacheFilePath = strdup(cachePath);
- const char* pathToExistingCacheFile = fCacheFilePath;
- char cachePathNonOverlay[1024];
+ strcat(cachePath, "/");
+ strcat(cachePath, cachePathNonOverlay);
+ if ( !overlay && (rootPath[0] != '\0') )
+ fCacheFilePath = strdup(cachePathNonOverlay);
+ else
+ fCacheFilePath = strdup(cachePath);
if ( overlay ) {
- strcpy(cachePathNonOverlay, DYLD_SHARED_CACHE_DIR);
- strcat(cachePathNonOverlay, DYLD_SHARED_CACHE_BASE_NAME);
- strcat(cachePathNonOverlay, cacheFileSuffix(optimize, fArchGraph->archName()));
- pathToExistingCacheFile = cachePathNonOverlay;
+ // in overlay mode if there already is a cache file in the overlay
+ // check if it is up to date. If there is no file, check if
+ // the one in the boot volume is up to date.
+ struct stat stat_buf;
+ if ( stat(fCacheFilePath, &stat_buf) == 0 )
+ fExistingIsNotUpToDate = this->notUpToDate(fCacheFilePath, aliasCount);
+ else
+ fExistingIsNotUpToDate = this->notUpToDate(cachePathNonOverlay, aliasCount);
+ }
+ else {
+ fExistingIsNotUpToDate = this->notUpToDate(fCacheFilePath, aliasCount);
}
- fExistingIsNotUpToDate = this->notUpToDate(pathToExistingCacheFile);
// sort shared dylibs
if ( verify ) {
}
// assign segments in each dylib a new address
- this->assignNewBaseAddresses();
+ this->assignNewBaseAddresses(verify);
+
+ // calculate where string pool offset will start
+ // calculate cache file header size
+ fHeaderSize = sizeof(dyld_cache_header)
+ + fMappings.size()*sizeof(shared_file_mapping_np)
+ + (fDylibs.size()+aliasCount)*sizeof(dyld_cache_image_info);
+ //fprintf(stderr, "aliasCount=%d, fHeaderSize=0x%08X\n", aliasCount, fHeaderSize);
+ // build list of aliases and compute where each ones path string will go
+ for(typename std::vector<struct LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
+ for(std::vector<const char*>::const_iterator ait = it->aliases.begin(); ait != it->aliases.end(); ++ait) {
+ LayoutInfo temp = *it;
+ // alias looks just like real dylib, but has a different name string
+ const char* aliasPath = *ait;
+ temp.aliases.clear();
+ temp.aliases.push_back(aliasPath);
+ temp.info.pathFileOffset = fHeaderSize;
+ fDylibAliases.push_back(temp);
+ fHeaderSize += strlen(aliasPath)+1;
+ }
+ }
+ std::sort(fDylibAliases.begin(), fDylibAliases.end(), ByNameSorter());
+ //fprintf(stderr, "fHeaderSize=0x%08X, fDylibAliases.size()=%lu\n", fHeaderSize, fDylibAliases.size());
+ fHeaderSize = pageAlign(fHeaderSize);
// check that cache we are about to create for verification purposes has same layout as existing cache
if ( verify ) {
}
}
- // calculate cache file header size
- fHeaderSize = pageAlign(sizeof(dyld_cache_header)
- + fMappings.size()*sizeof(shared_file_mapping_np)
- + fDylibs.size()*sizeof(dyld_cache_image_info) );
- //+ fDependencyPool.size()*sizeof(uint16_t));
- if ( fHeaderSize > 0x3000 )
+ if ( fHeaderSize > FIRST_DYLIB_TEXT_OFFSET )
throwf("header size miscalculation 0x%08X", fHeaderSize);
}
template <typename A>
-void SharedCache<A>::assignNewBaseAddresses()
+void SharedCache<A>::assignNewBaseAddresses(bool verify)
{
- // first layout TEXT and DATA for split-seg (or can be split-seg) dylibs
- uint64_t currentExecuteAddress = sharedRegionReadOnlyStartAddress() + 0x3000;
- uint64_t currentWritableAddress = sharedRegionWritableStartAddress();
+ uint64_t sharedCacheStartAddress = sharedRegionReadOnlyStartAddress();
+#if 0
+ if ( arch() == CPU_TYPE_X86_64 ) {
+ if ( verify ) {
+ if ( fExistingCacheForVerification == NULL ) {
+ throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n",
+ getpid(), archName());
+ }
+ const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)fExistingCacheForVerification;
+ const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)(fExistingCacheForVerification + header->mappingOffset());
+ sharedCacheStartAddress = mappings[0].address();
+ }
+ else {
+ // <rdar://problem/5274722> dyld shared cache can be more random
+ uint64_t readOnlySize = 0;
+ for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
+ if ( ! it->layout->hasSplitSegInfo() )
+ continue;
+ std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
+ for (int i=0; i < segs.size(); ++i) {
+ MachOLayoutAbstraction::Segment& seg = segs[i];
+ if ( ! seg.writable() )
+ readOnlySize += pageAlign(seg.size());
+ }
+ }
+ uint64_t maxSlide = sharedRegionReadOnlySize() - (readOnlySize + FIRST_DYLIB_TEXT_OFFSET);
+ sharedCacheStartAddress = sharedRegionReadOnlyStartAddress() + pageAlign(arc4random() % maxSlide);
+ }
+ }
+#endif
+ uint64_t currentExecuteAddress = sharedCacheStartAddress + FIRST_DYLIB_TEXT_OFFSET;
+ uint64_t currentWritableAddress = sharedRegionWritableStartAddress() + FIRST_DYLIB_DATA_OFFSET;
+
+ // first layout TEXT and DATA for dylibs
for(typename std::vector<LayoutInfo>::iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
std::vector<MachOLayoutAbstraction::Segment>& segs = ((MachOLayoutAbstraction*)(it->layout))->getSegments();
MachOLayoutAbstraction::Segment* executableSegment = NULL;
}
fLinkEditsTotalUnoptimizedSize = (currentReadOnlyAddress - fLinkEditsStartAddress + 4095) & (-4096);
+ // <rdar://problem/9361288> i386 dyld shared cache overflows after adding libclh.dylib
+ if ( (currentReadOnlyAddress - sharedRegionReadOnlyStartAddress()) > sharedRegionReadOnlySize() )
+ throwf("read-only slice of cache too big: %lluMB (max %lluMB)",
+ (currentReadOnlyAddress - sharedRegionReadOnlyStartAddress())/(1024*1024),
+ sharedRegionReadOnlySize()/(1024*1024));
+
// populate large mappings
uint64_t cacheFileOffset = 0;
- if ( currentExecuteAddress > sharedRegionReadOnlyStartAddress() + 0x3000 ) {
+ if ( currentExecuteAddress > sharedCacheStartAddress + FIRST_DYLIB_TEXT_OFFSET ) {
shared_file_mapping_np executeMapping;
- executeMapping.sfm_address = sharedRegionReadOnlyStartAddress();
- executeMapping.sfm_size = currentExecuteAddress - sharedRegionReadOnlyStartAddress();
+ executeMapping.sfm_address = sharedCacheStartAddress;
+ executeMapping.sfm_size = currentExecuteAddress - sharedCacheStartAddress;
executeMapping.sfm_file_offset = cacheFileOffset;
executeMapping.sfm_max_prot = VM_PROT_READ | VM_PROT_EXECUTE;
executeMapping.sfm_init_prot = VM_PROT_READ | VM_PROT_EXECUTE;
// empty cache
shared_file_mapping_np cacheHeaderMapping;
cacheHeaderMapping.sfm_address = sharedRegionWritableStartAddress();
- cacheHeaderMapping.sfm_size = 0x3000;
+ cacheHeaderMapping.sfm_size = FIRST_DYLIB_TEXT_OFFSET;
cacheHeaderMapping.sfm_file_offset = cacheFileOffset;
cacheHeaderMapping.sfm_max_prot = VM_PROT_READ;
cacheHeaderMapping.sfm_init_prot = VM_PROT_READ;
template <typename A>
-uint64_t SharedCache<A>::cacheFileOffsetForAddress(uint64_t addr)
+uint64_t SharedCache<A>::cacheFileOffsetForVMAddress(uint64_t vmaddr) const
{
- for(std::vector<shared_file_mapping_np>::iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
- if ( (it->sfm_address <= addr) && (addr < it->sfm_address+it->sfm_size) )
- return it->sfm_file_offset + addr - it->sfm_address;
+ for(std::vector<shared_file_mapping_np>::const_iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
+ if ( (it->sfm_address <= vmaddr) && (vmaddr < it->sfm_address+it->sfm_size) )
+ return it->sfm_file_offset + vmaddr - it->sfm_address;
}
- throwf("address 0x%0llX is not in cache", addr);
+ throwf("address 0x%0llX is not in cache", vmaddr);
+}
+
+template <typename A>
+uint64_t SharedCache<A>::VMAddressForCacheFileOffset(uint64_t offset) const
+{
+ for(std::vector<shared_file_mapping_np>::const_iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
+ if ( (it->sfm_file_offset <= offset) && (offset < it->sfm_file_offset+it->sfm_size) )
+ return it->sfm_address + offset - it->sfm_file_offset;
+ }
+ throwf("offset 0x%0llX is not in cache", offset);
}
+template <typename A>
+void *SharedCache<A>::mappedAddressForVMAddress(uint64_t vmaddr)
+{
+ if (!vmaddr) return NULL;
+ else return fInMemoryCache + cacheFileOffsetForVMAddress(vmaddr);
+}
template <typename A>
-uint64_t SharedCache<A>::mappedCacheAddressForAddress(uint64_t addr)
+uint64_t SharedCache<A>::VMAddressForMappedAddress(const void *mapaddr)
{
- if (!addr) return 0;
- else return (uint64_t)(fInMemoryCache + cacheFileOffsetForAddress(addr));
+ if (!mapaddr) return 0;
+ uint64_t offset = (uint8_t *)mapaddr - (uint8_t *)fInMemoryCache;
+ return VMAddressForCacheFileOffset(offset);
}
template <typename A>
-bool SharedCache<A>::notUpToDate(const void* cache)
+bool SharedCache<A>::notUpToDate(const void* cache, unsigned int aliasCount)
{
dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
// not valid if header signature is wrong
return false;
}
else {
- fprintf(stderr, "update_dyld_shared_cache[%u] current cache file has invalid header\n", getpid());
+ fprintf(stderr, "update_dyld_shared_cache[%u] updating cache because current cache file has invalid header\n", getpid());
return true;
}
}
// not valid if count of images does not match current images needed
- if ( header->imagesCount() != fDylibs.size() ) {
+ if ( header->imagesCount() != (fDylibs.size()+aliasCount) ) {
if ( fVerify ) {
fprintf(stderr, "update_dyld_shared_cache[%u] cannot verify %s because current cache file contains a different set of dylibs\n", getpid(), archName());
return false;
}
else {
- fprintf(stderr, "update_dyld_shared_cache[%u] current cache file is invalid because it contains a different set of dylibs\n", getpid());
+ fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because current cache file contains a different set of dylibs\n", getpid(), archName());
return true;
}
}
+ // get end of TEXT region
+ const dyldCacheFileMapping<E>* textMapping = (dyldCacheFileMapping<E>*)((uint8_t*)cache+sizeof(dyldCacheHeader<E>));
+ const uint32_t textSize = textMapping->size();
+
// verify every dylib in constructed graph is in existing cache with same inode and modTime
std::map<const MachOLayoutAbstraction*, uint32_t> sortingMap;
const dyldCacheImageInfo<E>* imagesStart = (dyldCacheImageInfo<E>*)((uint8_t*)cache + header->imagesOffset());
//fprintf(stderr, "inode=0x%llX, mTime=0x%llX, path=%s\n", it->info.inode, it->info.modTime, it->layout->getID().name);
for(const dyldCacheImageInfo<E>* cacheEntry = imagesStart; cacheEntry < imagesEnd; ++cacheEntry) {
if ( fVerify ) {
+ if ( cacheEntry->pathFileOffset() > textSize ) {
+ throwf("update_dyld_shared_cache[%u]: for arch=%s, image entries corrupt, bad path offset in %s\n",
+ getpid(), archName(), it->layout->getID().name);
+ }
// in -verify mode, just match by path and warn if file looks different
if ( strcmp((char*)cache+cacheEntry->pathFileOffset(), it->layout->getID().name) == 0 ) {
found = true;
}
}
else {
+ if ( cacheEntry->pathFileOffset() > textSize ) {
+ // cache corrupt, needs to be regenerated
+ return true;
+ }
// in normal update mode, everything has to match for cache to be up-to-date
if ( (cacheEntry->inode() == it->info.inode)
&& (cacheEntry->modTime() == it->info.modTime)
throwf("update_dyld_shared_cache[%u] can't verify %s cache because %s is not in existing cache\n", getpid(), archName(), it->layout->getID().name);
}
else {
- fprintf(stderr, "update_dyld_shared_cache[%u] current %s cache file invalid because %s has changed\n", getpid(), archName(), it->layout->getID().name);
+ fprintf(stderr, "update_dyld_shared_cache[%u] updating %s cache because dylib at %s has changed\n", getpid(), archName(), it->layout->getID().name);
return true;
}
}
template <typename A>
-bool SharedCache<A>::notUpToDate(const char* path)
+bool SharedCache<A>::notUpToDate(const char* path, unsigned int aliasCount)
{
// mmap existing cache file
int fd = ::open(path, O_RDONLY);
return true;
struct stat stat_buf;
::fstat(fd, &stat_buf);
- uint8_t* mappingAddr = (uint8_t*)mmap(NULL, stat_buf.st_size, PROT_READ , MAP_FILE | MAP_PRIVATE, fd, 0);
+ uint32_t cacheFileSize = stat_buf.st_size;
+ uint32_t cacheAllocatedSize = (cacheFileSize + 4095) & (-4096);
+ uint8_t* mappingAddr = NULL;
+ if ( vm_allocate(mach_task_self(), (vm_address_t*)(&mappingAddr), cacheAllocatedSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS )
+ throwf("can't vm_allocate cache of size %u", cacheFileSize);
+ // <rdar://problem/8960832> update_dyld_shared_cache -verify finds differences
+ (void)fcntl(fd, F_NOCACHE, 1);
+ ssize_t readResult = pread(fd, mappingAddr, cacheFileSize, 0);
+ if ( readResult != cacheFileSize )
+ throw "can't read existing cache file";
::close(fd);
- if ( mappingAddr == (uint8_t*)(-1) )
- return true;
// validate it
- bool result = this->notUpToDate(mappingAddr);
+ bool result = this->notUpToDate(mappingAddr, aliasCount);
if ( fVerify ) {
// don't unmap yet, leave so it can be verified later
fExistingCacheForVerification = mappingAddr;
}
else {
// unmap
- ::munmap(mappingAddr, stat_buf.st_size);
+ vm_deallocate(mach_task_self(), (vm_address_t)mappingAddr, cacheAllocatedSize);
if ( verbose && !result )
fprintf(stderr, "update_dyld_shared_cache: %s is up-to-date\n", path);
}
class LinkEditOptimizer
{
public:
- LinkEditOptimizer(const MachOLayoutAbstraction&, uint8_t*, StringPool&);
+ LinkEditOptimizer(const MachOLayoutAbstraction&, const SharedCache<A>&, uint8_t*, StringPool&);
virtual ~LinkEditOptimizer() {}
void copyBindInfo(uint32_t&);
void copyImportedSymbols(uint32_t symbolTableOffset, uint32_t&);
void copyExternalRelocations(uint32_t& offset);
void copyIndirectSymbolTable(uint32_t& offset);
+ void copyFunctionStarts(uint32_t& offset);
void updateLoadCommands(uint64_t newVMAddress, uint64_t size, uint32_t stringPoolOffset,
uint32_t linkEditsFileOffset, bool keepSignatures);
private:
+ const SharedCache<A>& fSharedCache;
const macho_header<P>* fHeader;
uint8_t* fNewLinkEditStart;
uint8_t* fLinkEditBase;
const MachOLayoutAbstraction& fLayout;
macho_dyld_info_command<P>* fDyldInfo;
macho_dysymtab_command<P>* fDynamicSymbolTable;
+ macho_linkedit_data_command<P>* fFunctionStarts;
macho_symtab_command<P>* fSymbolTableLoadCommand;
const macho_nlist<P>* fSymbolTable;
const char* fStrings;
uint32_t fImportedSymbolsCountInNewLinkEdit;
uint32_t fExternalRelocationsOffsetIntoNewLinkEdit;
uint32_t fIndirectSymbolTableOffsetInfoNewLinkEdit;
+ uint32_t fFunctionStartsOffsetInNewLinkEdit;
};
template <typename A>
-LinkEditOptimizer<A>::LinkEditOptimizer(const MachOLayoutAbstraction& layout, uint8_t* newLinkEdit, StringPool& stringPool)
- : fLayout(layout), fLinkEditBase(NULL), fNewLinkEditStart(newLinkEdit), fDyldInfo(NULL),
- fDynamicSymbolTable(NULL), fSymbolTableLoadCommand(NULL), fSymbolTable(NULL), fStrings(NULL), fNewStringPool(stringPool),
+LinkEditOptimizer<A>::LinkEditOptimizer(const MachOLayoutAbstraction& layout, const SharedCache<A>& sharedCache, uint8_t* newLinkEdit, StringPool& stringPool)
+ : fSharedCache(sharedCache), fLayout(layout), fLinkEditBase(NULL), fNewLinkEditStart(newLinkEdit), fDyldInfo(NULL),
+ fDynamicSymbolTable(NULL), fFunctionStarts(NULL), fSymbolTableLoadCommand(NULL), fSymbolTable(NULL), fStrings(NULL), fNewStringPool(stringPool),
fBindInfoOffsetIntoNewLinkEdit(0), fBindInfoSizeInNewLinkEdit(0),
fWeakBindInfoOffsetIntoNewLinkEdit(0), fWeakBindInfoSizeInNewLinkEdit(0),
fLazyBindInfoOffsetIntoNewLinkEdit(0), fLazyBindInfoSizeInNewLinkEdit(0),
fLocalSymbolsStartIndexInNewLinkEdit(0), fLocalSymbolsCountInNewLinkEdit(0),
fExportedSymbolsStartIndexInNewLinkEdit(0), fExportedSymbolsCountInNewLinkEdit(0),
fImportSymbolsStartIndexInNewLinkEdit(0), fImportedSymbolsCountInNewLinkEdit(0),
- fExternalRelocationsOffsetIntoNewLinkEdit(0), fIndirectSymbolTableOffsetInfoNewLinkEdit(0)
+ fExternalRelocationsOffsetIntoNewLinkEdit(0), fIndirectSymbolTableOffsetInfoNewLinkEdit(0),
+ fFunctionStartsOffsetInNewLinkEdit(0)
{
fHeader = (const macho_header<P>*)fLayout.getSegments()[0].mappedAddress();
case LC_DYLD_INFO_ONLY:
fDyldInfo = (macho_dyld_info_command<P>*)cmd;
break;
+ case LC_FUNCTION_STARTS:
+ fFunctionStarts = (macho_linkedit_data_command<P>*)cmd;
+ break;
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
template <typename A>
void LinkEditOptimizer<A>::copyExportInfo(uint32_t& offset)
{
- if ( (fDyldInfo != NULL) && (fDyldInfo->export_off() != 0) ) {
+ if ( (fDyldInfo != NULL) && (fLayout.getDyldInfoExports() != NULL) ) {
fExportInfoOffsetIntoNewLinkEdit = offset;
fExportInfoSizeInNewLinkEdit = fDyldInfo->export_size();
- // warning, export_off is only 32-bits so if the trie grows it must be allocated with 32-bits of fLinkeditBase
- memcpy(fNewLinkEditStart+offset, fLinkEditBase+(int32_t)fDyldInfo->export_off(), fDyldInfo->export_size());
+ memcpy(fNewLinkEditStart+offset, fLayout.getDyldInfoExports(), fDyldInfo->export_size());
offset += fDyldInfo->export_size();
}
}
if ( (entry->n_type() & N_TYPE) == N_SECT ) {
macho_nlist<P>* newSymbolEntry = &newSymbolTableStart[symbolIndex];
*newSymbolEntry = *entry;
- newSymbolEntry->set_n_strx(fNewStringPool.add(&fStrings[entry->n_strx()]));
+ newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()]));
++symbolIndex;
}
}
&& (strncmp(&fStrings[entry->n_strx()], "$ld$", 4) != 0) ) {
macho_nlist<P>* newSymbolEntry = &newSymbolTableStart[symbolIndex];
*newSymbolEntry = *entry;
- newSymbolEntry->set_n_strx(fNewStringPool.add(&fStrings[entry->n_strx()]));
+ newSymbolEntry->set_n_strx(fNewStringPool.addUnique(&fStrings[entry->n_strx()]));
fOldToNewSymbolIndexes[oldIndex] = symbolIndex-fLocalSymbolsStartIndexInNewLinkEdit;
++symbolIndex;
}
}
}
+template <typename A>
+void LinkEditOptimizer<A>::copyFunctionStarts(uint32_t& offset)
+{
+ if ( fFunctionStarts != NULL ) {
+ fFunctionStartsOffsetInNewLinkEdit = offset;
+ memcpy(&fNewLinkEditStart[offset], &fLinkEditBase[fFunctionStarts->dataoff()], fFunctionStarts->datasize());
+ offset += fFunctionStarts->datasize();
+ }
+}
+
template <typename A>
void LinkEditOptimizer<A>::copyIndirectSymbolTable(uint32_t& offset)
{
seg->set_filesize(size);
seg->set_fileoff(linkEditsFileOffset);
}
+ // don't alter __TEXT until <rdar://problem/7022345> is fixed
+ else if ( strcmp(seg->segname(), "__TEXT") != 0 ) {
+ // update all other segments fileoff to be offset from start of cache file
+ pint_t oldFileOff = seg->fileoff();
+ seg->set_fileoff(fSharedCache.cacheFileOffsetForVMAddress(seg->vmaddr()));
+ pint_t fileOffsetDelta = seg->fileoff() - oldFileOff;
+ // update all sections in this segment
+ macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
+ macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
+ for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( sect->offset() != 0 )
+ sect->set_offset(sect->offset()+fileOffsetDelta);
+ }
+ }
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
fDynamicSymbolTable->set_locreloff(0);
fDynamicSymbolTable->set_nlocrel(0);
+ // update function starts
+ if ( fFunctionStarts != NULL ) {
+ fFunctionStarts->set_dataoff(linkEditsFileOffset+fFunctionStartsOffsetInNewLinkEdit);
+ }
+
// now remove load commands no longer needed
const macho_load_command<P>* srcCmd = cmds;
macho_load_command<P>* dstCmd = (macho_load_command<P>*)cmds;
// create optimizer object for each LINKEDIT segment
std::vector<LinkEditOptimizer<A>*> optimizers;
for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- optimizers.push_back(new LinkEditOptimizer<A>(*it->layout, newLinkEdit, stringPool));
+ optimizers.push_back(new LinkEditOptimizer<A>(*it->layout, *this, newLinkEdit, stringPool));
}
// rebase info is not copied because images in shared cache are never rebased
(*it)->copyExportedSymbols(symbolTableOffset, symbolTableIndex);
(*it)->copyImportedSymbols(symbolTableOffset, symbolTableIndex);
}
+ fSizeOfOldSymbolTableInfoInCombinedLinkedit = symbolTableIndex * sizeof(macho_nlist<typename A::P>);
+ offset = symbolTableOffset + fSizeOfOldSymbolTableInfoInCombinedLinkedit & (-8);
// copy external relocations, 8-byte aligned after end of symbol table
- uint32_t externalRelocsOffset = symbolTableOffset + (symbolTableIndex * sizeof(macho_nlist<typename A::P>) + 7) & (-8);
- //uint32_t externalRelocsStartOffset = externalRelocsOffset;
+ fOffsetOfOldExternalRelocationsInCombinedLinkedit = offset;
for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyExternalRelocations(externalRelocsOffset);
+ (*it)->copyExternalRelocations(offset);
}
+ fSizeOfOldExternalRelocationsInCombinedLinkedit = offset - fOffsetOfOldExternalRelocationsInCombinedLinkedit;
+ // copy function starts
+ fOffsetOfFunctionStartsInCombinedLinkedit = offset;
+ for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
+ (*it)->copyFunctionStarts(offset);
+ }
+ fSizeOfFunctionStartsInCombinedLinkedit = offset - fOffsetOfFunctionStartsInCombinedLinkedit;
+
// copy indirect symbol tables
- uint32_t indirectSymbolTableOffset = externalRelocsOffset;
+ fOffsetOfOldIndirectSymbolsInCombinedLinkedit = offset;
for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->copyIndirectSymbolTable(indirectSymbolTableOffset);
+ (*it)->copyIndirectSymbolTable(offset);
}
-
+ fSizeOfOldIndirectSymbolsInCombinedLinkedit = offset - fOffsetOfOldIndirectSymbolsInCombinedLinkedit;
+
// copy string pool
- uint32_t stringPoolOffset = indirectSymbolTableOffset;
- memcpy(&newLinkEdit[stringPoolOffset], stringPool.getBuffer(), stringPool.size());
+ fOffsetOfOldStringPoolInCombinedLinkedit = offset;
+ memcpy(&newLinkEdit[offset], stringPool.getBuffer(), stringPool.size());
+ fSizeOfOldStringPoolInCombinedLinkedit = stringPool.size();
- // find new size
- fLinkEditsTotalOptimizedSize = (stringPoolOffset + stringPool.size() + 4095) & (-4096);
-
- // choose new linkedit file offset
- uint32_t linkEditsFileOffset = fLinkEditsStartAddress - sharedRegionReadOnlyStartAddress();
+ // total new size round up to page size
+ fLinkEditsTotalOptimizedSize = (fOffsetOfOldStringPoolInCombinedLinkedit + fSizeOfOldStringPoolInCombinedLinkedit + 4095) & (-4096);
+ // choose new linkedit file offset
+ uint32_t linkEditsFileOffset = cacheFileOffsetForVMAddress(fLinkEditsStartAddress);
+// uint32_t linkEditsFileOffset = fLinkEditsStartAddress - sharedRegionReadOnlyStartAddress();
+
// update load commands so that all dylibs shared different areas of the same LINKEDIT segment
for(typename std::vector<LinkEditOptimizer<A>*>::iterator it = optimizers.begin(); it != optimizers.end(); ++it) {
- (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalUnoptimizedSize, stringPoolOffset, linkEditsFileOffset, keepSignatures);
+ (*it)->updateLoadCommands(fLinkEditsStartAddress, fLinkEditsTotalUnoptimizedSize, fOffsetOfOldStringPoolInCombinedLinkedit, linkEditsFileOffset, keepSignatures);
}
//fprintf(stderr, "fLinkEditsTotalUnoptimizedSize=%llu, fLinkEditsTotalOptimizedSize=%u\n", fLinkEditsTotalUnoptimizedSize, fLinkEditsTotalOptimizedSize);
class ObjCSelectorUniquer
{
private:
- objc_selopt::string_map fSelectorStrings;
+ objc_opt::string_map fSelectorStrings;
SharedCache<A> *fCache;
size_t fCount;
{
fCount++;
const char *s = (const char *)
- fCache->mappedCacheAddressForAddress(oldValue);
- objc_selopt::string_map::iterator element =
- fSelectorStrings.insert(objc_selopt::string_map::value_type(s, oldValue)).first;
+ fCache->mappedAddressForVMAddress(oldValue);
+ objc_opt::string_map::iterator element =
+ fSelectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first;
return (typename A::P::uint_t)element->second;
}
- objc_selopt::string_map& strings() {
+ objc_opt::string_map& strings() {
return fSelectorStrings;
}
size_t count() const { return fCount; }
};
-template <>
-void SharedCache<arm>::optimizeObjC()
-{
- // objc optimizations on arm not yet supported
-}
-
template <typename A>
-void SharedCache<A>::optimizeObjC()
+void SharedCache<A>::optimizeObjC(std::vector<void*>& pointersInData)
{
+ const char *err;
+ size_t headerSize = sizeof(objc_opt::objc_opt_t);
+
if ( verbose ) {
- fprintf(stderr, "update_dyld_shared_cache: for %s, uniquing objc selectors\n", archName());
+ fprintf(stderr, "update_dyld_shared_cache: for %s, optimizing objc metadata\n", archName());
}
- // Find libobjc's __TEXT,__objc_selopt section
- const macho_section<P> *seloptSection = NULL;
+ // Find libobjc's empty sections to fill in
+ const macho_section<P> *optROSection = NULL;
+ const macho_section<P> *optRWSection = NULL;
for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
- if (0 == strstr(it->layout->getFilePath(), "libobjc")) continue;
- const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
- if ((seloptSection = mh->getSection("__TEXT", "__objc_selopt"))) break;
+ if ( strstr(it->layout->getFilePath(), "libobjc") != NULL ) {
+ const macho_header<P>* mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
+ optROSection = mh->getSection("__TEXT", "__objc_opt_ro");
+ // __objc_selopt is old name for __objc_opt_ro
+ if ( optROSection == NULL )
+ optROSection = mh->getSection("__TEXT", "__objc_selopt");
+ optRWSection = mh->getSection("__DATA", "__objc_opt_rw");
+ break;
+ }
}
- if (!seloptSection) {
- warn(archName(), "couldn't find libobjc's unique selector section (selectors not optimized)");
- return;
- }
-
- objc_selopt::objc_selopt_t *seloptData = (objc_selopt::objc_selopt_t *)
- mappedCacheAddressForAddress(seloptSection->addr());
- if (seloptSection->size() < sizeof(seloptData->version)) {
- warn(archName(), "libobjc's unique selector section is too small (selectors not optimized)");
- return;
- }
-
- if (E::get32(seloptData->version) != objc_selopt::VERSION) {
- warn(archName(), "libobjc's unique selector section version is unrecognized (selectors not optimized)");
- return;
- }
+ if ( optROSection == NULL ) {
+ warn(archName(), "libobjc's read-only section missing (metadata not optimized)");
+ return;
+ }
+
+ objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t*)mappedAddressForVMAddress(optROSection->addr());
+ if (optROSection->size() < headerSize) {
+ warn(archName(), "libobjc's read-only section is too small (metadata not optimized)");
+ return;
+ }
+ if (E::get32(optROHeader->version) != objc_opt::VERSION) {
+ warn(archName(), "libobjc's read-only section version is unrecognized (metadata not optimized)");
+ return;
+ }
// Update selector references and build selector list
- ObjCSelectorUniquer<A> uniq(this);
+
+ // This is SAFE: if we run out of room for the selector table,
+ // the modified binaries are still usable.
// Heuristic: choose selectors from libraries with more cstring data first.
// This tries to localize selector cstring memory.
+ ObjCSelectorUniquer<A> uniq(this);
std::vector<LayoutInfo> sortedDylibs = fDylibs;
std::sort(sortedDylibs.begin(), sortedDylibs.end(), ByCStringSectionSizeSorter());
+ SelectorOptimizer<A, ObjCSelectorUniquer<A> > selOptimizer(uniq);
for(typename std::vector<LayoutInfo>::const_iterator it = sortedDylibs.begin(); it != sortedDylibs.end(); ++it) {
const macho_header<P> *mh = (const macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
LegacySelectorUpdater<A, ObjCSelectorUniquer<A> >::update(this, mh, uniq);
- SelectorUpdater<A, ObjCSelectorUniquer<A> >::update(this, mh, uniq);
+ selOptimizer.optimize(this, mh);
}
if ( verbose ) {
fprintf(stderr, "update_dyld_shared_cache: for %s, found %zu unique objc selectors\n", archName(), uniq.strings().size());
}
- // Write selector hash table to libobjc's __TEXT,__objc_selopt section
- size_t bytesUsed;
- const char *err =
- objc_selopt::write_selopt(seloptData, seloptSection->addr(),
- seloptSection->size(), uniq.strings(),
- E::little_endian, &bytesUsed);
+ // Write selector table in read-only data.
+ size_t selTableOffset = P::round_up(headerSize);
+ size_t selTableSize;
+ objc_opt::objc_selopt_t *seloptData = (objc_opt::objc_selopt_t *)
+ mappedAddressForVMAddress(optROSection->addr() + selTableOffset);
+ err = objc_opt::write_selopt(seloptData,
+ optROSection->addr() + selTableOffset,
+ optROSection->size() - selTableOffset,
+ uniq.strings(),
+ E::little_endian, &selTableSize);
if (err) {
warn(archName(), err);
return;
}
if ( verbose ) {
+ size_t totalSize = headerSize + selTableSize;
fprintf(stderr, "update_dyld_shared_cache: for %s, %zu/%llu bytes "
- "(%d%%) used in libobjc unique selector section\n",
- archName(), bytesUsed, seloptSection->size(),
- (int)(bytesUsed / (double)seloptSection->size() * 100));
+ "(%d%%) used in libobjc read-only optimization section\n",
+ archName(), totalSize, optROSection->size(),
+ (int)(totalSize / (double)optROSection->size() * 100));
fprintf(stderr, "update_dyld_shared_cache: for %s, "
"updated %zu selector references\n",
archName(), uniq.count());
+ fprintf(stderr, "update_dyld_shared_cache: for %s, "
+ "wrote objc metadata optimization version %d\n",
+ archName(), objc_opt::VERSION);
}
+ // if r/w section exists in libojc attempt to optimize categories into classes
+ if ( optRWSection != NULL ) {
+ // Attach categories to classes in the same framework.
+ // Build aggregated (but unsorted) method lists in read-write data.
+
+ // This is SAFE: if we run out of room while attaching categories in
+ // a binary then previously-edited binaries are still valid. (This assumes
+ // each binary is processed all-or-nothing, which CategoryAttacher does.)
+ // This must be done AFTER uniquing selectors.
+ // This must be done BEFORE sorting method lists.
+
+ size_t categoryOffset = 0;
+ uint8_t *categoryData = (uint8_t*)mappedAddressForVMAddress(optRWSection->addr() + categoryOffset);
+ CategoryAttacher<A> categoryAttacher(categoryData, optRWSection->size() - categoryOffset);
+ for(typename std::vector<LayoutInfo>::const_iterator it = sortedDylibs.begin(); it != sortedDylibs.end(); ++it) {
+ macho_header<P> *mh = (macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
+ err = categoryAttacher.optimize(this, mh, pointersInData);
+ if (err) {
+ warn(archName(), err);
+ return;
+ }
+ }
+ size_t categorySize = categoryAttacher.bytesUsed();
+
+
+ // Sort method lists.
+
+ // This is SAFE: modified binaries are still usable as unsorted lists.
+ // This must be done AFTER uniquing selectors.
+ // This must be done AFTER attaching categories.
+
+ MethodListSorter<A> methodSorter;
+ for(typename std::vector<LayoutInfo>::const_iterator it = sortedDylibs.begin(); it != sortedDylibs.end(); ++it) {
+ macho_header<P> *mh = (macho_header<P>*)(*it->layout).getSegments()[0].mappedAddress();
+ methodSorter.optimize(this, mh);
+ }
+
+ if ( verbose ) {
+ size_t totalRWSize = categorySize;
+ fprintf(stderr, "update_dyld_shared_cache: for %s, %zu/%llu bytes "
+ "(%d%%) used in libobjc read-write optimization section\n",
+ archName(), totalRWSize, optRWSection->size(),
+ (int)(totalRWSize / (double)optRWSection->size() * 100));
+ fprintf(stderr, "update_dyld_shared_cache: for %s, "
+ "attached %zu categories (%zd bytes used)\n",
+ archName(), categoryAttacher.count(),
+ categoryAttacher.bytesUsed());
+ }
+ }
+
+ // Success. Update RO header last
+ E::set32(optROHeader->selopt_offset, headerSize);
+
return;
}
}
+
+template <> bool SharedCache<x86_64>::addCacheSlideInfo(){ return true; }
+template <> bool SharedCache<arm>::addCacheSlideInfo() { return true; }
+template <> bool SharedCache<x86>::addCacheSlideInfo() { return false; }
+template <> bool SharedCache<ppc>::addCacheSlideInfo() { return false; }
+
+
+
template <typename A>
bool SharedCache<A>::update(bool usesOverlay, bool force, bool optimize, bool deleteExistingFirst, int archIndex,
int archCount, bool keepSignatures)
uint32_t cacheFileSize = 0;
for(std::vector<shared_file_mapping_np>::iterator it = fMappings.begin(); it != fMappings.end(); ++it) {
uint32_t end = it->sfm_file_offset + it->sfm_size;
- if ( end > cacheFileSize )
+ if ( end > cacheFileSize )
cacheFileSize = end;
}
if ( vm_allocate(mach_task_self(), (vm_address_t*)(&inMemoryCache), cacheFileSize, VM_FLAGS_ANYWHERE) != KERN_SUCCESS )
header->set_mappingOffset(sizeof(dyldCacheHeader<E>));
header->set_mappingCount(fMappings.size());
header->set_imagesOffset(header->mappingOffset() + fMappings.size()*sizeof(dyldCacheFileMapping<E>));
- header->set_imagesCount(fDylibs.size());
+ header->set_imagesCount(fDylibs.size()+fDylibAliases.size());
header->set_dyldBaseAddress(fDyldBaseAddress);
- //header->set_dependenciesOffset(sizeof(dyldCacheHeader<E>) + fMappings.size()*sizeof(dyldCacheFileMapping<E>) + fDylibs.size()*sizeof(dyldCacheImageInfo<E>));
- //header->set_dependenciesCount(fDependencyPool.size());
+ header->set_codeSignatureOffset(cacheFileSize);
+ header->set_codeSignatureSize(0);
+ header->set_slideInfoOffset(0);
+ header->set_slideInfoSize(0);
// fill in mappings
dyldCacheFileMapping<E>* mapping = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
image->set_address(it->info.address);
image->set_modTime(it->info.modTime);
image->set_inode(it->info.inode);
- image->set_pathFileOffset(cacheFileOffsetForAddress(it->info.address+it->info.pathFileOffset));
- //image->set_dependenciesStartOffset(it->info.dependenciesStartOffset);
+ image->set_pathFileOffset(cacheFileOffsetForVMAddress(it->info.address+it->info.pathFileOffset));
+ ++image;
+ }
+
+ // add aliases to end of image table
+ for(typename std::vector<LayoutInfo>::iterator it = fDylibAliases.begin(); it != fDylibAliases.end(); ++it) {
+ image->set_address(it->info.address);
+ image->set_modTime(it->info.modTime);
+ image->set_inode(it->info.inode);
+ image->set_pathFileOffset(it->info.pathFileOffset);
+ strcpy((char*)inMemoryCache+it->info.pathFileOffset, it->aliases[0]);
+ //fprintf(stderr, "adding alias to offset 0x%08X %s\n", it->info.pathFileOffset, it->aliases[0]);
++image;
}
if ( seg.size() > 0 ) {
const uint64_t segmentSrcStartOffset = it->layout->getOffsetInUniversalFile()+seg.fileOffset();
const uint64_t segmentSize = seg.fileSize();
- const uint64_t segmentDstStartOffset = cacheFileOffsetForAddress(seg.newAddress());
+ const uint64_t segmentDstStartOffset = cacheFileOffsetForVMAddress(seg.newAddress());
ssize_t readResult = ::pread(src, &inMemoryCache[segmentDstStartOffset], segmentSize, segmentSrcStartOffset);
if ( readResult != segmentSize ) {
if ( readResult == -1 )
for (int i=0; i < segs.size(); ++i) {
MachOLayoutAbstraction::Segment& seg = segs[i];
if ( seg.size() > 0 )
- seg.setMappedAddress(inMemoryCache + cacheFileOffsetForAddress(seg.newAddress()));
+ seg.setMappedAddress(inMemoryCache + cacheFileOffsetForVMAddress(seg.newAddress()));
//fprintf(stderr, "%s at %p to %p for %s\n", seg.name(), seg.mappedAddress(), (char*)seg.mappedAddress()+ seg.size(), it->layout->getID().name);
}
}
+
+ // also construct list of all pointers in cache to other things in cache
+ std::vector<void*> pointersInData;
+ pointersInData.reserve(1024);
+
+ // add pointer in start of __DATA to start of __TEXT to remain compatible with previous dylds
+ pint_t* dataStartPtr = (pint_t*)(&inMemoryCache[fMappings[1].sfm_file_offset]);
+ P::setP(*dataStartPtr, fMappings[0].sfm_address);
// rebase each dylib in shared cache
for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
try {
Rebaser<A> r(*it->layout);
- r.rebase();
+ r.rebase(pointersInData);
//if ( verbose )
// fprintf(stderr, "update_dyld_shared_cache: for %s, rebasing dylib into cache for %s\n", archName(), it->layout->getID().name);
}
}
}
- // merge/optimize all LINKEDIT segments
- if ( optimize ) {
- //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024));
- cacheFileSize = (this->optimizeLINKEDIT(keepSignatures) - inMemoryCache);
- //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024));
- // update header to reduce mapping size
- dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
- dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
- dyldCacheFileMapping<E>* lastMapping = &mappings[cacheHeader->mappingCount()-1];
- lastMapping->set_size(cacheFileSize-lastMapping->file_offset());
- // update fMappings so .map file will print correctly
- fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset;
- }
-
if ( verbose )
fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information for %lu files:\n", archName(), fDylibs.size());
// instantiate a Binder for each image and add to map
if ( verbose )
fprintf(stderr, "update_dyld_shared_cache: for %s, updating binding information in cache for %s\n", archName(), (*it)->getDylibID());
try {
- (*it)->bind();
+ (*it)->bind(pointersInData);
+ }
+ catch (const char* msg) {
+ throwf("%s in %s", msg, (*it)->getDylibID());
+ }
+ }
+ // optimize binding
+ for(typename std::vector<Binder<A>*>::iterator it = binders.begin(); it != binders.end(); ++it) {
+ try {
+ (*it)->optimize();
}
catch (const char* msg) {
throwf("%s in %s", msg, (*it)->getDylibID());
delete *it;
}
+ // merge/optimize all LINKEDIT segments
+ if ( optimize ) {
+ //fprintf(stderr, "update_dyld_shared_cache: original cache file size %uMB\n", cacheFileSize/(1024*1024));
+ cacheFileSize = (this->optimizeLINKEDIT(keepSignatures) - inMemoryCache);
+ //fprintf(stderr, "update_dyld_shared_cache: optimized cache file size %uMB\n", cacheFileSize/(1024*1024));
+ // update header to reduce mapping size
+ dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
+ dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
+ dyldCacheFileMapping<E>* lastMapping = &mappings[cacheHeader->mappingCount()-1];
+ lastMapping->set_size(cacheFileSize-lastMapping->file_offset());
+ // update fMappings so .map file will print correctly
+ fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset;
+ // update header
+ //fprintf(stderr, "update_dyld_shared_cache: changing end of cache address from 0x%08llX to 0x%08llX\n",
+ // header->codeSignatureOffset(), fMappings.back().sfm_address + fMappings.back().sfm_size);
+ header->set_codeSignatureOffset(fMappings.back().sfm_file_offset + fMappings.back().sfm_size);
+ }
+
// unique objc selectors and update other objc metadata
- if (optimize) {
- optimizeObjC();
- }
- if ( progress ) {
- // assuming objc optimizations takes 15% of time
- fprintf(stdout, "%3u/%u\n", (archIndex+1)*55, archCount*100);
+ if ( optimize ) {
+ optimizeObjC(pointersInData);
+ if ( progress ) {
+ // assuming objc optimizations takes 15% of time
+ fprintf(stdout, "%3u/%u\n", (archIndex+1)*55, archCount*100);
+ }
}
+ if ( addCacheSlideInfo() ) {
+ // build bitmap of which pointers need sliding
+ uint8_t* const dataStart = &inMemoryCache[fMappings[1].sfm_file_offset]; // R/W mapping is always second
+ uint8_t* const dataEnd = &inMemoryCache[fMappings[1].sfm_file_offset+fMappings[1].sfm_size];
+ const int bitmapSize = (dataEnd - dataStart)/(4*8);
+ uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1);
+ void* lastPointer = inMemoryCache;
+ for(std::vector<void*>::iterator pit=pointersInData.begin(); pit != pointersInData.end(); ++pit) {
+ if ( *pit != lastPointer ) {
+ void* p = *pit;
+ if ( (p < dataStart) || ( p > dataEnd) )
+ throwf("DATA pointer for sliding, out of range 0x%08lX\n", (long)((uint8_t*)p-inMemoryCache));
+ long offset = (long)((uint8_t*)p - dataStart);
+ if ( (offset % 4) != 0 )
+ throwf("pointer not 4-byte aligned in DATA offset 0x%08lX\n", offset);
+ long byteIndex = offset / (4*8);
+ long bitInByte = (offset % 32) >> 2;
+ bitmap[byteIndex] |= (1 << bitInByte);
+ lastPointer = p;
+ }
+ }
+
+ // allocate worst case size block of all slide info
+ const int entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes.
+ const int toc_count = bitmapSize/entry_size;
+ int slideInfoSize = sizeof(dyldCacheSlideInfo<E>) + 2*toc_count + entry_size*(toc_count+1);
+ dyldCacheSlideInfo<E>* slideInfo = (dyldCacheSlideInfo<E>*)calloc(slideInfoSize, 1);
+ slideInfo->set_version(1);
+ slideInfo->set_toc_offset(sizeof(dyldCacheSlideInfo<E>));
+ slideInfo->set_toc_count(toc_count);
+ slideInfo->set_entries_offset((slideInfo->toc_offset()+2*toc_count+127)&(-128));
+ slideInfo->set_entries_count(0);
+ slideInfo->set_entries_size(entry_size);
+ // append each unique entry
+ const dyldCacheSlideInfoEntry* bitmapAsEntries = (dyldCacheSlideInfoEntry*)bitmap;
+ dyldCacheSlideInfoEntry* const entriesInSlidInfo = (dyldCacheSlideInfoEntry*)((char*)slideInfo+slideInfo->entries_offset());
+ int entry_count = 0;
+ for (int i=0; i < toc_count; ++i) {
+ const dyldCacheSlideInfoEntry* thisEntry = &bitmapAsEntries[i];
+ // see if it is same as one already added
+ bool found = false;
+ for (int j=0; j < entry_count; ++j) {
+ if ( memcmp(thisEntry, &entriesInSlidInfo[j], entry_size) == 0 ) {
+ //fprintf(stderr, "toc[%d] optimized to %d\n", i, j);
+ slideInfo->set_toc(i, j);
+ found = true;
+ break;
+ }
+ }
+ if ( ! found ) {
+ // append to end
+ memcpy(&entriesInSlidInfo[entry_count], thisEntry, entry_size);
+ slideInfo->set_toc(i, entry_count++);
+ }
+ }
+ slideInfo->set_entries_count(entry_count);
+
+ int slideInfoPageSize = (slideInfo->entries_offset() + entry_count*entry_size + 4095) & (-4096);
+ cacheFileSize += slideInfoPageSize;
+
+ // update mappings to increase RO size
+ dyldCacheHeader<E>* cacheHeader = (dyldCacheHeader<E>*)inMemoryCache;
+ dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&inMemoryCache[sizeof(dyldCacheHeader<E>)];
+ dyldCacheFileMapping<E>* lastMapping = &mappings[cacheHeader->mappingCount()-1];
+ lastMapping->set_size(lastMapping->size()+slideInfoPageSize);
+
+ // update header to show location of slidePointers
+ cacheHeader->set_slideInfoOffset(cacheHeader->codeSignatureOffset());
+ cacheHeader->set_slideInfoSize(slideInfoPageSize);
+ cacheHeader->set_codeSignatureOffset(cacheHeader->codeSignatureOffset()+slideInfoPageSize);
+
+ // update fMappings so .map file will print correctly
+ fMappings.back().sfm_size = cacheFileSize-fMappings.back().sfm_file_offset;
+
+ // copy compressed into into buffer
+ memcpy(&inMemoryCache[cacheHeader->slideInfoOffset()], slideInfo, slideInfoPageSize);
+ }
+
+
if ( fVerify ) {
+ // if no existing cache, say so
+ if ( fExistingCacheForVerification == NULL ) {
+ throwf("update_dyld_shared_cache[%u] for arch=%s, could not verify because cache file does not exist in /var/db/dyld/\n",
+ getpid(), archName());
+ }
// new cache is built, compare header entries
const dyldCacheHeader<E>* newHeader = (dyldCacheHeader<E>*)inMemoryCache;
const dyldCacheHeader<E>* oldHeader = (dyldCacheHeader<E>*)fExistingCacheForVerification;
if ( result != 0 )
fprintf(stderr, "update_dyld_shared_cache: warning, close() failed with errno=%d for %s\n", errno, tempCachePath);
- // atomically swap in new cache file, do this after F_FULLFSYNC
+ // <rdar://problem/7901042> Make life easier for the kernel at shutdown.
+ // If we just move the new cache file over the old, the old file
+ // may need to exist in the open-unlink state. But because it
+ // may be mapped into the shared region, it cannot be deleted
+ // until all user processes are terminated. That leaves are
+ // small to non-existent window for the kernel to delete the
+ // old cache file.
+ if ( fCacheFileInFinalLocation ) {
+ char tmpDirPath[64];
+ const char* pathLastSlash = strrchr(fCacheFilePath, '/');
+ if ( pathLastSlash != NULL ) {
+ sprintf(tmpDirPath, "/var/run%s.old.%u", pathLastSlash, getpid());
+ // move existing cache file to /var/run to be clean up next boot
+ result = ::rename(fCacheFilePath, tmpDirPath);
+ if ( result != 0 ) {
+ if ( errno != ENOENT )
+ fprintf(stderr, "update_dyld_shared_cache: warning, unable to move existing cache to %s errno=%d for %s\n", tmpDirPath, errno, fCacheFilePath);
+ }
+ }
+ }
+
+ // move new cache file to correct location for use after reboot
result = ::rename(tempCachePath, fCacheFilePath);
if ( result != 0 )
throwf("can't swap newly create dyld shared cache file: rename(%s,%s) returned errno=%d", tempCachePath, fCacheFilePath, errno);
-
+
+
// flush everything to disk to assure rename() gets recorded
::sync();
didUpdate = true;
(fOffsetOfOldSymbolTableInfoInCombinedLinkedit-fOffsetOfLazyBindInfoInCombinedLinkedit)/1024,
fLinkEditsStartAddress+fOffsetOfLazyBindInfoInCombinedLinkedit,
fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit);
- fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table info\n",
- (fLinkEditsTotalOptimizedSize-fOffsetOfOldSymbolTableInfoInCombinedLinkedit)/(1024*1024),
+ fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld symbol table size\n",
+ (fSizeOfOldSymbolTableInfoInCombinedLinkedit)/(1024*1024),
fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit,
- fLinkEditsStartAddress+fLinkEditsTotalOptimizedSize);
+ fLinkEditsStartAddress+fOffsetOfOldSymbolTableInfoInCombinedLinkedit+fSizeOfOldSymbolTableInfoInCombinedLinkedit);
+ if ( fSizeOfFunctionStartsInCombinedLinkedit != 0 )
+ fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld functions starts size\n",
+ fSizeOfFunctionStartsInCombinedLinkedit/1024,
+ fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit,
+ fLinkEditsStartAddress+fOffsetOfFunctionStartsInCombinedLinkedit+fSizeOfFunctionStartsInCombinedLinkedit);
+ if ( fSizeOfOldExternalRelocationsInCombinedLinkedit != 0 )
+ fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld external relocs size\n",
+ fSizeOfOldExternalRelocationsInCombinedLinkedit/1024,
+ fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit,
+ fLinkEditsStartAddress+fOffsetOfOldExternalRelocationsInCombinedLinkedit+fSizeOfOldExternalRelocationsInCombinedLinkedit);
+ fprintf(fmap, "linkedit %4uKB 0x%0llX -> 0x%0llX non-dyld indirect symbol table size\n",
+ fSizeOfOldIndirectSymbolsInCombinedLinkedit/1024,
+ fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit,
+ fLinkEditsStartAddress+fOffsetOfOldIndirectSymbolsInCombinedLinkedit+fSizeOfOldIndirectSymbolsInCombinedLinkedit);
+ fprintf(fmap, "linkedit %4uMB 0x%0llX -> 0x%0llX non-dyld string pool\n",
+ (fSizeOfOldStringPoolInCombinedLinkedit)/(1024*1024),
+ fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit,
+ fLinkEditsStartAddress+fOffsetOfOldStringPoolInCombinedLinkedit+fSizeOfOldStringPoolInCombinedLinkedit);
for(typename std::vector<LayoutInfo>::const_iterator it = fDylibs.begin(); it != fDylibs.end(); ++it) {
fprintf(fmap, "%s\n", it->layout->getID().name);
+ for (std::vector<const char*>::const_iterator ait = it->aliases.begin(); ait != it->aliases.end(); ++ait)
+ fprintf(fmap, "%s\n", *ait);
const std::vector<MachOLayoutAbstraction::Segment>& segs = it->layout->getSegments();
for (int i=0; i < segs.size(); ++i) {
const MachOLayoutAbstraction::Segment& seg = segs[i];
*last = '\0';
--last;
}
- paths.push_back(symbolStart);
+ // <rdar://problem/8305479> images in shared cache are bound against different IOKit than found at runtime
+ // HACK: Just ignore the known bad IOKit
+ if ( strcmp(symbolStart, "/System/Library/Frameworks/IOKit.framework/IOKit") == 0 ) {
+ fprintf(stderr, "update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n");
+ warnings.push_back("update_dyld_shared_cache: warning, ignoring /System/Library/Frameworks/IOKit.framework/IOKit\n");
+ }
+ else {
+ paths.push_back(symbolStart);
+ }
symbolStart = NULL;
state = lineStart;
}
if ( dir == NULL )
throwf("%s does not exist, errno=%d\n", dirOfPathFiles, errno);
for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) {
- if ( entry->d_type == DT_REG ) {
- // only look at files ending in .paths
+ if ( entry->d_type == DT_REG || entry->d_type == DT_UNKNOWN ) {
+ // only look at regular files ending in .paths
if ( strcmp(&entry->d_name[entry->d_namlen-6], ".paths") == 0 ) {
+ struct stat tmpStatPathsFile;
char fullPath[strlen(dirOfPathFiles)+entry->d_namlen+2];
strcpy(fullPath, dirOfPathFiles);
strcat(fullPath, "/");
strcat(fullPath, entry->d_name);
- parsePathsFile(fullPath, rootsPaths);
+ if ( lstat(fullPath, &tmpStatPathsFile) == -1 ) {
+ fprintf(stderr, "update_dyld_shared_cache: can't access %s\n", fullPath);
+ }
+ else if ( S_ISREG(tmpStatPathsFile.st_mode) ) {
+ parsePathsFile(fullPath, rootsPaths);
+ }
+ else {
+ fprintf(stderr, "update_dyld_shared_cache: wrong file type for %s\n", fullPath);
+ }
}
else {
fprintf(stderr, "update_dyld_shared_cache: warning, ignore file with wrong extension: %s\n", entry->d_name);
// creation time was before the last restart of this machine.
static void deleteOrphanTempCacheFiles()
{
- DIR* dir = ::opendir(DYLD_SHARED_CACHE_DIR);
+ DIR* dir = ::opendir(MACOSX_DYLD_SHARED_CACHE_DIR);
if ( dir != NULL ) {
std::vector<const char*> filesToDelete;
for (dirent* entry = ::readdir(dir); entry != NULL; entry = ::readdir(dir)) {
if ( entry->d_type == DT_REG ) {
// only look at files with .tmp in name
if ( strstr(entry->d_name, ".tmp") != NULL ) {
- char fullPath[strlen(DYLD_SHARED_CACHE_DIR)+entry->d_namlen+2];
- strcpy(fullPath, DYLD_SHARED_CACHE_DIR);
+ char fullPath[strlen(MACOSX_DYLD_SHARED_CACHE_DIR)+entry->d_namlen+2];
+ strcpy(fullPath, MACOSX_DYLD_SHARED_CACHE_DIR);
strcat(fullPath, "/");
strcat(fullPath, entry->d_name);
struct stat tmpFileStatInfo;
{
#if __i386__ || __x86_64__
// <rdar://problem/5217377> Rosetta does not work with optimized dyld shared cache
- SharedCache<ppc> cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, false, usesOverlay, dyldBaseAddress);
+ SharedCache<ppc> cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, false, usesOverlay, dyldBaseAddress);
didUpdate |= cache.update(usesOverlay, force, false, deleteExistingFirst, index, archCount, keepSignatures);
#else
- SharedCache<ppc> cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
+ SharedCache<ppc> cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures);
#endif
}
break;
case CPU_TYPE_I386:
{
- SharedCache<x86> cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
+ SharedCache<x86> cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures);
}
break;
case CPU_TYPE_X86_64:
{
- SharedCache<x86_64> cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
+ SharedCache<x86_64> cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures);
}
break;
case CPU_TYPE_ARM:
{
- SharedCache<arm> cache(ArchGraph::graphForArchPair(*a), rootPath, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
+ SharedCache<arm> cache(ArchGraph::graphForArchPair(*a), rootPath, cacheDir, alphaSort, verify, optimize, usesOverlay, dyldBaseAddress);
didUpdate |= cache.update(usesOverlay, force, optimize, deleteExistingFirst, index, archCount, keepSignatures);
}
break;
}
}
- deleteOrphanTempCacheFiles();
+ if ( !iPhoneOS )
+ deleteOrphanTempCacheFiles();
return didUpdate;
}
bool hasOverlay = false;
bool verify = false;
bool keepSignatures = false;
+ const char* cacheDir = NULL;
try {
// parse command line options
else if ( strcmp(arg, "-no_opt") == 0 ) {
optimize = false;
}
+ else if ( strcmp(arg, "-iPhone") == 0 ) {
+ iPhoneOS = true;
+ }
else if ( strcmp(arg, "-dylib_list") == 0 ) {
dylibListFile = argv[++i];
if ( dylibListFile == NULL )
throw "-dylib_list missing path argument";
- keepSignatures = true;
}
else if ( (strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0) ) {
if ( hasOverlay )
throw "-root missing path argument";
hasOverlay = true;
}
+ else if ( strcmp(arg, "-cache_dir") == 0 ) {
+ cacheDir = argv[++i];
+ if ( cacheDir == NULL )
+ throw "-cache_dir missing path argument";
+ }
else if ( strcmp(arg, "-arch") == 0 ) {
const char* arch = argv[++i];
if ( strcmp(arch, "ppc") == 0 )
}
// strip tailing slashes on -root or -overlay
+ // make it a real path so as to not make all dylibs look like symlink aliases
if ( rootPath[0] != '\0' ) {
- int len = strlen(rootPath)-1;
- if ( rootPath[len] == '/' ) {
- char* newRootPath = strdup(rootPath);
- while ( newRootPath[len] == '/' )
- newRootPath[len--] = '\0';
- rootPath = newRootPath;
- }
+ char realRootPath[MAXPATHLEN];
+ if ( realpath(rootPath, realRootPath) == NULL )
+ throwf("realpath() failed on %s\n", rootPath);
+ rootPath = strdup(realRootPath);
}
+ // set location to write cache dir
+ if ( cacheDir == NULL ) {
+ if ( (rootPath[0] == '\0') || hasOverlay ) {
+ cacheDir = (iPhoneOS ? IPHONE_DYLD_SHARED_CACHE_DIR : MACOSX_DYLD_SHARED_CACHE_DIR);
+ }
+ else {
+ asprintf((char**)&cacheDir, "%s/%s", rootPath, (iPhoneOS ? IPHONE_DYLD_SHARED_CACHE_DIR : MACOSX_DYLD_SHARED_CACHE_DIR));
+ }
+ }
+
// if no restrictions specified, use architectures that work on this machine
if ( onlyArchs.size() == 0 ) {
- int available;
- size_t len = sizeof(int);
- #if __i386__ || __x86_64__
- onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL));
- // check rosetta is installed
- char rosettaPath[1024];
- strlcpy(rosettaPath, rootPath, 1024);
- strlcat(rosettaPath, "/usr/libexec/oah/translate", 1024);
- struct stat stat_buf;
- if ( stat(rosettaPath, &stat_buf) == 0 ) {
- onlyArchs.insert(ArchPair(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL));
+ if ( iPhoneOS ) {
+ onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6));
+ onlyArchs.insert(ArchPair(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7));
}
- else if ( hasOverlay ) {
- // in overlay mode, rosetta may be installed on base system, but is not in update root
- if ( stat("/usr/libexec/oah/translate", &stat_buf) == 0 )
+ else {
+ int available;
+ size_t len = sizeof(int);
+ #if __i386__ || __x86_64__
+ onlyArchs.insert(ArchPair(CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL));
+ // check rosetta is installed
+ char rosettaPath[1024];
+ strlcpy(rosettaPath, rootPath, 1024);
+ strlcat(rosettaPath, "/usr/libexec/oah/translate", 1024);
+ struct stat stat_buf;
+ if ( stat(rosettaPath, &stat_buf) == 0 ) {
onlyArchs.insert(ArchPair(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL));
+ }
+ else if ( hasOverlay ) {
+ // in overlay mode, rosetta may be installed on base system, but is not in update root
+ if ( stat("/usr/libexec/oah/translate", &stat_buf) == 0 )
+ onlyArchs.insert(ArchPair(CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL));
+ }
+ // check system is capable of running 64-bit programs
+ if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available )
+ onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL));
+ #else
+ #error unsupported architecture
+ #endif
}
- // check system is capable of running 64-bit programs
- if ( (sysctlbyname("hw.optional.x86_64", &available, &len, NULL, 0) == 0) && available )
- onlyArchs.insert(ArchPair(CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL));
- #else
- #error unknown architecture
- #endif
}
if ( !verify && (geteuid() != 0) )
setSharedDylibs(rootPath, hasOverlay, dylibListFile, onlyArchs);
else
scanForSharedDylibs(rootPath, hasOverlay, "/var/db/dyld/shared_region_roots/", onlyArchs);
- updateSharedeCacheFile(rootPath, hasOverlay, DYLD_SHARED_CACHE_DIR, onlyArchs, force, alphaSort, optimize,
+ updateSharedeCacheFile(rootPath, hasOverlay, cacheDir, onlyArchs, force, alphaSort, optimize,
false, verify, keepSignatures);
}
catch (const char* msg) {
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
uint32_t ImageLoader::fgImagesUsedFromSharedCache = 0;
uint32_t ImageLoader::fgImagesWithUsedPrebinding = 0;
-uint32_t ImageLoader::fgImagesRequiringNoFixups = 0;
uint32_t ImageLoader::fgImagesRequiringCoalescing = 0;
uint32_t ImageLoader::fgImagesHasWeakDefinitions = 0;
uint32_t ImageLoader::fgTotalRebaseFixups = 0;
uint64_t ImageLoader::fgTotalDOF;
uint64_t ImageLoader::fgTotalInitTime;
uint16_t ImageLoader::fgLoadOrdinal = 0;
+std::vector<ImageLoader::InterposeTuple>ImageLoader::fgInterposingTuples;
uintptr_t ImageLoader::fgNextPIEDylibAddress = 0;
: fPath(path), fDevice(0), fInode(0), fLastModified(0),
fPathHash(0), fDlopenReferenceCount(0), fStaticReferenceCount(0),
fDynamicReferenceCount(0), fDynamicReferences(NULL), fInitializerRecursiveLock(NULL),
- fDepth(0), fLoadOrder(0), fState(0), fLibraryCount(libCount),
+ fDepth(0), fLoadOrder(fgLoadOrdinal++), fState(0), fLibraryCount(libCount),
fAllLibraryChecksumsAndLoadAddressesMatch(false), fLeaveMapped(false), fNeverUnload(false),
fHideSymbols(false), fMatchByInstallName(false),
- fRegisteredDOF(false), fAllLazyPointersBound(false), fBeingRemoved(false), fAddFuncNotified(false),
+ fInterposed(false), fRegisteredDOF(false), fAllLazyPointersBound(false),
+ fBeingRemoved(false), fAddFuncNotified(false),
fPathOwnedByImage(false), fWeakSymbolsBound(false)
{
if ( fPath != NULL )
{
// this cannot be done in destructor because libImage() is implemented
// in a subclass
+ DependentLibraryInfo libraryInfos[image->libraryCount()];
+ image->doGetDependentLibraries(libraryInfos);
for(unsigned int i=0; i < image->libraryCount(); ++i) {
ImageLoader* lib = image->libImage(i);
- if ( lib != NULL )
+ if ( (lib != NULL) && ! libraryInfos[i].upward )
lib->fStaticReferenceCount--;
}
delete image;
if ( fPathOwnedByImage && (fPath != NULL) )
delete [] fPath;
if ( fDynamicReferences != NULL ) {
- for (std::set<const ImageLoader*>::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) {
+ for (std::vector<const ImageLoader*>::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) {
const_cast<ImageLoader*>(*it)->fDynamicReferenceCount--;
}
delete fDynamicReferences;
void ImageLoader::addDynamicReference(const ImageLoader* target)
{
- if ( fDynamicReferences == NULL )
- fDynamicReferences = new std::set<const ImageLoader*>();
- if ( fDynamicReferences->count(target) == 0 ) {
- fDynamicReferences->insert(target);
+ bool alreadyInVector = false;
+ if ( fDynamicReferences == NULL ) {
+ fDynamicReferences = new std::vector<const ImageLoader*>();
+ }
+ else {
+ for (std::vector<const ImageLoader*>::iterator it = fDynamicReferences->begin(); it != fDynamicReferences->end(); ++it ) {
+ if ( *it == target ) {
+ alreadyInVector = true;
+ break;
+ }
+ }
+ }
+ if ( ! alreadyInVector ) {
+ fDynamicReferences->push_back(target);
const_cast<ImageLoader*>(target)->fDynamicReferenceCount++;
}
//dyld::log("dyld: addDynamicReference() from %s to %s, fDynamicReferences->size()=%lu\n", this->getPath(), target->getPath(), fDynamicReferences->size());
for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
const uint8_t* segStart = (const uint8_t*)segActualLoadAddress(i);
const uint8_t* segEnd = (const uint8_t*)segActualEndAddress(i);
+ if ( strcmp(segName(i), "__UNIXSTACK") == 0 ) {
+ // __UNIXSTACK never slides. This is the only place that cares
+ // and checking for that segment name in segActualLoadAddress()
+ // is too expensive.
+ segStart -= getSlide();
+ segEnd -= getSlide();
+ }
if ( (start <= segStart) && (segStart < end) )
return true;
if ( (start <= segEnd) && (segEnd < end) )
return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn);
}
+// this is called by initializeMainExecutable() to interpose on the initial set of images
+void ImageLoader::applyInterposing(const LinkContext& context)
+{
+ if ( fgInterposingTuples.size() != 0 )
+ this->recursiveApplyInterposing(context);
+}
void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, const RPathChain& loaderRPaths)
{
//dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", this->getPath(), fStaticReferenceCount, fNeverUnload);
+ // clear error strings
+ (*context.setErrorStrings)(dyld_error_kind_none, NULL, NULL, NULL);
+
uint64_t t0 = mach_absolute_time();
this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths);
context.notifyBatch(dyld_image_state_dependents_mapped);
uint64_t t4 = mach_absolute_time();
this->weakBind(context);
+ uint64_t t5 = mach_absolute_time();
+
context.notifyBatch(dyld_image_state_bound);
+ uint64_t t6 = mach_absolute_time();
- uint64_t t5 = mach_absolute_time();
std::vector<DOFInfo> dofs;
this->recursiveGetDOFSections(context, dofs);
context.registerDOFs(dofs);
- uint64_t t6 = mach_absolute_time();
+ uint64_t t7 = mach_absolute_time();
+ // interpose any dynamically loaded images
+ if ( !context.linkingMainExecutable && (fgInterposingTuples.size() != 0) ) {
+ this->recursiveApplyInterposing(context);
+ }
+ // clear error strings
+ (*context.setErrorStrings)(dyld_error_kind_none, NULL, NULL, NULL);
+
fgTotalLoadLibrariesTime += t1 - t0;
fgTotalRebaseTime += t3 - t2;
fgTotalBindTime += t4 - t3;
fgTotalWeakBindTime += t5 - t4;
- fgTotalDOF += t6 - t5;
+ fgTotalDOF += t7 - t6;
// done with initial dylib loads
fgNextPIEDylibAddress = 0;
return false;
}
-void ImageLoader::runInitializers(const LinkContext& context)
+void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)
{
uint64_t t1 = mach_absolute_time();
mach_port_t this_thread = mach_thread_self();
- this->recursiveInitialization(context, this_thread);
+ this->recursiveInitialization(context, this_thread, timingInfo);
context.notifyBatch(dyld_image_state_initialized);
mach_port_deallocate(mach_task_self(), this_thread);
uint64_t t2 = mach_absolute_time();
if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) {
// <rdar://problem/5910137> dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized
// in preflight mode, don't even load dylib that are in the shared cache because they will never be unloaded
- setLibImage(i, NULL, false);
+ setLibImage(i, NULL, false, false);
continue;
}
#endif
}
if ( fNeverUnload )
dependentLib->setNeverUnload();
- dependentLib->fStaticReferenceCount += 1;
+ if ( ! requiredLibInfo.upward )
+ dependentLib->fStaticReferenceCount += 1;
LibraryInfo actualInfo = dependentLib->doGetLibraryInfo();
depLibReRequired = requiredLibInfo.required;
depLibCheckSumsMatch = ( actualInfo.checksum == requiredLibInfo.info.checksum );
}
// check found library version is compatible
if ( actualInfo.minVersion < requiredLibInfo.info.minVersion ) {
+ // record values for possible use by CrashReporter or Finder
dyld::throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d",
this->getShortName(), requiredLibInfo.info.minVersion >> 16, (requiredLibInfo.info.minVersion >> 8) & 0xff, requiredLibInfo.info.minVersion & 0xff,
dependentLib->getShortName(), actualInfo.minVersion >> 16, (actualInfo.minVersion >> 8) & 0xff, actualInfo.minVersion & 0xff);
}
- // prebinding for this image disabled if any dependent library changed or slid
- if ( !depLibCheckSumsMatch || (dependentLib->getSlide() != 0) )
+ // prebinding for this image disabled if any dependent library changed
+ if ( !depLibCheckSumsMatch )
+ canUsePrelinkingInfo = false;
+ // prebinding for this image disabled unless both this and dependent are in the shared cache
+ if ( !dependentLib->inSharedCache() || !this->inSharedCache() )
canUsePrelinkingInfo = false;
+
//if ( context.verbosePrebinding ) {
// if ( !requiredLib.checksumMatches )
// fprintf(stderr, "dyld: checksum mismatch, (%u v %u) for %s referencing %s\n",
// fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), dependentLib->getPath());
if ( requiredLibInfo.required ) {
fState = dyld_image_state_mapped;
+ // record values for possible use by CrashReporter or Finder
+ if ( strstr(msg, "Incompatible") != NULL )
+ (*context.setErrorStrings)(dyld_error_kind_dylib_version, this->getPath(), requiredLibInfo.name, NULL);
+ else if ( strstr(msg, "architecture") != NULL )
+ (*context.setErrorStrings)(dyld_error_kind_dylib_wrong_arch, this->getPath(), requiredLibInfo.name, NULL);
+ else
+ (*context.setErrorStrings)(dyld_error_kind_dylib_missing, this->getPath(), requiredLibInfo.name, NULL);
dyld::throwf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getPath(), msg);
}
// ok if weak library not found
dependentLib = NULL;
canUsePrelinkingInfo = false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero
}
- setLibImage(i, dependentLib, depLibReExported);
+ setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward);
}
fAllLibraryChecksumsAndLoadAddressesMatch = canUsePrelinkingInfo;
catch (const char* msg) {
// this image is not rebased
fState = dyld_image_state_dependents_mapped;
+ CRSetCrashLogMessage2(NULL);
throw;
}
}
}
+void ImageLoader::recursiveApplyInterposing(const LinkContext& context)
+{
+ if ( ! fInterposed ) {
+ // break cycles
+ fInterposed = true;
+
+ try {
+ // interpose lower level libraries first
+ for(unsigned int i=0; i < libraryCount(); ++i) {
+ ImageLoader* dependentImage = libImage(i);
+ if ( dependentImage != NULL )
+ dependentImage->recursiveApplyInterposing(context);
+ }
+
+ // interpose this image
+ doInterpose(context);
+ }
+ catch (const char* msg) {
+ // this image is not interposed
+ fInterposed = false;
+ throw;
+ }
+ }
+}
catch (const char* msg) {
// restore state
fState = dyld_image_state_rebased;
+ CRSetCrashLogMessage2(NULL);
throw;
}
}
// count how many have not already had weakbinding done
int countNotYetWeakBound = 0;
int countOfImagesWithWeakDefinitions = 0;
+ int countOfImagesWithWeakDefinitionsNotInSharedCache = 0;
for(int i=0; i < count; ++i) {
if ( ! imagesNeedingCoalescing[i]->fWeakSymbolsBound )
++countNotYetWeakBound;
- if ( imagesNeedingCoalescing[i]->hasCoalescedExports() )
+ if ( imagesNeedingCoalescing[i]->hasCoalescedExports() ) {
++countOfImagesWithWeakDefinitions;
+ if ( ! imagesNeedingCoalescing[i]->inSharedCache() )
+ ++countOfImagesWithWeakDefinitionsNotInSharedCache;
+ }
}
// don't need to do any coalescing if only one image has overrides, or all have already been done
- if ( (countOfImagesWithWeakDefinitions > 1) && (countNotYetWeakBound > 0) ) {
+ if ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) {
// make symbol iterators for each
ImageLoader::CoalIterator iterators[count];
ImageLoader::CoalIterator* sortedIts[count];
}
-void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread)
+void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, InitializerTimingList& timingInfo)
{
recursive_lock lock_info(this_thread);
recursiveSpinLock(lock_info);
ImageLoader* dependentImage = libImage(i);
if ( dependentImage != NULL )
// don't try to initialize stuff "above" me
- if ( (dependentImage != NULL) && (dependentImage->fDepth >= fDepth) )
- dependentImage->recursiveInitialization(context, this_thread);
+ if ( (dependentImage != NULL) && (dependentImage->fDepth >= fDepth) && !libIsUpward(i) )
+ dependentImage->recursiveInitialization(context, this_thread, timingInfo);
}
// record termination order
context.terminationRecorder(this);
// let objc know we are about to initalize this image
+ uint64_t t1 = mach_absolute_time();
fState = dyld_image_state_dependents_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_dependents_initialized, this);
-
+
// initialize this image
- this->doInitialization(context);
-
+ bool hasInitializers = this->doInitialization(context);
+
// let anyone know we finished initalizing this image
fState = dyld_image_state_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_initialized, this);
+
+ if ( hasInitializers ) {
+ uint64_t t2 = mach_absolute_time();
+ timingInfo.images[timingInfo.count].image = this;
+ timingInfo.images[timingInfo.count].initTime = (t2-t1);
+ timingInfo.count++;
+ }
}
catch (const char* msg) {
// this image is not initialized
}
-void ImageLoader::printStatistics(unsigned int imageCount)
+void ImageLoader::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo)
{
uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime;
char commaNum1[40];
char commaNum2[40];
printTime("total time", totalTime, totalTime);
- dyld::log("total images loaded: %d (%u from dyld shared cache, %u needed no fixups)\n", imageCount, fgImagesUsedFromSharedCache, fgImagesRequiringNoFixups);
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ if ( fgImagesUsedFromSharedCache != 0 )
+ dyld::log("total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache);
+ else
+ dyld::log("total images loaded: %d\n", imageCount);
+#else
+ dyld::log("total images loaded: %d (%u from dyld shared cache)\n", imageCount, fgImagesUsedFromSharedCache);
+#endif
dyld::log("total segments mapped: %u, into %llu pages with %llu pages pre-fetched\n", fgTotalSegmentsMapped, fgTotalBytesMapped/4096, fgTotalBytesPreFetched/4096);
printTime("total images loading time", fgTotalLoadLibrariesTime, totalTime);
printTime("total dtrace DOF registration time", fgTotalDOF, totalTime);
printTime("total weak binding fixups time", fgTotalWeakBindTime, totalTime);
dyld::log("total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2));
printTime("total initializer time", fgTotalInitTime, totalTime);
+ for (uintptr_t i=0; i < timingInfo.count; ++i) {
+ dyld::log("%21s ", timingInfo.images[i].image->getShortName());
+ printTime("", timingInfo.images[i].initTime, totalTime);
+ }
+
}
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <mach-o/nlist.h>
#include <stdint.h>
#include <vector>
-#include <set>
#include <new>
+#if (__i386__ || __x86_64__)
+ #include <CrashReporterClient.h>
+#else
+ // work around until iOS has CrashReporterClient.h
+ #define CRSetCrashLogMessage(x)
+ #define CRSetCrashLogMessage2(x)
+#endif
+
+#define LOG_BINDINGS 0
+
#include "mach-o/dyld_images.h"
#include "mach-o/dyld_priv.h"
#define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM
#endif
+#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER
+ #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10
+#endif
+#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT
+ #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08
+#endif
#define SPLIT_SEG_SHARED_REGION_SUPPORT __arm__
#define SPLIT_SEG_DYLIB_SUPPORT (__ppc__ || __i386__ || __arm__)
#define PREBOUND_IMAGE_SUPPORT (__ppc__ || __i386__ || __arm__)
-#define TEXT_RELOC_SUPPORT (__ppc__ || __i386__)
-#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__)
+#define TEXT_RELOC_SUPPORT (__ppc__ || __i386__ || __arm__)
+#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__ || __arm__)
#define SUPPORT_OLD_CRT_INITIALIZATION (__ppc__ || __i386__)
-#define COMPRESSED_DYLD_INFO_SUPPORT (__i386__ || __x86_64__)
-#define CORESYMBOLICATION_SUPPORT (__i386__ || __x86_64__)
+#define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__)
+#define SUPPORT_VERSIONED_PATHS (__i386__ || __x86_64__)
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ #define CORESYMBOLICATION_SUPPORT 1
+#else
+ #define CORESYMBOLICATION_SUPPORT (__i386__ || __x86_64__)
+#endif
#if __arm__
#define INITIAL_IMAGE_COUNT 100
#else
extern void log(const char* format, ...) __attribute__((format(printf, 1, 2)));
extern void warn(const char* format, ...) __attribute__((format(printf, 1, 2)));
extern const char* mkstringf(const char* format, ...) __attribute__((format(printf, 1, 2)));
+#if LOG_BINDINGS
+ extern void logBindings(const char* format, ...) __attribute__((format(printf, 1, 2)));
+#endif
};
unsigned int (*imageCount)();
void (*setNewProgramVars)(const ProgramVars&);
bool (*inSharedCache)(const char* path);
+ void (*setErrorStrings)(unsigned errorCode, const char* errorClientOfDylibPath,
+ const char* errorTargetDylibPath, const char* errorSymbol);
#if SUPPORT_OLD_CRT_INITIALIZATION
void (*setRunInitialzersOldWay)();
#endif
bool bindFlat;
bool linkingMainExecutable;
bool startedInitializingMainExecutable;
- bool noPIE;
bool processIsRestricted;
bool verboseOpts;
bool verboseEnv;
bool verboseInit;
bool verboseDOF;
bool verbosePrebinding;
+ bool verboseCoreSymbolication;
bool verboseWarnings;
+ bool verboseRPaths;
+ bool verboseInterposing;
};
struct CoalIterator
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0;
virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0;
+ struct InitializerTimingList
+ {
+ uintptr_t count;
+ struct {
+ ImageLoader* image;
+ uint64_t initTime;
+ } images[1];
+ };
// constructor is protected, but anyone can delete an image
// runInitializers() is normally called in link() but the main executable must
// run crt code before initializers
- void runInitializers(const LinkContext& context);
+ void runInitializers(const LinkContext& context, InitializerTimingList& timingInfo);
// called after link() forces all lazy pointers to be bound
void bindAllLazyPointers(const LinkContext& context, bool recursive);
// even if image is deleted, leave segments mapped in
bool leaveMapped() { return fLeaveMapped; }
+ // image resides in dyld shared cache
+ virtual bool inSharedCache() const = 0;
+
// checks if the specifed address is within one of this image's segments
virtual bool containsAddress(const void* addr) const;
virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const = 0;
// gets address of implementation (code) of the specified exported symbol
- virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor=NULL) const = 0;
+ virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
+ const ImageLoader* requestor=NULL, bool runResolver=false) const = 0;
// gets attributes of the specified exported symbol
virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0;
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0;
// called at runtime when a fast lazily bound function is first called
- virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) = 0;
+ virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
+ void (*lock)(), void (*unlock)()) = 0;
// calls termination routines (e.g. C++ static destructors for image)
virtual void doTermination(const LinkContext& context) = 0;
virtual uintptr_t segActualEndAddress(unsigned int) const = 0;
+ // if the image contains interposing functions, register them
+ virtual void registerInterposing() = 0;
+
+ // when resolving symbols look in subImage if symbol can't be found
+ void reExport(ImageLoader* subImage);
+
+ void applyInterposing(const LinkContext& context);
dyld_image_states getState() { return (dyld_image_states)fState; }
void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; }
// triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast
- static void printStatistics(unsigned int imageCount);
+ static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo);
// used with DYLD_IMAGE_SUFFIX
static void addSuffix(const char* path, const char* suffix, char* result);
LibraryInfo info;
bool required;
bool reExported;
+ bool upward;
};
+ struct InterposeTuple {
+ uintptr_t replacement;
+ ImageLoader* replacementImage; // don't apply replacement to this image
+ uintptr_t replacee;
+ };
+
typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars);
typedef void (*Terminator)(void);
unsigned int libraryCount() const { return fLibraryCount; }
virtual ImageLoader* libImage(unsigned int) const = 0;
virtual bool libReExported(unsigned int) const = 0;
- virtual void setLibImage(unsigned int, ImageLoader*, bool) = 0;
+ virtual bool libIsUpward(unsigned int) const = 0;
+ virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) = 0;
// To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized.
void recursiveRebase(const LinkContext& context);
void recursiveBind(const LinkContext& context, bool forceLazysBound);
void weakBind(const LinkContext& context);
+ void recursiveApplyInterposing(const LinkContext& context);
void recursiveGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs);
- void recursiveInitialization(const LinkContext& context, mach_port_t this_thread);
+ void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, ImageLoader::InitializerTimingList&);
// fill in information about dependent libraries (array length is fLibraryCount)
virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0;
// if image has any dtrace DOF sections, append them to list to be registered
virtual void doGetDOFSections(const LinkContext& context, std::vector<DOFInfo>& dofs) = 0;
+ // do interpose
+ virtual void doInterpose(const LinkContext& context) = 0;
+
// run any initialization routines in this image
- virtual void doInitialization(const LinkContext& context) = 0;
+ virtual bool doInitialization(const LinkContext& context) = 0;
// return if this image has termination routines
virtual bool needsTermination() = 0;
static uintptr_t fgNextPIEDylibAddress;
static uint32_t fgImagesWithUsedPrebinding;
static uint32_t fgImagesUsedFromSharedCache;
- static uint32_t fgImagesRequiringNoFixups;
static uint32_t fgImagesHasWeakDefinitions;
static uint32_t fgImagesRequiringCoalescing;
static uint32_t fgTotalRebaseFixups;
static uint64_t fgTotalWeakBindTime;
static uint64_t fgTotalDOF;
static uint64_t fgTotalInitTime;
+ static std::vector<InterposeTuple> fgInterposingTuples;
const char* fPath;
dev_t fDevice;
ino_t fInode;
uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image
uint32_t fStaticReferenceCount; // count of images that have a fLibraries entry pointing to this image
uint32_t fDynamicReferenceCount; // count of images that have a fDynamicReferences entry pointer to this image
- std::set<const ImageLoader*>* fDynamicReferences; // list of all images this image used because of a flat/coalesced lookup
+ std::vector<const ImageLoader*>* fDynamicReferences; // list of all images this image used because of a flat/coalesced lookup
private:
struct recursive_lock {
fNeverUnload : 1, // image was statically loaded by main executable
fHideSymbols : 1, // ignore this image's exported symbols when linking other images
fMatchByInstallName : 1,// look at image's install-path not its load path
+ fInterposed : 1,
fRegisteredDOF : 1,
fAllLazyPointersBound : 1,
fBeingRemoved : 1,
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#define __eip eip
#define __rip rip
-
+#define __STDC_LIMIT_MACROS
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/sysctl.h>
#include <libkern/OSAtomic.h>
#include <libkern/OSCacheControl.h>
+#include <stdint.h>
#include "ImageLoaderMachO.h"
#include "ImageLoaderMachOCompressed.h"
#include "ImageLoaderMachOClassic.h"
#include "mach-o/dyld_images.h"
+// <rdar://problem/8718137> use stack guard random value to add padding between dylibs
+extern "C" long __stack_chk_guard;
+#ifndef LC_LOAD_UPWARD_DYLIB
+ #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
+#endif
// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables
#if __LP64__
fReadOnlyImportSegment(false),
#endif
fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false),
- fHasInitializers(false), fHasTerminators(false)
+ fHasInitializers(false), fHasTerminators(false), fGoodFirstSegment(false), fRegisteredAsRequiresCoalescing(false)
{
fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0);
// determine if this mach-o file has classic or compressed LINKEDIT and number of segments it has
void ImageLoaderMachO::sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed,
- unsigned int* segCount, unsigned int* libCount)
+ unsigned int* segCount, unsigned int* libCount,
+ const linkedit_data_command** codeSigCmd)
{
*compressed = false;
*segCount = 0;
*libCount = 0;
+ *codeSigCmd = NULL;
const uint32_t cmd_count = mh->ncmds;
const struct load_command* const cmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header));
const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + mh->sizeofcmds);
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
*libCount += 1;
break;
+ case LC_CODE_SIGNATURE:
+ *codeSigCmd = (struct linkedit_data_command*)cmd; // only support one LC_CODE_SIGNATURE per image
+ break;
}
uint32_t cmdLength = cmd->cmdsize;
cmd = (const struct load_command*)(((char*)cmd)+cmdLength);
bool compressed;
unsigned int segCount;
unsigned int libCount;
- sniffLoadCommands(mh, path, &compressed, &segCount, &libCount);
+ const linkedit_data_command* codeSigCmd;
+ sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd);
// instantiate concrete class based on content of load commands
if ( compressed )
return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context);
bool compressed;
unsigned int segCount;
unsigned int libCount;
- sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount);
+ const linkedit_data_command* codeSigCmd;
+ sniffLoadCommands((const macho_header*)fileData, path, &compressed, &segCount, &libCount, &codeSigCmd);
// instantiate concrete class based on content of load commands
if ( compressed )
- return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, context);
+ return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context);
else
- return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, context);
+ return ImageLoaderMachOClassic::instantiateFromFile(path, fd, fileData, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context);
}
// create image by using cached mach-o file
-ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, const LinkContext& context)
+ImageLoader* ImageLoaderMachO::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context)
{
// instantiate right concrete class
bool compressed;
unsigned int segCount;
unsigned int libCount;
- sniffLoadCommands(mh, path, &compressed, &segCount, &libCount);
+ const linkedit_data_command* codeSigCmd;
+ sniffLoadCommands(mh, path, &compressed, &segCount, &libCount, &codeSigCmd);
// instantiate concrete class based on content of load commands
if ( compressed )
- return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, info, segCount, libCount, context);
+ return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context);
else
- return ImageLoaderMachOClassic::instantiateFromCache(mh, path, info, segCount, libCount, context);
+ return ImageLoaderMachOClassic::instantiateFromCache(mh, path, slide, info, segCount, libCount, context);
}
// create image by copying an in-memory mach-o file
bool compressed;
unsigned int segCount;
unsigned int libCount;
- sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount);
+ const linkedit_data_command* sigcmd;
+ sniffLoadCommands(mh, moduleName, &compressed, &segCount, &libCount, &sigcmd);
// instantiate concrete class based on content of load commands
if ( compressed )
return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context);
#if TEXT_RELOC_SUPPORT
// __TEXT segment always starts at beginning of file and contains mach_header and load commands
if ( strcmp(segName(i),"__TEXT") == 0 ) {
- if ( segHasRebaseFixUps(i) )
+ if ( segHasRebaseFixUps(i) && (fSlide != 0) )
fTextSegmentRebases = true;
if ( segHasBindFixUps(i) )
fTextSegmentBinds = true;
// keep count of prebound images with weak exports
if ( this->participatesInCoalescing() ) {
++fgImagesRequiringCoalescing;
+ fRegisteredAsRequiresCoalescing = true;
if ( this->hasCoalescedExports() )
++fgImagesHasWeakDefinitions;
}
case LC_RPATH:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
// do nothing, just prevent LC_REQ_DYLD exception from occuring
break;
default:
// for UnmapSegments() to work
void ImageLoaderMachO::destroy()
{
- // keep count of images with weak exports
- if ( this->participatesInCoalescing() ) {
+ // update count of images with weak exports
+ if ( fRegisteredAsRequiresCoalescing ) {
--fgImagesRequiringCoalescing;
if ( this->hasCoalescedExports() )
--fgImagesHasWeakDefinitions;
}
#if CODESIGNING_SUPPORT
-void ImageLoaderMachO::loadCodeSignature(const uint8_t* fileData, int fd, uint64_t offsetInFatFile)
+void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile)
{
- // look for code signature load command
- // do this in the read() memory buffer - not in the mapped __TEXT segment
- const uint32_t cmd_count = ((macho_header*)fileData)->ncmds;
- const struct load_command* const cmds = (struct load_command*)&fileData[sizeof(macho_header)];
- const struct load_command* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd == LC_CODE_SIGNATURE ) {
- const struct linkedit_data_command *sigcmd = (struct linkedit_data_command*) cmd;
- // fLinkEditBase is not set up yet, so compute it
- const uint8_t* linkEditBase = NULL;
- for(unsigned int i=0; i < fSegmentsCount; ++i) {
- // set up pointer to __LINKEDIT segment
- if ( strcmp(segName(i),"__LINKEDIT") == 0 ) {
- linkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i));
- break;
- }
- }
- fsignatures_t siginfo;
- siginfo.fs_file_start=offsetInFatFile; // CD coverage offset
- siginfo.fs_blob_start=(void*)(linkEditBase+sigcmd->dataoff); // start of CD in file
- siginfo.fs_blob_size=sigcmd->datasize; // size of CD
- int result = fcntl(fd, F_ADDSIGS, &siginfo);
- if ( result == -1 )
- dyld::log("dyld: code signature failed for %s with errno=%d\n", this->getPath(), errno);
- break; // only support one LC_CODE_SIGNATURE
- }
- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
- }
+ fsignatures_t siginfo;
+ siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file
+ siginfo.fs_blob_start=(void*)(codeSigCmd->dataoff); // start of CD in mach-o file
+ siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD
+ int result = fcntl(fd, F_ADDFILESIGS, &siginfo);
+ if ( result == -1 )
+ dyld::log("dyld: F_ADDFILESIGS failed for %s with errno=%d\n", this->getPath(), errno);
+ //dyld::log("dyld: registered code signature for %s\n", this->getPath());
}
#endif
return NULL;
}
+void ImageLoaderMachO::registerInterposing()
+{
+ // mach-o files advertise interposing by having a __DATA __interpose section
+ uintptr_t textStart = this->segActualLoadAddress(0);
+ uintptr_t textEnd = this->segActualEndAddress(0);
+ // <rdar://problem/8268602> verify that the first segment load command is for a read-only segment
+ if ( ! fGoodFirstSegment )
+ return;
+ struct InterposeData { uintptr_t replacement; uintptr_t replacee; };
+ const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+ const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
+ const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( ((sect->flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sect->sectname, "__interpose") == 0) && (strcmp(seg->segname, "__DATA") == 0)) ) {
+ const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide);
+ const unsigned int count = sect->size / sizeof(InterposeData);
+ for (uint32_t i=0; i < count; ++i) {
+ ImageLoader::InterposeTuple tuple;
+ tuple.replacement = interposeArray[i].replacement;
+ tuple.replacementImage = this;
+ tuple.replacee = interposeArray[i].replacee;
+ // <rdar://problem/7937695> verify that replacement is in this image
+ if ( (tuple.replacement >= textStart) && (tuple.replacement < textEnd) ) {
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ if ( it->replacee == tuple.replacee ) {
+ tuple.replacee = it->replacement;
+ }
+ }
+ ImageLoader::fgInterposingTuples.push_back(tuple);
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+}
void* ImageLoaderMachO::getMain() const
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
return false;
case LC_ID_DYLIB:
{
lib->info.maxVersion = 0;
lib->required = false;
lib->reExported = false;
+ lib->upward = false;
}
else {
uint32_t index = 0;
case LC_LOAD_DYLIB:
case LC_LOAD_WEAK_DYLIB:
case LC_REEXPORT_DYLIB:
+ case LC_LOAD_UPWARD_DYLIB:
{
const struct dylib_command* dylib = (struct dylib_command*)cmd;
DependentLibraryInfo* lib = &libs[index++];
lib->info.maxVersion = dylib->dylib.current_version;
lib->required = (cmd->cmd != LC_LOAD_WEAK_DYLIB);
lib->reExported = (cmd->cmd == LC_REEXPORT_DYLIB);
+ lib->upward = (cmd->cmd == LC_LOAD_UPWARD_DYLIB);
}
break;
}
for (uint32_t i = 0; i < cmd_count; ++i) {
switch (cmd->cmd) {
case LC_RPATH:
+ const char* pathToAdd = NULL;
const char* path = (char*)cmd + ((struct rpath_command*)cmd)->path.offset;
if ( strncmp(path, "@loader_path/", 13) == 0 ) {
if ( context.processIsRestricted && (context.mainExecutable == this) ) {
strcpy(&addPoint[1], &path[13]);
else
strcpy(newRealPath, &path[13]);
- path = strdup(newRealPath);
+ pathToAdd = strdup(newRealPath);
}
}
else if ( strncmp(path, "@executable_path/", 17) == 0 ) {
strcpy(&addPoint[1], &path[17]);
else
strcpy(newRealPath, &path[17]);
- path = strdup(newRealPath);
+ pathToAdd = strdup(newRealPath);
}
}
else if ( (path[0] != '/') && context.processIsRestricted ) {
struct stat stat_buf;
if ( stat(newPath, &stat_buf) != -1 ) {
//dyld::log("combined DYLD_ROOT_PATH and LC_RPATH: %s\n", newPath);
- path = strdup(newPath);
+ pathToAdd = strdup(newPath);
found = true;
break;
}
}
if ( ! found ) {
// make copy so that all elements of 'paths' can be freed
- path = strdup(path);
+ pathToAdd = strdup(path);
}
}
else {
// make copy so that all elements of 'paths' can be freed
- path = strdup(path);
+ pathToAdd = strdup(path);
}
- paths.push_back(path);
+ if ( pathToAdd != NULL )
+ paths.push_back(pathToAdd);
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
segMakeWritable(textSegmentIndex, context);
}
else {
- segProtect(textSegmentIndex, context);
+ // iPhoneOS requires range to be invalidated before it is made executable
sys_icache_invalidate((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex));
+ segProtect(textSegmentIndex, context);
}
}
#endif
-uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor) const
+uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
+ const ImageLoader* requestor, bool runResolver) const
{
- return this->getSymbolAddress(sym, requestor, context);
+ return this->getSymbolAddress(sym, requestor, context, runResolver);
}
-uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, const LinkContext& context) const
+uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor,
+ const LinkContext& context, bool runResolver) const
{
- uintptr_t result = exportedSymbolAddress(sym);
+ uintptr_t result = exportedSymbolAddress(context, sym, runResolver);
+ // check for interposing overrides
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ // replace all references to 'replacee' with 'replacement'
+ if ( (result == it->replacee) && (requestor != it->replacementImage) ) {
+ if ( context.verboseInterposing ) {
+ dyld::log("dyld interposing: replace 0x%lX with 0x%lX in %s\n",
+ it->replacee, it->replacement, this->getPath());
+ }
+ result = it->replacement;
+ }
+ }
return result;
}
}
-void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const char* symbol, const char* referencedFrom, const char* expectedIn)
+void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol,
+ const char* referencedFrom, const char* expectedIn)
{
+ // record values for possible use by CrashReporter or Finder
+ (*context.setErrorStrings)(dyld_error_kind_symbol_missing, referencedFrom, expectedIn, symbol);
dyld::throwf("Symbol not found: %s\n Referenced from: %s\n Expected in: %s\n", symbol, referencedFrom, expectedIn);
}
((targetImage != NULL) ? targetImage->getShortName() : "<weak>import-missing>"),
symbolName, (uintptr_t)location, value);
}
+#if LOG_BINDINGS
+// dyld::logBindings("%s: %s\n", targetImage->getShortName(), symbolName);
+#endif
// do actual update
uintptr_t* locationToFix = (uintptr_t*)location;
// lookup _NXArgc
sym = this->findExportedSymbol("_NXArgc", false, NULL);
if ( sym != NULL )
- vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this);
+ vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this, false);
// lookup _NXArgv
sym = this->findExportedSymbol("_NXArgv", false, NULL);
if ( sym != NULL )
- vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this);
+ vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false);
// lookup _environ
sym = this->findExportedSymbol("_environ", false, NULL);
if ( sym != NULL )
- vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this);
+ vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false);
// lookup __progname
sym = this->findExportedSymbol("___progname", false, NULL);
if ( sym != NULL )
- vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this);
+ vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this, false);
context.setNewProgramVars(vars);
}
bool ImageLoaderMachO::usablePrebinding(const LinkContext& context) const
{
// if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
- if ( (this->isPrebindable() || fInSharedCache)
- && (this->getSlide() == 0)
+ if ( ((this->isPrebindable() && (this->getSlide() == 0)) || fInSharedCache)
&& this->usesTwoLevelNameSpace()
&& this->allDependentLibrariesAsWhenPreBound() ) {
// allow environment variables to disable prebinding
void ImageLoaderMachO::doImageInit(const LinkContext& context)
{
if ( fHasDashInit ) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // <rdar://problem/8543820> verify initializers are in first segment for dylibs
+ if ( this->isDylib() && !fGoodFirstSegment ) {
+ if ( context.verboseInit )
+ dyld::log("dyld: ignoring -init in %s\n", this->getPath());
+ return;
+ }
+ uintptr_t textStart = this->segActualLoadAddress(0);
+ uintptr_t textEnd = this->segActualEndAddress(0);
+#endif
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
const struct load_command* cmd = cmds;
switch (cmd->cmd) {
case LC_ROUTINES_COMMAND:
Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide);
- if ( context.verboseInit )
- dyld::log("dyld: calling -init function 0x%p in %s\n", func, this->getPath());
- func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // <rdar://problem/8543820> verify initializers are in first segment for dylibs
+ if ( this->isDylib() && (((uintptr_t)func >= textEnd) || ((uintptr_t)func < textStart)) ) {
+ if ( context.verboseInit )
+ dyld::log("dyld: ignoring out of bounds initializer function %p in %s\n", func, this->getPath());
+ }
+ else {
+#endif
+ if ( context.verboseInit )
+ dyld::log("dyld: calling -init function 0x%p in %s\n", func, this->getPath());
+ func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ }
+#endif
break;
}
cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
{
if ( fHasInitializers ) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // <rdar://problem/8543820> verify initializers are in first segment for dylibs
+ if ( this->isDylib() && !fGoodFirstSegment ) {
+ if ( context.verboseInit )
+ dyld::log("dyld: ignoring all initializers in %s\n", this->getPath());
+ return;
+ }
+ uintptr_t textStart = this->segActualLoadAddress(0);
+ uintptr_t textEnd = this->segActualEndAddress(0);
+#endif
const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
const struct load_command* cmd = cmds;
const uint32_t count = sect->size / sizeof(uintptr_t);
for (uint32_t i=0; i < count; ++i) {
Initializer func = inits[i];
- if ( context.verboseInit )
- dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
- func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // <rdar://problem/8543820> verify initializers are in first segment for dylibs
+ if ( this->isDylib() && (((uintptr_t)func >= textEnd) || ((uintptr_t)func < textStart)) ) {
+ if ( context.verboseInit )
+ dyld::log("dyld: ignoring out of bounds initializer function %p in %s\n", func, this->getPath());
+ }
+ else {
+#endif
+ if ( context.verboseInit )
+ dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath());
+ func(context.argc, context.argv, context.envp, context.apple, &context.programVars);
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ }
+#endif
}
}
}
- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
}
}
}
}
-void ImageLoaderMachO::doInitialization(const LinkContext& context)
+bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
+ CRSetCrashLogMessage2(this->getPath());
+
// mach-o has -init and static initializers
doImageInit(context);
doModInitFunctions(context);
+
+ CRSetCrashLogMessage2(NULL);
+
+ return (fHasDashInit || fHasInitializers);
}
bool ImageLoaderMachO::needsInitialization()
}
-void ImageLoaderMachO::printStatistics(unsigned int imageCount)
+void ImageLoaderMachO::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo)
{
- ImageLoader::printStatistics(imageCount);
+ ImageLoader::printStatistics(imageCount, timingInfo);
dyld::log("total symbol trie searches: %d\n", fgSymbolTrieSearchs);
dyld::log("total symbol table binary searches: %d\n", fgSymbolTableBinarySearchs);
- dyld::log("total images defining/using weak symbols: %u/%u\n", fgImagesHasWeakDefinitions, fgImagesRequiringCoalescing);
+ dyld::log("total images defining weak symbols: %u\n", fgImagesHasWeakDefinitions);
+ dyld::log("total images using weak symbols: %u\n", fgImagesRequiringCoalescing);
}
if ( strcmp(segName(i), "__PAGEZERO") == 0 )
continue;
if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) )
- throw "can't map";
+ dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i));
}
}
else {
vm_size_t size = length;
// in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either
if ( fgNextPIEDylibAddress != 0 ) {
- addr = fgNextPIEDylibAddress + (arc4random() & 0x3) * 4096; // add small random padding between dylibs
+ // add small (0-3 pages) random padding between dylibs
+ addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*4096;
+ //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard));
kern_return_t r = vm_allocate(mach_task_self(), &addr, size, VM_FLAGS_FIXED);
if ( r == KERN_SUCCESS ) {
fgNextPIEDylibAddress = addr + size;
intptr_t slide = this->assignSegmentAddresses(context);
if ( context.verboseMapping )
dyld::log("dyld: Mapping %s\n", this->getPath());
+ // <rdar://problem/8268602> verify that the first segment load command is for a r-x segment
+ // that starts at begining of file and is larger than all load commands
+ uintptr_t firstSegMappedStart = segPreferredLoadAddress(0) + slide;
+ uintptr_t firstSegMappedEnd = firstSegMappedStart + this->segSize(0);
+ if ( (this->segLoadCommand(0)->initprot == (VM_PROT_EXECUTE|VM_PROT_READ))
+ && (this->segFileOffset(0) == 0)
+ && (this->segFileSize(0) != 0)
+ && (this->segSize(0) > ((macho_header*)fMachOData)->sizeofcmds) ) {
+ fGoodFirstSegment = true;
+ }
// map in all segments
for(unsigned int i=0, e=segmentCount(); i < e; ++i) {
vm_offset_t fileOffset = segFileOffset(i) + offsetInFat;
vm_size_t size = segFileSize(i);
- void* requestedLoadAddress = (void*)(segPreferredLoadAddress(i) + slide);
+ uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide;
+ // <rdar://problem/8268602> verify other segments map after first
+ if ( (i != 0) && (requestedLoadAddress < firstSegMappedEnd) )
+ fGoodFirstSegment = false;
int protection = 0;
if ( !segUnaccessible(i) ) {
- if ( segExecutable(i) )
+ // If has text-relocs, don't set x-bit initially.
+ // Instead set it later after text-relocs have been done.
+ // The iPhone OS does not like it when you make executable code writable.
+ if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) )
protection |= PROT_EXEC;
if ( segReadable(i) )
protection |= PROT_READ;
dyld::throwf("truncated mach-o error: segment %s extends to %llu which is past end of file %llu",
segName(i), (uint64_t)(fileOffset+size), fileLen);
}
- void* loadAddress = mmap(requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset);
+ void* loadAddress = mmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset);
if ( loadAddress == ((void*)(-1)) ) {
dyld::throwf("mmap() error %d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s",
- errno, (uintptr_t)requestedLoadAddress, (uintptr_t)size, segName(i), getPath());
+ errno, requestedLoadAddress, (uintptr_t)size, segName(i), getPath());
}
}
// update stats
++ImageLoader::fgTotalSegmentsMapped;
ImageLoader::fgTotalBytesMapped += size;
if ( context.verboseMapping )
- dyld::log("%18s at 0x%08lX->0x%08lX with permissions %c%c%c\n", segName(i), (uintptr_t)requestedLoadAddress, (uintptr_t)((char*)requestedLoadAddress+size-1),
+ dyld::log("%18s at 0x%08lX->0x%08lX with permissions %c%c%c\n", segName(i), requestedLoadAddress, requestedLoadAddress+size-1,
(protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' );
}
+
// update slide to reflect load location
this->setSlide(slide);
}
vm_size_t size = segSize(segIndex);
const bool setCurrentPermissions = false;
kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection);
- if ( r != KERN_SUCCESS )
- throw "can't set vm permissions for mapped segment";
+ if ( r != KERN_SUCCESS ) {
+ dyld::throwf("vm_protect(0x%08llX, 0x%08llX, false, 0x%02X) failed, result=%d for segment %s in %s",
+ (long long)addr, (long long)size, protection, r, segName(segIndex), this->getPath());
+ }
if ( context.verboseMapping ) {
dyld::log("%18s at 0x%08lX->0x%08lX altered permissions to %c%c%c\n", segName(segIndex), (uintptr_t)addr, (uintptr_t)addr+size-1,
(protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' );
vm_size_t size = segSize(segIndex);
const bool setCurrentPermissions = false;
vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ;
- if ( segExecutable(segIndex) )
+ if ( segExecutable(segIndex) && !segHasRebaseFixUps(segIndex) )
protection |= VM_PROT_EXECUTE;
kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection);
- if ( r != KERN_SUCCESS )
- throw "can't set vm permissions for mapped segment";
+ if ( r != KERN_SUCCESS ) {
+ dyld::throwf("vm_protect(0x%08llX, 0x%08llX, false, 0x%02X) failed, result=%d for segment %s in %s",
+ (long long)addr, (long long)size, protection, r, segName(segIndex), this->getPath());
+ }
if ( context.verboseMapping ) {
dyld::log("%18s at 0x%08lX->0x%08lX altered permissions to %c%c%c\n", segName(segIndex), (uintptr_t)addr, (uintptr_t)addr+size-1,
(protection & PROT_READ) ? 'r' : '.', (protection & PROT_WRITE) ? 'w' : '.', (protection & PROT_EXEC) ? 'x' : '.' );
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
static ImageLoader* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context);
static ImageLoader* instantiateFromFile(const char* path, int fd, const uint8_t firstPage[4096], uint64_t offsetInFat,
uint64_t lenInFat, const struct stat& info, const LinkContext& context);
- static ImageLoader* instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info, const LinkContext& context);
+ static ImageLoader* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, const LinkContext& context);
static ImageLoader* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context);
+ bool inSharedCache() const { return fInSharedCache; }
const char* getInstallPath() const;
virtual void* getMain() const;
virtual const struct mach_header* machHeader() const;
virtual const void* getEnd() const;
virtual bool hasCoalescedExports() const;
virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const ImageLoader** foundIn) const;
- virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, const ImageLoader* requestor) const;
+ virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context,
+ const ImageLoader* requestor, bool runResolver) const;
virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const;
virtual const char* getExportedSymbolName(const Symbol* sym) const;
virtual uint32_t getExportedSymbolCount() const;
virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex) = 0;
virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context) = 0;
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0;
- virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context) = 0;
+ virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) = 0;
virtual void doTermination(const LinkContext& context);
virtual bool needsInitialization();
virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length);
virtual uintptr_t segActualLoadAddress(unsigned int) const;
virtual uintptr_t segPreferredLoadAddress(unsigned int) const;
virtual uintptr_t segActualEndAddress(unsigned int) const;
+ virtual void registerInterposing();
- static void printStatistics(unsigned int imageCount);
+ static void printStatistics(unsigned int imageCount, const InitializerTimingList&);
protected:
ImageLoaderMachO(const ImageLoaderMachO&);
void operator=(const ImageLoaderMachO&);
- virtual void setDyldInfo(const dyld_info_command*) = 0;
+ virtual void setDyldInfo(const struct dyld_info_command*) = 0;
virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) = 0;
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0;
virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0;
virtual void rebase(const LinkContext& context) = 0;
virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0;
virtual bool containsSymbol(const void* addr) const = 0;
- virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const = 0;
+ virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const = 0;
virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const = 0;
virtual const char* exportedSymbolName(const Symbol* symbol) const = 0;
virtual unsigned int exportedSymbolCount() const = 0;
virtual void doRebase(const LinkContext& context);
virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0;
virtual void doBindJustLazies(const LinkContext& context) = 0;
- virtual void doInitialization(const LinkContext& context);
+ virtual bool doInitialization(const LinkContext& context);
virtual void doGetDOFSections(const LinkContext& context, std::vector<ImageLoader::DOFInfo>& dofs);
virtual bool needsTermination();
virtual bool segmentsMustSlideTogether() const;
void destroy();
static void sniffLoadCommands(const macho_header* mh, const char* path, bool* compressed,
- unsigned int* segCount, unsigned int* libCount);
+ unsigned int* segCount, unsigned int* libCount,
+ const linkedit_data_command** codeSigCmd);
static bool needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh);
#if CODESIGNING_SUPPORT
- void loadCodeSignature(const uint8_t* fileData, int fd, uint64_t offsetInFatFile);
+ void loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile);
#endif
const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const;
void parseLoadCmds();
void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context);
void mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context);
void UnmapSegments();
- void __attribute__((noreturn)) throwSymbolNotFound(const char* symbol, const char* referencedFrom, const char* expectedIn);
+ void __attribute__((noreturn)) throwSymbolNotFound(const LinkContext& context, const char* symbol,
+ const char* referencedFrom, const char* expectedIn);
void doImageInit(const LinkContext& context);
void doModInitFunctions(const LinkContext& context);
void setupLazyPointerHandler(const LinkContext& context);
void preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context);
+ void doInterpose(const LinkContext& context) = 0;
bool hasReferencesToWeakSymbols() const;
- uintptr_t getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, const LinkContext& context) const;
+ uintptr_t getSymbolAddress(const Symbol* sym, const ImageLoader* requestor,
+ const LinkContext& context, bool runResolver) const;
static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer);
protected:
fHasDOFSections : 1,
fHasDashInit : 1,
fHasInitializers : 1,
- fHasTerminators : 1;
+ fHasTerminators : 1,
+ fGoodFirstSegment : 1,
+ fRegisteredAsRequiresCoalescing : 1; // <rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
static uint32_t fgSymbolTableBinarySearchs;
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
image->setSlide(slide);
// for PIE record end of program, to know where to start loading dylibs
- if ( (mh->flags & MH_PIE) && !context.noPIE )
+ if ( slide != 0 )
fgNextPIEDylibAddress = (uintptr_t)image->getEnd();
image->instantiateFinish(context);
-
+ image->setMapped(context);
+
#if __i386__
// kernel may have mapped in __IMPORT segment read-only, we need it read/write to do binding
if ( image->fReadOnlyImportSegment ) {
// create image by mapping in a mach-o file
ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData,
uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info,
- unsigned int segCount, unsigned int libCount, const LinkContext& context)
+ unsigned int segCount, unsigned int libCount,
+ const struct linkedit_data_command* codeSigCmd, const LinkContext& context)
{
ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart((macho_header*)fileData, path, segCount, libCount);
try {
// record info about file
image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime);
- // mmap segments
- image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context);
-
#if CODESIGNING_SUPPORT
- // if this code is signed, validate the signature before accessing any mapped pages
- image->loadCodeSignature(fileData, fd, offsetInFat);
+ // if this image is code signed, let kernel validate signature before mapping any pages from image
+ if ( codeSigCmd != NULL )
+ image->loadCodeSignature(codeSigCmd, fd, offsetInFat);
#endif
+ // mmap segments
+ image->mapSegmentsClassic(fd, offsetInFat, lenInFat, info.st_size, context);
+
+ // finish up
+ image->instantiateFinish(context);
+
// if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path
const char* installName = image->getInstallPath();
if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') )
else
image->setPath(path);
+ // make sure path is stable before recording in dyld_all_image_infos
+ image->setMapped(context);
+
// pre-fetch content of __DATA segment for faster launches
// don't do this on prebound images or if prefetching is disabled
if ( !context.preFetchDisabled && !image->isPrebindable())
image->preFetchDATA(fd, offsetInFat, context);
- // finish up
- image->instantiateFinish(context);
}
catch (...) {
// ImageLoader::setMapped() can throw an exception to block loading of image
}
// create image by using cached mach-o file
-ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info,
+ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info,
unsigned int segCount, unsigned int libCount, const LinkContext& context)
{
ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount);
}
image->instantiateFinish(context);
+ image->setMapped(context);
}
catch (...) {
// ImageLoader::setMapped() can throw an exception to block loading of image
image->setPath(moduleName);
image->instantiateFinish(context);
+ image->setMapped(context);
}
catch (...) {
// ImageLoader::setMapped() can throw an exception to block loading of image
{
// now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
this->parseLoadCmds();
-
- // notify state change
- this->setMapped(context);
}
ImageLoaderMachOClassic::~ImageLoaderMachOClassic()
ImageLoader* ImageLoaderMachOClassic::libImage(unsigned int libIndex) const
{
const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t)));
- // mask off low bit
- return (ImageLoader*)(images[libIndex] & (-2));
+ // mask off low bits
+ return (ImageLoader*)(images[libIndex] & (-4));
}
bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const
return ((images[libIndex] & 1) != 0);
}
+bool ImageLoaderMachOClassic::libIsUpward(unsigned int libIndex) const
+{
+ const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t)));
+ // upward flag is second bit
+ return ((images[libIndex] & 2) != 0);
+}
+
-void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported)
+void ImageLoaderMachOClassic::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward)
{
uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t)));
uintptr_t value = (uintptr_t)image;
if ( reExported )
value |= 1;
+ if ( upward )
+ value |= 2;
images[libIndex] = value;
}
return ImageLoaderMachO::mapSegments(fd, offsetInFat, lenInFat, fileLen, context);
#if SPLIT_SEG_SHARED_REGION_SUPPORT
- // try to map into shared region at preferred address
- if ( mapSplitSegDylibInfoSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) == 0)
- return;
+ // don't map split-seg dylibs into shared region if shared cache is in use
+ if ( ! context.dyldLoadedAtSameAddressNeededBySharedCache ) {
+ // try to map into shared region at preferred address
+ if ( mapSplitSegDylibInfoSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) == 0)
+ return;
+ }
// if there is a problem, fall into case where we map file somewhere outside the shared region
#endif
void ImageLoaderMachOClassic::rebase(const LinkContext& context)
{
+ CRSetCrashLogMessage2(this->getPath());
register const uintptr_t slide = this->fSlide;
const uintptr_t relocBase = this->getRelocBase();
// update stats
fgTotalRebaseFixups += fDynamicInfo->nlocrel;
+ CRSetCrashLogMessage2(NULL);
}
}
-uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const Symbol* symbol) const
+uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const
{
const struct macho_nlist* sym = (macho_nlist*)symbol;
uintptr_t result = sym->n_value + fSlide;
return false;
}
-uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context) const
+uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context, bool runResolver) const
{
- return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context);
+ return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context, runResolver);
}
uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol,
// flat lookup
if ( ((undefinedSymbol->n_type & N_PEXT) != 0) && ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) {
// is a multi-module private_extern internal reference that the linker did not optimize away
- uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context);
+ uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false);
*foundIn = this;
return addr;
}
// if reference is weak_import, then it is ok, just return 0
return 0;
}
- throwSymbolNotFound(symbolName, this->getPath(), "flat namespace");
+ throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace");
}
else {
// symbol requires searching images with coalesced symbols (not done during prebinding)
this->addDynamicReference(*foundIn);
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
}
- //throwSymbolNotFound(symbolName, this->getPath(), "coalesced namespace");
+ //throwSymbolNotFound(context, symbolName, this->getPath(), "coalesced namespace");
//dyld::log("dyld: coalesced symbol %s not found in any coalesced image, falling back to two-level lookup", symbolName);
}
// if this is a real definition (not an undefined symbol) there is no ordinal
if ( (undefinedSymbol->n_type & N_TYPE) == N_SECT ) {
// static linker should never generate this case, but if it does, do something sane
- uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context);
+ uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false);
*foundIn = this;
return addr;
}
if ( context.flatExportFinder(symbolName, &sym, foundIn) )
return (*foundIn)->getExportedSymbolAddress(sym, context, this);
- throwSymbolNotFound(symbolName, this->getPath(), "dynamic lookup");
+ throwSymbolNotFound(context, symbolName, this->getPath(), "dynamic lookup");
}
else if ( ord <= libraryCount() ) {
target = libImage(ord-1);
else if ( (undefinedSymbol->n_type & N_PEXT) != 0 ) {
// don't know why the static linker did not eliminate the internal reference to a private extern definition
*foundIn = this;
- return this->getSymbolAddress(undefinedSymbol, context);
+ return this->getSymbolAddress(undefinedSymbol, context, false);
}
else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) {
// if definition not found and reference is weak return 0
}
// nowhere to be found
- throwSymbolNotFound(symbolName, this->getPath(), target->getPath());
+ throwSymbolNotFound(context, symbolName, this->getPath(), target->getPath());
}
}
return targetAddr;
}
-uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context)
+uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)())
{
throw "compressed LINKEDIT lazy binder called with classic LINKEDIT";
}
}
const struct macho_nlist* sym = &fSymbolTable[symbol_index];
//dyld::log("getAddressCoalIterator() => 0x%llX, %s symbol_index=%d, in %s\n", (uint64_t)(sym->n_value + fSlide), &fStrings[sym->n_un.n_strx], symbol_index, this->getPath());
+#if __arm__
+ // processor assumes code address with low bit set is thumb
+ if (sym->n_desc & N_ARM_THUMB_DEF)
+ return (sym->n_value | 1) + fSlide ;
+ else
+ return sym->n_value + fSlide;
+#else
return sym->n_value + fSlide;
+#endif
}
#if __arm__
// if weak and thumb subtract off extra thumb bit
if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 )
- addend += 1;
+ addend &= -2;
#endif
}
}
// to be definition address plus addend
//dyld::log("weak def, initialValue=0x%lX, undefAddr=0x%lX\n", initialValue, undefinedSymbol->n_value+fSlide);
addend = initialValue - (undefinedSymbol->n_value + fSlide);
+ #if __arm__
+ // if weak and thumb subtract off extra thumb bit
+ if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 )
+ addend &= -2;
+ #endif
}
else {
// nothing fixed up yet, addend is just initial value
void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound)
{
+ CRSetCrashLogMessage2(this->getPath());
#if __i386__
this->initializeLazyStubs(context);
#endif
// no valid prebinding, so bind symbols.
// values bound by name are stored two different ways in classic mach-o:
+ #if TEXT_RELOC_SUPPORT
+ // if there are __TEXT fixups, temporarily make __TEXT writable
+ if ( fTextSegmentBinds )
+ this->makeTextSegmentWritable(context, true);
+ #endif
+
// 1) external relocations are used for data initialized to external symbols
this->doBindExternalRelocations(context);
// if this image is in the shared cache, there is no way to reset the lazy pointers, so bind them now
this->bindIndirectSymbolPointers(context, true, forceLazysBound || fInSharedCache);
+ #if TEXT_RELOC_SUPPORT
+ // if there were __TEXT fixups, restore write protection
+ if ( fTextSegmentBinds )
+ this->makeTextSegmentWritable(context, false);
+ #endif
}
// set up dyld entry points in image
this->setupLazyPointerHandler(context);
+
+ CRSetCrashLogMessage2(NULL);
}
void ImageLoaderMachOClassic::doBindJustLazies(const LinkContext& context)
this->bindIndirectSymbolPointers(context, false, true);
}
+void ImageLoaderMachOClassic::doInterpose(const LinkContext& context)
+{
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interposing %lu tuples onto: %s\n", fgInterposingTuples.size(), this->getPath());
+
+ // scan indirect symbols
+ const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds;
+ const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_SEGMENT_COMMAND:
+ {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
+ const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ const uint8_t type = sect->flags & SECTION_TYPE;
+ if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) {
+ const uint32_t pointerCount = sect->size / sizeof(uintptr_t);
+ uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide);
+ for (uint32_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) {
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ // replace all references to 'replacee' with 'replacement'
+ if ( (symbolPointers[pointerIndex] == it->replacee) && (this != it->replacementImage) ) {
+ if ( context.verboseInterposing ) {
+ dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n",
+ &symbolPointers[pointerIndex], it->replacee, it->replacement, this->getPath());
+ }
+ symbolPointers[pointerIndex] = it->replacement;
+ }
+ }
+ }
+ }
+ #if __i386__
+ // i386 has special self-modifying stubs that might be prebound to "JMP rel32" that need checking
+ else if ( (type == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) {
+ // check each jmp entry in this section
+ uint8_t* start = (uint8_t*)(sect->addr + this->fSlide);
+ uint8_t* end = start + sect->size;
+ for (uint8_t* entry = start; entry < end; entry += 5) {
+ if ( entry[0] == 0xE9 ) { // 0xE9 == JMP
+ uint32_t rel32 = *((uint32_t*)&entry[1]); // assume unaligned load of uint32_t is ok
+ uint32_t target = (uint32_t)&entry[5] + rel32;
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ // replace all references to 'replacee' with 'replacement'
+ if ( (it->replacee == target) && (this != it->replacementImage) ) {
+ if ( context.verboseInterposing ) {
+ dyld::log("dyld: interposing: at %p replace JMP 0x%lX with JMP 0x%lX in %s\n",
+ &entry[1], it->replacee, it->replacement, this->getPath());
+ }
+ uint32_t newRel32 = it->replacement - (uint32_t)&entry[5];
+ *((uint32_t*)&entry[1]) = newRel32; // assume unaligned store of uint32_t is ok
+ }
+ }
+ }
+ }
+ }
+ #endif
+ }
+ }
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+
+ // scan external relocations
+ const uintptr_t relocBase = this->getRelocBase();
+ const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]);
+ const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel];
+ for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) {
+ if (reloc->r_length == RELOC_SIZE) {
+ switch(reloc->r_type) {
+ case POINTER_RELOC:
+ {
+ uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase));
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ // replace all references to 'replacee' with 'replacement'
+ if ( (*location == it->replacee) && (this != it->replacementImage) ) {
+ if ( context.verboseInterposing ) {
+ dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n",
+ location, it->replacee, it->replacement, this->getPath());
+ }
+ *location = it->replacement;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+
const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const void** closestAddr) const
{
uintptr_t targetAddress = (uintptr_t)addr - fSlide;
}
}
if ( bestSymbol != NULL ) {
+#if __arm__
+ if (bestSymbol->n_desc & N_ARM_THUMB_DEF)
+ *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide);
+ else
+ *closestAddr = (void*)(bestSymbol->n_value + fSlide);
+#else
*closestAddr = (void*)(bestSymbol->n_value + fSlide);
+#endif
return &fStrings[bestSymbol->n_un.n_strx];
}
return NULL;
unsigned int segCount, unsigned int libCount, const LinkContext& context);
static ImageLoaderMachOClassic* instantiateFromFile(const char* path, int fd, const uint8_t* fileData,
uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info,
- unsigned int segCount, unsigned int libCount, const LinkContext& context);
- static ImageLoaderMachOClassic* instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info,
+ unsigned int segCount, unsigned int libCount,
+ const struct linkedit_data_command* codeSigCmd, const LinkContext& context);
+ static ImageLoaderMachOClassic* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info,
unsigned int segCount, unsigned int libCount, const LinkContext& context);
static ImageLoaderMachOClassic* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len,
unsigned int segCount, unsigned int libCount, const LinkContext& context);
virtual ImageLoader* libImage(unsigned int) const;
virtual bool libReExported(unsigned int) const;
- virtual void setLibImage(unsigned int, ImageLoader*, bool);
+ virtual bool libIsUpward(unsigned int) const;
+ virtual void setLibImage(unsigned int, ImageLoader*, bool, bool);
virtual void doBind(const LinkContext& context, bool forceLazysBound);
virtual void doBindJustLazies(const LinkContext& context);
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context);
- virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context);
+ virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)());
virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const;
virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder);
virtual bool incrementCoalIterator(CoalIterator&);
virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, const LinkContext& context);
protected:
+ virtual void doInterpose(const LinkContext& context);
virtual void setDyldInfo(const dyld_info_command*) {}
virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*);
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const;
virtual void rebase(const LinkContext& context);
virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const;
virtual bool containsSymbol(const void* addr) const;
- virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const;
+ virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const;
virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const;
virtual const char* exportedSymbolName(const Symbol* symbol) const;
virtual unsigned int exportedSymbolCount() const;
static bool symbolIsWeakDefinition(const struct macho_nlist* symbol);
uintptr_t resolveUndefined(const LinkContext& context, const struct macho_nlist* symbol, bool twoLevel,
bool dontCoalesce, const ImageLoader **foundIn);
- uintptr_t getSymbolAddress(const macho_nlist*, const LinkContext& context) const;
+ uintptr_t getSymbolAddress(const macho_nlist*, const LinkContext& context, bool runResolver) const;
bool isAddrInSection(uintptr_t addr, uint8_t sectionIndex);
void doBindExternalRelocations(const LinkContext& context);
uintptr_t bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect,
uint64_t slice = *p & 0x7f;
- if (bit >= 64 || slice << bit >> bit != slice)
- dyld::throwf("uleb128 too big");
+ if (bit > 63)
+ dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result);
else {
result |= (slice << bit);
bit += 7;
}
- }
- while (*p++ & 0x80);
+ } while (*p++ & 0x80);
return result;
}
image->setSlide(slide);
// for PIE record end of program, to know where to start loading dylibs
- if ( (mh->flags & MH_PIE) && !context.noPIE )
+ if ( slide != 0 )
fgNextPIEDylibAddress = (uintptr_t)image->getEnd();
image->setNeverUnload();
image->instantiateFinish(context);
+ image->setMapped(context);
if ( context.verboseMapping ) {
dyld::log("dyld: Main executable mapped %s\n", path);
// create image by mapping in a mach-o file
ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData,
uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info,
- unsigned int segCount, unsigned int libCount, const LinkContext& context)
+ unsigned int segCount, unsigned int libCount,
+ const struct linkedit_data_command* codeSigCmd, const LinkContext& context)
{
ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount);
// record info about file
image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime);
+ #if CODESIGNING_SUPPORT
+ // if this image is code signed, let kernel validate signature before mapping any pages from image
+ if ( codeSigCmd != NULL )
+ image->loadCodeSignature(codeSigCmd, fd, offsetInFat);
+ #endif
+
// mmap segments
image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context);
else
image->setPath(path);
+ // make sure path is stable before recording in dyld_all_image_infos
+ image->setMapped(context);
+
// pre-fetch content of __DATA and __LINKEDIT segment for faster launches
// don't do this on prebound images or if prefetching is disabled
if ( !context.preFetchDisabled && !image->isPrebindable()) {
}
// create image by using cached mach-o file
-ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info,
- unsigned int segCount, unsigned int libCount, const LinkContext& context)
+ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide,
+ const struct stat& info, unsigned int segCount,
+ unsigned int libCount, const LinkContext& context)
{
ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount);
try {
// remember this is from shared cache and cannot be unloaded
image->fInSharedCache = true;
+ image->fGoodFirstSegment = true;
image->setNeverUnload();
+ image->setSlide(slide);
// segments already mapped in cache
if ( context.verboseMapping ) {
}
image->instantiateFinish(context);
+ image->setMapped(context);
}
catch (...) {
// ImageLoader::setMapped() can throw an exception to block loading of image
image->setPath(moduleName);
image->instantiateFinish(context);
+ image->setMapped(context);
}
catch (...) {
// ImageLoader::setMapped() can throw an exception to block loading of image
{
// now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide
this->parseLoadCmds();
-
- // notify state change
- this->setMapped(context);
}
uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const
ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const
{
const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
- // mask off low bit
- return (ImageLoader*)(images[libIndex] & (-2));
+ // mask off low bits
+ return (ImageLoader*)(images[libIndex] & (-4));
}
bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const
return ((images[libIndex] & 1) != 0);
}
+bool ImageLoaderMachOCompressed::libIsUpward(unsigned int libIndex) const
+{
+ const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
+ // re-export flag is second bit
+ return ((images[libIndex] & 2) != 0);
+}
+
-void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported)
+void ImageLoaderMachOCompressed::setLibImage(unsigned int libIndex, ImageLoader* image, bool reExported, bool upward)
{
uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t)));
uintptr_t value = (uintptr_t)image;
if ( reExported )
value |= 1;
+ if ( upward )
+ value |= 2;
images[libIndex] = value;
}
const char* adstr = "sequential";
if ( advise == MADV_FREE )
adstr = "free";
- dyld::log("%18s %s 0x%0lX -> 0x%0lX\n", "__LINKEDIT", adstr, start, end-1);
+ dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath());
}
}
void ImageLoaderMachOCompressed::rebase(const LinkContext& context)
{
+ CRSetCrashLogMessage2(this->getPath());
const uintptr_t slide = this->fSlide;
const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off;
const uint8_t* const end = &start[fDyldInfo->rebase_size];
free((void*)msg);
throw newMsg;
}
+ CRSetCrashLogMessage2(NULL);
}
-
-
-
-const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const
+//
+// This function is the hotspot of symbol lookup. It was pulled out of findExportedSymbol()
+// to enable it to be re-written in assembler if needed.
+//
+const uint8_t* ImageLoaderMachOCompressed::trieWalk(const uint8_t* start, const uint8_t* end, const char* s)
{
- //dyld::log("findExportedSymbolCompressed(%s) in %s\n", symbol, this->getShortName());
- if ( fDyldInfo->export_size == 0 )
- return NULL;
- ++ImageLoaderMachO::fgSymbolTrieSearchs;
- const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off];
- const uint8_t* end = &start[fDyldInfo->export_size];
const uint8_t* p = start;
- const char* s = symbol;
- do {
- const uint8_t terminalSize = *p++;
- const uint8_t* children = p + terminalSize;
+ while ( p != NULL ) {
+ uint32_t terminalSize = *p++;
+ if ( terminalSize > 127 ) {
+ // except for re-export-with-rename, all terminal sizes fit in one byte
+ --p;
+ terminalSize = read_uleb128(p, end);
+ }
if ( (*s == '\0') && (terminalSize != 0) ) {
- // found match, return pointer to terminal part of node
- //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p);
- if ( foundIn != NULL )
- *foundIn = (ImageLoader*)this;
- return (Symbol*)p;
+ //dyld::log("trieWalk(%p) returning %p\n", start, p);
+ return p;
}
- const uint8_t childrenCount = *children++;
- const uint8_t* e = children;
- const uint8_t* newNode = NULL;
- for (uint8_t i=0; i < childrenCount; ++i) {
+ const uint8_t* children = p + terminalSize;
+ //dyld::log("trieWalk(%p) sym=%s, terminalSize=%d, children=%p\n", start, s, terminalSize, children);
+ uint8_t childrenRemaining = *children++;
+ p = children;
+ uint32_t nodeOffset = 0;
+ for (; childrenRemaining > 0; --childrenRemaining) {
const char* ss = s;
+ //dyld::log("trieWalk(%p) child str=%s\n", start, (char*)p);
bool wrongEdge = false;
- //dyld::log("findExportedSymbol() looking at edge %s for match to %s\n", e, s);
// scan whole edge to get to next edge
// if edge is longer than target symbol name, don't read past end of symbol name
- while ( *e != '\0' ) {
+ char c = *p;
+ while ( c != '\0' ) {
if ( !wrongEdge ) {
- if ( *e != *ss++ )
+ if ( c != *ss )
wrongEdge = true;
+ ++ss;
}
- ++e;
+ ++p;
+ c = *p;
}
if ( wrongEdge ) {
// advance to next child
- ++e;
- read_uleb128(e, end);
+ ++p; // skip over zero terminator
+ // skip over uleb128 until last byte is found
+ while ( (*p & 0x80) != 0 )
+ ++p;
+ ++p; // skil over last byte of uleb128
}
else {
- // the symbol so far matches this edge (child)
+ // the symbol so far matches this edge (child)
// so advance to the child's node
- ++e;
- uint32_t nodeOffset = read_uleb128(e, end);
- newNode = &start[nodeOffset];
+ ++p;
+ nodeOffset = read_uleb128(p, end);
s = ss;
- //dyld::log("findExportedSymbol() found matching edge advancing to node 0x%x\n", nodeOffset);
+ //dyld::log("trieWalk() found matching edge advancing to node 0x%x\n", nodeOffset);
break;
}
}
- if ( newNode != NULL )
- p = newNode;
+ if ( nodeOffset != 0 )
+ p = &start[nodeOffset];
+ else
+ p = NULL;
+ }
+ //dyld::log("trieWalk(%p) return NULL\n", start);
+ return NULL;
+}
+
+
+const ImageLoader::Symbol* ImageLoaderMachOCompressed::findExportedSymbol(const char* symbol, const ImageLoader** foundIn) const
+{
+ //dyld::log("Compressed::findExportedSymbol(%s) in %s\n", symbol, this->getShortName());
+ if ( fDyldInfo->export_size == 0 )
+ return NULL;
+#if LOG_BINDINGS
+ dyld::logBindings("%s: %s\n", this->getShortName(), symbol);
+#endif
+ ++ImageLoaderMachO::fgSymbolTrieSearchs;
+ const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off];
+ const uint8_t* end = &start[fDyldInfo->export_size];
+ const uint8_t* foundNodeStart = this->trieWalk(start, end, symbol);
+ if ( foundNodeStart != NULL ) {
+ const uint8_t* p = foundNodeStart;
+ const uint32_t flags = read_uleb128(p, end);
+ // found match, return pointer to terminal part of node
+ if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) {
+ // re-export from another dylib, lookup there
+ const uint32_t ordinal = read_uleb128(p, end);
+ const char* importedName = (char*)p;
+ if ( importedName[0] == '\0' )
+ importedName = symbol;
+ if ( (ordinal > 0) && (ordinal <= libraryCount()) ) {
+ const ImageLoader* reexportedFrom = libImage(ordinal-1);
+ //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName);
+ return reexportedFrom->findExportedSymbol(importedName, true, foundIn);
+ }
+ else {
+ //dyld::throwf("bad mach-o binary, library ordinal (%u) invalid (max %u) for re-exported symbol %s in %s",
+ // ordinal, libraryCount(), symbol, this->getPath());
+ }
+ }
else {
- //dyld::log("findExportedSymbol(%s) in %s failed\n", symbol, this->getShortName());
- return NULL;
+ //dyld::log("findExportedSymbol(%s) in %s found match, returning %p\n", symbol, this->getShortName(), p);
+ if ( foundIn != NULL )
+ *foundIn = (ImageLoader*)this;
+ // return pointer to terminal part of node
+ return (Symbol*)foundNodeStart;
}
- } while ( true );
+ }
+ return NULL;
}
}
-uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const Symbol* symbol) const
+uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const
{
const uint8_t* exportNode = (uint8_t*)symbol;
const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off;
const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size;
if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) )
throw "symbol is not in trie";
+ //dyld::log("exportedSymbolAddress(): node=%p, nodeOffset=0x%04X in %s\n", symbol, (int)((uint8_t*)symbol - exportTrieStart), this->getShortName());
uint32_t flags = read_uleb128(exportNode, exportTrieEnd);
- if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR )
+ if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) {
+ if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) {
+ // this node has a stub and resolver, run the resolver to get target address
+ read_uleb128(exportNode, exportTrieEnd); // skip over stub
+ typedef uintptr_t (*ResolverProc)(void);
+ ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData);
+ uintptr_t result = (*resolver)();
+ if ( context.verboseBind )
+ dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, result);
+ return result;
+ }
+ return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData;
+ }
+ else if ( (flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) == EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL ) {
+ if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER )
+ dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol);
return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData;
+ }
else
- throw "unsupported exported symbol kind";
+ dyld::throwf("unsupported exported symbol kind. flags=%d at node=%p", flags, symbol);
}
bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const
-uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, const ImageLoader** foundIn)
+uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import,
+ bool runResolver, const ImageLoader** foundIn)
{
const Symbol* sym;
if ( context.flatExportFinder(symbolName, &sym, foundIn) ) {
if ( (*foundIn != this) && !(*foundIn)->neverUnload() )
this->addDynamicReference(*foundIn);
- return (*foundIn)->getExportedSymbolAddress(sym, context, this);
+ return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
}
// if a bundle is loaded privately the above will not find its exports
if ( this->isBundle() && this->hasHiddenExports() ) {
// look in self for needed symbol
sym = this->ImageLoaderMachO::findExportedSymbol(symbolName, false, foundIn);
if ( sym != NULL )
- return (*foundIn)->getExportedSymbolAddress(sym, context, this);
+ return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
}
if ( weak_import ) {
// definition can't be found anywhere, ok because it is weak, just return 0
return 0;
}
- throwSymbolNotFound(symbolName, this->getPath(), "flat namespace");
+ throwSymbolNotFound(context, symbolName, this->getPath(), "flat namespace");
}
uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import,
- const char* symbolName, const ImageLoader** foundIn)
+ const char* symbolName, bool runResolver, const ImageLoader** foundIn)
{
// two level lookup
const Symbol* sym = targetImage->findExportedSymbol(symbolName, true, foundIn);
if ( sym != NULL ) {
- return (*foundIn)->getExportedSymbolAddress(sym, context, this);
+ return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver);
}
if ( weak_import ) {
}
// nowhere to be found
- throwSymbolNotFound(symbolName, this->getPath(), targetImage->getPath());
+ throwSymbolNotFound(context, symbolName, this->getPath(), targetImage->getPath());
}
uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName,
uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage,
- LastLookup* last)
+ LastLookup* last, bool runResolver)
{
*targetImage = NULL;
bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT);
uintptr_t symbolAddress;
if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) {
- symbolAddress = this->resolveFlat(context, symbolName, weak_import, targetImage);
+ symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage);
}
else {
if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) {
}
}
else {
- symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, targetImage);
+ symbolAddress = resolveTwolevel(context, *targetImage, weak_import, symbolName, runResolver, targetImage);
}
}
}
uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
- uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg, LastLookup* last)
+ uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg,
+ LastLookup* last, bool runResolver)
{
const ImageLoader* targetImage;
uintptr_t symbolAddress;
// resolve symbol
- symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last);
+ symbolAddress = this->resolve(context, symbolName, symboFlags, libraryOrdinal, &targetImage, last, runResolver);
// do actual update
return this->bindLocation(context, addr, symbolAddress, targetImage, type, symbolName, addend, msg);
void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound)
{
+ CRSetCrashLogMessage2(this->getPath());
+
// if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind
// note: flat-namespace binaries need to have imports rebound (even if correctly prebound)
if ( this->usablePrebinding(context) ) {
// don't need to bind
}
else {
+
+ #if TEXT_RELOC_SUPPORT
+ // if there are __TEXT fixups, temporarily make __TEXT writable
+ if ( fTextSegmentBinds )
+ this->makeTextSegmentWritable(context, true);
+ #endif
+
// run through all binding opcodes
eachBind(context, &ImageLoaderMachOCompressed::bindAt);
- // if this image is in the shared cache, but depends on someting no longer in the shared cache,
+ #if TEXT_RELOC_SUPPORT
+ // if there were __TEXT fixups, restore write protection
+ if ( fTextSegmentBinds )
+ this->makeTextSegmentWritable(context, false);
+ #endif
+
+ // if this image is in the shared cache, but depends on something no longer in the shared cache,
// there is no way to reset the lazy pointers, so force bind them now
- if ( forceLazysBound || fInSharedCache )
+ if ( forceLazysBound || fInSharedCache )
this->doBindJustLazies(context);
+
+ // this image is in cache, but something below it is not. If
+ // this image has lazy pointer to a resolver function, then
+ // the stub may have been altered to point to a shared lazy pointer.
+ if ( fInSharedCache )
+ this->updateOptimizedLazyPointers(context);
+
+ // tell kernel we are done with chunks of LINKEDIT
+ if ( !context.preFetchDisabled )
+ this->markFreeLINKEDIT(context);
}
// set up dyld entry points in image
// do last so flat main executables will have __dyld or __program_vars set up
this->setupLazyPointerHandler(context);
-
- // tell kernel we are done with chunks of LINKEDIT
- if ( !context.preFetchDisabled )
- this->markFreeLINKEDIT(context);
+ CRSetCrashLogMessage2(NULL);
}
case BIND_OPCODE_DO_BIND:
if ( address >= segmentEndAddress )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
+ (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
if ( address >= segmentEndAddress )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
+ (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += read_uleb128(p, end) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
if ( address >= segmentEndAddress )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
+ (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB:
for (uint32_t i=0; i < count; ++i) {
if ( address >= segmentEndAddress )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
+ (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false);
address += skip + sizeof(intptr_t);
}
break;
case BIND_OPCODE_DO_BIND:
if ( address >= segmentEndAddress )
throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
- (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "lazy forced", NULL);
+ (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "lazy forced", NULL, true);
address += sizeof(intptr_t);
break;
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
}
-uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context)
+uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context,
+ void (*lock)(), void (*unlock)())
{
+ // <rdar://problem/8663923> race condition with flat-namespace lazy binding
+ if ( this->usesTwoLevelNameSpace() ) {
+ // two-level namespace lookup does not require lock because dependents can't be unloaded before this image
+ }
+ else {
+ // acquire dyld global lock
+ if ( lock != NULL )
+ lock();
+ }
+
const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off;
const uint8_t* const end = &start[fDyldInfo->lazy_bind_size];
- if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size )
- throw "fast lazy bind offset out of range";
+ if ( lazyBindingInfoOffset > fDyldInfo->lazy_bind_size ) {
+ dyld::throwf("fast lazy bind offset out of range (%u, max=%u) in image %s",
+ lazyBindingInfoOffset, fDyldInfo->lazy_bind_size, this->getPath());
+ }
uint8_t type = BIND_TYPE_POINTER;
uintptr_t address = 0;
address = segActualLoadAddress(immediate) + read_uleb128(p, end);
break;
case BIND_OPCODE_DO_BIND:
- result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL);
+
+
+ result = this->bindAt(context, address, type, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true);
break;
case BIND_OPCODE_SET_ADDEND_SLEB:
case BIND_OPCODE_ADD_ADDR_ULEB:
dyld::throwf("bad lazy bind opcode %d", *p);
}
}
+
+ if ( !this->usesTwoLevelNameSpace() ) {
+ // release dyld global lock
+ if ( unlock != NULL )
+ unlock();
+ }
return result;
}
this->addDynamicReference(targetImage);
}
+uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
+ uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver)
+{
+ if ( type == BIND_TYPE_POINTER ) {
+ uintptr_t* fixupLocation = (uintptr_t*)addr;
+ uintptr_t value = *fixupLocation;
+ for (std::vector<InterposeTuple>::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) {
+ // replace all references to 'replacee' with 'replacement'
+ if ( (value == it->replacee) && (this != it->replacementImage) ) {
+ if ( context.verboseInterposing ) {
+ dyld::log("dyld: interposing: at %p replace 0x%lX with 0x%lX in %s\n",
+ fixupLocation, it->replacee, it->replacement, this->getPath());
+ }
+ *fixupLocation = it->replacement;
+ }
+ }
+ }
+ return 0;
+}
+
+void ImageLoaderMachOCompressed::doInterpose(const LinkContext& context)
+{
+ if ( context.verboseInterposing )
+ dyld::log("dyld: interposing %lu tuples onto image: %s\n", fgInterposingTuples.size(), this->getPath());
+
+ // update prebound symbols
+ eachBind(context, &ImageLoaderMachOCompressed::interposeAt);
+ eachLazyBind(context, &ImageLoaderMachOCompressed::interposeAt);
+}
}
}
if ( bestSymbol != NULL ) {
+#if __arm__
+ if (bestSymbol->n_desc & N_ARM_THUMB_DEF)
+ *closestAddr = (void*)((bestSymbol->n_value | 1) + fSlide);
+ else
+ *closestAddr = (void*)(bestSymbol->n_value + fSlide);
+#else
*closestAddr = (void*)(bestSymbol->n_value + fSlide);
+#endif
return &symbolTableStrings[bestSymbol->n_un.n_strx];
}
return NULL;
}
#endif
+
+#if __arm__ || __x86_64__
+void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr)
+{
+#if __arm__
+ uint32_t* instructions = (uint32_t*)stub;
+ // sanity check this is a stub we understand
+ if ( (instructions[0] != 0xe59fc004) || (instructions[1] != 0xe08fc00c) || (instructions[2] != 0xe59cf000) )
+ return;
+
+ void** lazyPointerAddr = (void**)(instructions[3] + (stub + 12));
+#endif
+#if __x86_64__
+ // sanity check this is a stub we understand
+ if ( (stub[0] != 0xFF) || (stub[1] != 0x25) )
+ return;
+ int32_t ripOffset = *((int32_t*)(&stub[2]));
+ void** lazyPointerAddr = (void**)(ripOffset + stub + 6);
+#endif
+
+ // if stub does not use original lazy pointer (meaning it was optimized by update_dyld_shared_cache)
+ if ( lazyPointerAddr != originalLazyPointerAddr ) {
+ // copy newly re-bound lazy pointer value to shared lazy pointer
+ *lazyPointerAddr = *originalLazyPointerAddr;
+ }
+}
+#endif
+
+
+// <rdar://problem/8890875> overriding shared cache dylibs with resolvers fails
+void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context)
+{
+#if __arm__ || __x86_64__
+ // find stubs and lazy pointer sections
+ const struct macho_section* stubsSection = NULL;
+ const struct macho_section* lazyPointerSection = NULL;
+ const dysymtab_command* dynSymbolTable = NULL;
+ const macho_header* mh = (macho_header*)fMachOData;
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)];
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if (cmd->cmd == LC_SEGMENT_COMMAND) {
+ const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
+ const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command));
+ const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ const uint8_t type = sect->flags & SECTION_TYPE;
+ if ( type == S_SYMBOL_STUBS )
+ stubsSection = sect;
+ else if ( type == S_LAZY_SYMBOL_POINTERS )
+ lazyPointerSection = sect;
+ }
+ }
+ else if ( cmd->cmd == LC_DYSYMTAB ) {
+ dynSymbolTable = (struct dysymtab_command*)cmd;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+
+ // sanity check
+ if ( dynSymbolTable == NULL )
+ return;
+ if ( (stubsSection == NULL) || (lazyPointerSection == NULL) )
+ return;
+ const uint32_t stubsCount = stubsSection->size / stubsSection->reserved2;
+ const uint32_t lazyPointersCount = lazyPointerSection->size / sizeof(void*);
+ if ( stubsCount != lazyPointersCount )
+ return;
+ const uint32_t stubsIndirectTableOffset = stubsSection->reserved1;
+ const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1;
+ if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms )
+ return;
+ if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms )
+ return;
+
+ // walk stubs and lazy pointers
+ const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff];
+ void** const lazyPointersStartAddr = (void**)(lazyPointerSection->addr + this->fSlide);
+ uint8_t* const stubsStartAddr = (uint8_t*)(stubsSection->addr + this->fSlide);
+ uint8_t* stub = stubsStartAddr;
+ void** lpa = lazyPointersStartAddr;
+ for(uint32_t i=0; i < stubsCount; ++i, stub += stubsSection->reserved2, ++lpa) {
+ // sanity check symbol index of stub and lazy pointer match
+ if ( indirectTable[stubsIndirectTableOffset+i] != indirectTable[lazyPointersIndirectTableOffset+i] )
+ continue;
+ this->updateAlternateLazyPointer(stub, lpa);
+ }
+
+#endif
+}
+
+
unsigned int segCount, unsigned int libCount, const LinkContext& context);
static ImageLoaderMachOCompressed* instantiateFromFile(const char* path, int fd, const uint8_t* fileData,
uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info,
- unsigned int segCount, unsigned int libCount, const LinkContext& context);
- static ImageLoaderMachOCompressed* instantiateFromCache(const macho_header* mh, const char* path, const struct stat& info,
+ unsigned int segCount, unsigned int libCount,
+ const struct linkedit_data_command* codeSigCmd, const LinkContext& context);
+ static ImageLoaderMachOCompressed* instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info,
unsigned int segCount, unsigned int libCount, const LinkContext& context);
static ImageLoaderMachOCompressed* instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len,
unsigned int segCount, unsigned int libCount, const LinkContext& context);
virtual ImageLoader* libImage(unsigned int) const;
virtual bool libReExported(unsigned int) const;
- virtual void setLibImage(unsigned int, ImageLoader*, bool);
+ virtual bool libIsUpward(unsigned int) const;
+ virtual void setLibImage(unsigned int, ImageLoader*, bool, bool);
virtual void doBind(const LinkContext& context, bool forceLazysBound);
virtual void doBindJustLazies(const LinkContext& context);
virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context);
- virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context);
+ virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)());
virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const;
virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder);
virtual bool incrementCoalIterator(CoalIterator&);
protected:
+ virtual void doInterpose(const LinkContext& context);
virtual void setDyldInfo(const dyld_info_command* dyldInfo) { fDyldInfo = dyldInfo; }
virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) {}
virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; }
virtual void rebase(const LinkContext& context);
virtual const ImageLoader::Symbol* findExportedSymbol(const char* name, const ImageLoader** foundIn) const;
virtual bool containsSymbol(const void* addr) const;
- virtual uintptr_t exportedSymbolAddress(const Symbol* symbol) const;
+ virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, bool runResolver) const;
virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const;
virtual const char* exportedSymbolName(const Symbol* symbol) const;
virtual unsigned int exportedSymbolCount() const;
typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type,
const char* symbolName, uint8_t symboFlags, intptr_t addend, int libraryOrdinal,
- const char* msg, LastLookup* last);
+ const char* msg, LastLookup* last, bool runResolver);
void eachLazyBind(const LinkContext& context, bind_handler);
void eachBind(const LinkContext& context, bind_handler);
const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos);
uintptr_t bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName,
uint8_t symboFlags, intptr_t addend, int libraryOrdinal, const char* msg,
- LastLookup* last);
+ LastLookup* last, bool runResolver=false);
void bindCompressed(const LinkContext& context);
void throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex,
const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos);
uintptr_t resolve(const LinkContext& context, const char* symbolName,
- uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage, LastLookup* last = NULL);
- uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import,
+ uint8_t symboFlags, int libraryOrdinal, const ImageLoader** targetImage,
+ LastLookup* last = NULL, bool runResolver=false);
+ uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver,
const ImageLoader** foundIn);
uintptr_t resolveCoalesced(const LinkContext& context, const char* symbolName, const ImageLoader** foundIn);
uintptr_t resolveTwolevel(const LinkContext& context, const ImageLoader* targetImage, bool weak_import,
- const char* symbolName, const ImageLoader** foundIn);
-
+ const char* symbolName, bool runResolver, const ImageLoader** foundIn);
+ uintptr_t interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*,
+ uint8_t, intptr_t, int, const char*, LastLookup*, bool runResolver);
+ static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* s);
+ void updateOptimizedLazyPointers(const LinkContext& context);
+ void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr);
+
const struct dyld_info_command* fDyldInfo;
};
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
+#include <dirent.h>
#include <sys/param.h>
#include <mach/mach_time.h> // mach_absolute_time()
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <mach-o/ldsyms.h>
#include <sys/mman.h>
#include <sys/dtrace.h>
#include <libkern/OSAtomic.h>
+#include <Availability.h>
+#include <Kernel/sys/codesign.h>
+
#ifndef CPU_SUBTYPE_ARM_V5TEJ
#define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7)
#ifndef CPU_SUBTYPE_ARM_V7
#define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9)
#endif
+#ifndef LC_DYLD_ENVIRONMENT
+ #define LC_DYLD_ENVIRONMENT 0x27
+#endif
+
+#ifndef VM_PROT_SLIDE
+ #define VM_PROT_SLIDE 0x20
+#endif
#include <vector>
#include <algorithm>
extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
extern void removeImageFromAllImages(const mach_header* mh);
extern void setAlImageInfosHalt(const char* message, uintptr_t flags);
+extern void addNonSharedCacheImageUUID(const dyld_uuid_info& info);
+extern const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]);
// magic so CrashReporter logs message
extern "C" {
const char* const * DYLD_FALLBACK_LIBRARY_PATH;
const char* const * DYLD_INSERT_LIBRARIES;
const char* const * LD_LIBRARY_PATH; // for unix conformance
+ const char* const * DYLD_VERSIONED_LIBRARY_PATH;
+ const char* const * DYLD_VERSIONED_FRAMEWORK_PATH;
bool DYLD_PRINT_LIBRARIES;
bool DYLD_PRINT_LIBRARIES_POST_LAUNCH;
bool DYLD_BIND_AT_LAUNCH;
bool DYLD_PRINT_OPTS;
bool DYLD_PRINT_ENV;
bool DYLD_DISABLE_DOFS;
+ bool DYLD_PRINT_CS_NOTIFICATIONS;
// DYLD_SHARED_CACHE_DONT_VALIDATE ==> sSharedCacheIgnoreInodeAndTimeStamp
// DYLD_SHARED_CACHE_DIR ==> sSharedCacheDir
// DYLD_ROOT_PATH ==> gLinkContext.rootPaths
// DYLD_NEW_LOCAL_SHARED_REGIONS ==> gLinkContext.sharedRegionMode
// DYLD_SHARED_REGION ==> gLinkContext.sharedRegionMode
// DYLD_PRINT_WARNINGS ==> gLinkContext.verboseWarnings
+ // DYLD_PRINT_RPATHS ==> gLinkContext.verboseRPaths
+ // DYLD_PRINT_INTERPOSING ==> gLinkContext.verboseInterposing
};
typedef std::vector<dyld_image_state_change_handler> StateHandlers;
struct RegisteredDOF { const mach_header* mh; int registrationID; };
+struct DylibOverride { const char* installName; const char* override; };
// all global state
static const char* sExecPath = NULL;
static const macho_header* sMainExecutableMachHeader = NULL;
+#if CPU_SUBTYPES_SUPPORTED
static cpu_type_t sHostCPU;
static cpu_subtype_t sHostCPUsubtype;
+#endif
static ImageLoader* sMainExecutable = NULL;
static bool sProcessIsRestricted = false;
static unsigned int sInsertedDylibCount = 0;
static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked
#if DYLD_SHARED_CACHE_SUPPORT
static const dyld_cache_header* sSharedCache = NULL;
+static long sSharedCacheSlide = 0;
static bool sSharedCacheIgnoreInodeAndTimeStamp = false;
-static const char* sSharedCacheDir = DYLD_SHARED_CACHE_DIR;
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ bool gSharedCacheOverridden = false;
+ static const char* sSharedCacheDir = IPHONE_DYLD_SHARED_CACHE_DIR;
+ static bool sDylibsOverrideCache = false;
+#else
+ static const char* sSharedCacheDir = MACOSX_DYLD_SHARED_CACHE_DIR;
+#endif
#endif
ImageLoader::LinkContext gLinkContext;
bool gLogAPIs = false;
#if SUPPORT_OLD_CRT_INITIALIZATION
bool gRunInitializersOldWay = false;
#endif
-
+static std::vector<DylibOverride> sDylibOverrides;
//
// The MappedRanges structure is used for fast address->image lookups.
//#define ALTERNATIVE_LOGFILE "/dev/console"
static int sLogfile = STDERR_FILENO;
+#if LOG_BINDINGS
+static int sBindingsLogfile = -1;
+static void mysprintf(char* dst, const char* format, ...)
+{
+ _SIMPLE_STRING buf = _simple_salloc();
+ if ( buf != NULL ) {
+ va_list list;
+ va_start(list, format);
+ _simple_vsprintf(buf, format, list);
+ va_end(list);
+ strcpy(dst, _simple_string(buf));
+ _simple_sfree(buf);
+ }
+ else {
+ strcpy(dst, "out of memory");
+ }
+}
+void logBindings(const char* format, ...)
+{
+ if ( sBindingsLogfile != -1 ) {
+ va_list list;
+ va_start(list, format);
+ _simple_vdprintf(sBindingsLogfile, format, list);
+ va_end(list);
+ }
+}
+
+#endif
+
void log(const char* format, ...)
{
va_list list;
}
+
// notify gdb about these new images
-static const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
+static const char* updateAllImages(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
{
- addImagesToAllImages(infoCount, info);
+ // <rdar://problem/8812589> don't add images without paths to all-image-info-list
+ if ( info[0].imageFilePath != NULL )
+ addImagesToAllImages(infoCount, info);
return NULL;
}
}
}
}
+ if ( state == dyld_image_state_mapped ) {
+ // <rdar://problem/7008875> Save load addr + UUID for images from outside the shared cache
+ if ( !image->inSharedCache() ) {
+ dyld_uuid_info info;
+ if ( image->getUUID(info.imageUUID) ) {
+ info.imageLoadAddress = image->machHeader();
+ addNonSharedCacheImageUUID(info);
+ }
+ }
+ }
#if CORESYMBOLICATION_SUPPORT
// mach message csdlc about dynamically loaded images
- if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) {
- CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage;
- if ( connection->is_valid_version() ) {
- if ( state == dyld_image_state_terminated ) {
+ if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) {
+ if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
+ dyld::log("dyld core symbolication unload notification: %p %s\n", image->machHeader(), image->getPath());
+ }
+ if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) {
+ CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage;
+ if ( connection->is_valid_version() ) {
coresymbolication_unload_image(connection, image);
}
}
}
-#endif
+#endif
+}
+
+
+
+
+//
+// Normally, dyld_all_image_infos is only updated in batches after an entire
+// graph is loaded. But if there is an error loading the initial set of
+// dylibs needed by the main executable, dyld_all_image_infos is not yet set
+// up, leading to usually brief crash logs.
+//
+// This function manually adds the images loaded so far to dyld_all_image_infos.
+// It should only be called before terminating.
+//
+void syncAllImages()
+{
+ for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) {
+ dyld_image_info info;
+ ImageLoader* image = *it;
+ info.imageLoadAddress = image->machHeader();
+ info.imageFilePath = image->getPath();
+ info.imageFileModDate = image->lastModified();
+ // add to all_image_infos if not already there
+ bool found = false;
+ int existingCount = dyld_all_image_infos.infoArrayCount;
+ const dyld_image_info* existing = dyld_all_image_infos.infoArray;
+ if ( existing != NULL ) {
+ for (int i=0; i < existingCount; ++i) {
+ if ( existing[i].imageLoadAddress == info.imageLoadAddress ) {
+ //dyld::log("not adding %s\n", info.imageFilePath);
+ found = true;
+ break;
+ }
+ }
+ }
+ if ( ! found ) {
+ //dyld::log("adding %s\n", info.imageFilePath);
+ addImagesToAllImages(1, &info);
+ }
+ }
}
}
}
#if CORESYMBOLICATION_SUPPORT
- if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) {
- CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage;
- if ( connection->is_valid_version() ) {
- if ( state == dyld_image_state_rebased ) {
+ if ( state == dyld_image_state_rebased ) {
+ if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) {
+ for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
+ dyld_image_states imageState = (*it)->getState();
+ if ( (imageState == dyld_image_state_rebased) || (orLater && (imageState > dyld_image_state_rebased)) )
+ dyld::log("dyld core symbolication load notification: %p %s\n", (*it)->machHeader(), (*it)->getPath());
+ }
+ }
+ if ( dyld_all_image_infos.coreSymbolicationShmPage != NULL) {
+ CSCppDyldSharedMemoryPage* connection = (CSCppDyldSharedMemoryPage*)dyld_all_image_infos.coreSymbolicationShmPage;
+ if ( connection->is_valid_version() ) {
// This needs to be captured now
uint64_t load_timestamp = mach_absolute_time();
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
#endif
}
+
+
static void notifyBatch(dyld_image_states state)
{
notifyBatchPartial(state, false, NULL);
// notify
notifySingle(dyld_image_state_terminated, image);
+ // <rdar://problem/7740779> dyld should directly call __cxa_finalize()
+ if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 8) )
+ (*gLibSystemHelpers->cxa_finalize)(image->machHeader());
+
// remove from mapped images table
removedMappedRanges(image);
void initializeMainExecutable()
{
+ // apply interposing to initial set of images
+ // do this before making the __IMPORT segments in shared cache read-only
+ sMainExecutable->applyInterposing(gLinkContext);
// record that we've reached this step
gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs
+ ImageLoader::InitializerTimingList initializerTimes[sAllImages.size()];
const int rootCount = sImageRoots.size();
if ( rootCount > 1 ) {
- for(int i=1; i < rootCount; ++i)
- sImageRoots[i]->runInitializers(gLinkContext);
+ for(int i=1; i < rootCount; ++i) {
+ initializerTimes[0].count = 0;
+ sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);
+ }
}
// run initializers for main executable and everything it brings up
- sMainExecutable->runInitializers(gLinkContext);
+ initializerTimes[0].count = 0;
+ sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);
// register atexit() handler to run terminators in all loaded images when this process exits
if ( gLibSystemHelpers != NULL )
// dump info if requested
if ( sEnv.DYLD_PRINT_STATISTICS )
- ImageLoaderMachO::printStatistics(sAllImages.size());
+ ImageLoaderMachO::printStatistics(sAllImages.size(), initializerTimes[0]);
}
bool mainExecutablePrebound()
}
+// forward reference
+static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName);
+
+
//
-// Turns a colon separated list of strings
-// into a NULL terminated array of string
-// pointers.
+// Examines a dylib file and if its current_version is newer than the installed
+// dylib at its install_name, then add the dylib file to sDylibOverrides.
//
-static const char** parseColonList(const char* list)
+static void checkDylibOverride(const char* dylibFile)
+{
+ //dyld::log("checkDylibOverride('%s')\n", dylibFile);
+ uint32_t altVersion;
+ char sysInstallName[PATH_MAX];
+ if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) ) {
+ //dyld::log("%s has version 0x%08X and install name %s\n", dylibFile, altVersion, sysInstallName);
+ uint32_t sysVersion;
+ if ( getDylibVersionAndInstallname(sysInstallName, &sysVersion, NULL) ) {
+ //dyld::log("%s has version 0x%08X\n", sysInstallName, sysVersion);
+ if ( altVersion > sysVersion ) {
+ //dyld::log("override found: %s -> %s\n", sysInstallName, dylibFile);
+ // see if there already is an override for this dylib
+ bool entryExists = false;
+ for (std::vector<DylibOverride>::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) {
+ if ( strcmp(it->installName, sysInstallName) == 0 ) {
+ entryExists = true;
+ uint32_t prevVersion;
+ if ( getDylibVersionAndInstallname(it->override, &prevVersion, NULL) ) {
+ if ( altVersion > prevVersion ) {
+ // found an even newer override
+ free((void*)(it->override));
+ it->override = strdup(dylibFile);
+ break;
+ }
+ }
+ }
+ }
+ if ( ! entryExists ) {
+ DylibOverride entry;
+ entry.installName = strdup(sysInstallName);
+ entry.override = strdup(dylibFile);
+ sDylibOverrides.push_back(entry);
+ //dyld::log("added override: %s -> %s\n", entry.installName, entry.override);
+ }
+ }
+ }
+ }
+
+}
+
+static void checkDylibOverridesInDir(const char* dirPath)
+{
+ //dyld::log("checkDylibOverridesInDir('%s')\n", dirPath);
+ char dylibPath[PATH_MAX];
+ int dirPathLen = strlen(dirPath);
+ strlcpy(dylibPath, dirPath, PATH_MAX);
+ DIR* dirp = opendir(dirPath);
+ if ( dirp != NULL) {
+ dirent entry;
+ dirent* entp = NULL;
+ while ( readdir_r(dirp, &entry, &entp) == 0 ) {
+ if ( entp == NULL )
+ break;
+ if ( entp->d_type != DT_REG )
+ continue;
+ dylibPath[dirPathLen] = '/';
+ dylibPath[dirPathLen+1] = '\0';
+ if ( strlcat(dylibPath, entp->d_name, PATH_MAX) > PATH_MAX )
+ continue;
+ checkDylibOverride(dylibPath);
+ }
+ closedir(dirp);
+ }
+}
+
+
+static void checkFrameworkOverridesInDir(const char* dirPath)
+{
+ //dyld::log("checkFrameworkOverridesInDir('%s')\n", dirPath);
+ char frameworkPath[PATH_MAX];
+ int dirPathLen = strlen(dirPath);
+ strlcpy(frameworkPath, dirPath, PATH_MAX);
+ DIR* dirp = opendir(dirPath);
+ if ( dirp != NULL) {
+ dirent entry;
+ dirent* entp = NULL;
+ while ( readdir_r(dirp, &entry, &entp) == 0 ) {
+ if ( entp == NULL )
+ break;
+ if ( entp->d_type != DT_DIR )
+ continue;
+ frameworkPath[dirPathLen] = '/';
+ frameworkPath[dirPathLen+1] = '\0';
+ int dirNameLen = strlen(entp->d_name);
+ if ( dirNameLen < 11 )
+ continue;
+ if ( strcmp(&entp->d_name[dirNameLen-10], ".framework") != 0 )
+ continue;
+ if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) > PATH_MAX )
+ continue;
+ if ( strlcat(frameworkPath, "/", PATH_MAX) > PATH_MAX )
+ continue;
+ if ( strlcat(frameworkPath, entp->d_name, PATH_MAX) > PATH_MAX )
+ continue;
+ frameworkPath[strlen(frameworkPath)-10] = '\0';
+ checkDylibOverride(frameworkPath);
+ }
+ closedir(dirp);
+ }
+}
+
+//
+// Turns a colon separated list of strings into a NULL terminated array
+// of string pointers. If mainExecutableDir param is not NULL,
+// substitutes @loader_path with main executable's dir.
+//
+static const char** parseColonList(const char* list, const char* mainExecutableDir)
{
static const char* sEmptyList[] = { NULL };
for(const char* s=list; *s != '\0'; ++s) {
if (*s == ':') {
int len = s-start;
- char* str = new char[len+1];
- strncpy(str, start, len);
- str[len] = '\0';
- start = &s[1];
- result[index++] = str;
+ if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
+ int mainExecDirLen = strlen(mainExecutableDir);
+ char* str = new char[mainExecDirLen+len+1];
+ strcpy(str, mainExecutableDir);
+ strlcat(str, &start[13], mainExecDirLen+len+1);
+ str[mainExecDirLen+len-13] = '\0';
+ start = &s[1];
+ result[index++] = str;
+ }
+ else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
+ int mainExecDirLen = strlen(mainExecutableDir);
+ char* str = new char[mainExecDirLen+len+1];
+ strcpy(str, mainExecutableDir);
+ strlcat(str, &start[17], mainExecDirLen+len+1);
+ str[mainExecDirLen+len-17] = '\0';
+ start = &s[1];
+ result[index++] = str;
+ }
+ else {
+ char* str = new char[len+1];
+ strncpy(str, start, len);
+ str[len] = '\0';
+ start = &s[1];
+ result[index++] = str;
+ }
}
}
int len = strlen(start);
- char* str = new char[len+1];
- strcpy(str, start);
- result[index++] = str;
+ if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) {
+ int mainExecDirLen = strlen(mainExecutableDir);
+ char* str = new char[mainExecDirLen+len+1];
+ strcpy(str, mainExecutableDir);
+ strlcat(str, &start[13], mainExecDirLen+len+1);
+ str[mainExecDirLen+len-13] = '\0';
+ result[index++] = str;
+ }
+ else if ( (mainExecutableDir != NULL) && (strncmp(start, "@executable_path/", 17) == 0) ) {
+ int mainExecDirLen = strlen(mainExecutableDir);
+ char* str = new char[mainExecDirLen+len+1];
+ strcpy(str, mainExecutableDir);
+ strlcat(str, &start[17], mainExecDirLen+len+1);
+ str[mainExecDirLen+len-17] = '\0';
+ result[index++] = str;
+ }
+ else {
+ char* str = new char[len+1];
+ strcpy(str, start);
+ result[index++] = str;
+ }
result[index] = NULL;
+ //dyld::log("parseColonList(%s)\n", list);
+ //for(int i=0; result[i] != NULL; ++i)
+ // dyld::log(" %s\n", result[i]);
return (const char**)result;
}
+static void appendParsedColonList(const char* list, const char* mainExecutableDir, const char* const ** storage)
+{
+ const char** newlist = parseColonList(list, mainExecutableDir);
+ if ( *storage == NULL ) {
+ // first time, just set
+ *storage = newlist;
+ }
+ else {
+ // need to append to existing list
+ const char* const* existing = *storage;
+ int count = 0;
+ for(int i=0; existing[i] != NULL; ++i)
+ ++count;
+ for(int i=0; newlist[i] != NULL; ++i)
+ ++count;
+ const char** combinedList = new const char*[count+2];
+ int index = 0;
+ for(int i=0; existing[i] != NULL; ++i)
+ combinedList[index++] = existing[i];
+ for(int i=0; newlist[i] != NULL; ++i)
+ combinedList[index++] = newlist[i];
+ combinedList[index] = NULL;
+ // leak old arrays
+ *storage = combinedList;
+ }
+}
+
static void paths_expand_roots(const char **paths, const char *key, const char *val)
{
}
}
-void processDyldEnvironmentVarible(const char* key, const char* value)
+void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainExecutableDir)
{
if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) {
- sEnv.DYLD_FRAMEWORK_PATH = parseColonList(value);
+ appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FRAMEWORK_PATH);
}
else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) {
- sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = parseColonList(value);
+ appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_FRAMEWORK_PATH);
}
else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) {
- sEnv.DYLD_LIBRARY_PATH = parseColonList(value);
+ appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_LIBRARY_PATH);
}
else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) {
- sEnv.DYLD_FALLBACK_LIBRARY_PATH = parseColonList(value);
+ appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_LIBRARY_PATH);
}
else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) {
if ( strcmp(value, "/") != 0 ) {
- gLinkContext.rootPaths = parseColonList(value);
+ gLinkContext.rootPaths = parseColonList(value, mainExecutableDir);
for (int i=0; gLinkContext.rootPaths[i] != NULL; ++i) {
if ( gLinkContext.rootPaths[i][0] != '/' ) {
dyld::warn("DYLD_ROOT_PATH not used because it contains a non-absolute path\n");
gLinkContext.imageSuffix = value;
}
else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) {
- sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value);
+ sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL);
}
else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) {
sEnv.DYLD_PRINT_OPTS = true;
else if ( strcmp(key, "DYLD_PRINT_WARNINGS") == 0 ) {
gLinkContext.verboseWarnings = true;
}
- else if ( strcmp(key, "DYLD_NO_PIE") == 0 ) {
- gLinkContext.noPIE = true;
+ else if ( strcmp(key, "DYLD_PRINT_RPATHS") == 0 ) {
+ gLinkContext.verboseRPaths = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_CS_NOTIFICATIONS") == 0 ) {
+ sEnv.DYLD_PRINT_CS_NOTIFICATIONS = true;
+ }
+ else if ( strcmp(key, "DYLD_PRINT_INTERPOSING") == 0 ) {
+ gLinkContext.verboseInterposing = true;
}
else if ( strcmp(key, "DYLD_SHARED_REGION") == 0 ) {
if ( strcmp(value, "private") == 0 ) {
dyld::warn("unknown option to DYLD_IGNORE_PREBINDING. Valid options are: all, app, nonsplit\n");
}
}
+#if SUPPORT_VERSIONED_PATHS
+ else if ( strcmp(key, "DYLD_VERSIONED_LIBRARY_PATH") == 0 ) {
+ appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_LIBRARY_PATH);
+ }
+ else if ( strcmp(key, "DYLD_VERSIONED_FRAMEWORK_PATH") == 0 ) {
+ appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_FRAMEWORK_PATH);
+ }
+#endif
else {
dyld::warn("unknown environment variable: %s\n", key);
}
}
+#if SUPPORT_LC_DYLD_ENVIRONMENT
+static void checkLoadCommandEnvironmentVariables()
+{
+ // <rdar://problem/8440934> Support augmenting dyld environment variables in load commands
+ const uint32_t cmd_count = sMainExecutableMachHeader->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((char*)sMainExecutableMachHeader)+sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_DYLD_ENVIRONMENT:
+ {
+ const struct dylinker_command* envcmd = (struct dylinker_command*)cmd;
+ const char* keyEqualsValue = (char*)envcmd + envcmd->name.offset;
+ char mainExecutableDir[strlen(sExecPath)];
+ strcpy(mainExecutableDir, sExecPath);
+ char* lastSlash = strrchr(mainExecutableDir, '/');
+ if ( lastSlash != NULL)
+ lastSlash[1] = '\0';
+ // only process variables that start with DYLD_ and end in _PATH
+ if ( (strncmp(keyEqualsValue, "DYLD_", 5) == 0) ) {
+ const char* equals = strchr(keyEqualsValue, '=');
+ if ( equals != NULL ) {
+ if ( strncmp(&equals[-5], "_PATH", 5) == 0 ) {
+ const char* value = &equals[1];
+ const int keyLen = equals-keyEqualsValue;
+ char key[keyLen+1];
+ strncpy(key, keyEqualsValue, keyLen);
+ key[keyLen] = '\0';
+ //dyld::log("processing: %s\n", keyEqualsValue);
+ //dyld::log("mainExecutableDir: %s\n", mainExecutableDir);
+ processDyldEnvironmentVariable(key, value, mainExecutableDir);
+ }
+ }
+ }
+ }
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+}
+#endif // SUPPORT_LC_DYLD_ENVIRONMENT
+
+
+#if SUPPORT_VERSIONED_PATHS
+static void checkVersionedPaths()
+{
+ // search DYLD_VERSIONED_LIBRARY_PATH directories for dylibs and check if they are newer
+ if ( sEnv.DYLD_VERSIONED_LIBRARY_PATH != NULL ) {
+ for(const char* const* lp = sEnv.DYLD_VERSIONED_LIBRARY_PATH; *lp != NULL; ++lp) {
+ checkDylibOverridesInDir(*lp);
+ }
+ }
+
+ // search DYLD_VERSIONED_FRAMEWORK_PATH directories for dylibs and check if they are newer
+ if ( sEnv.DYLD_VERSIONED_FRAMEWORK_PATH != NULL ) {
+ for(const char* const* fp = sEnv.DYLD_VERSIONED_FRAMEWORK_PATH; *fp != NULL; ++fp) {
+ checkFrameworkOverridesInDir(*fp);
+ }
+ }
+}
+#endif
+
+
//
// For security, setuid programs ignore DYLD_* environment variables.
// Additionally, the DYLD_* enviroment variables are removed
do {
*d = d[removedCount];
} while ( *d++ != NULL );
+ for(int i=0; i < removedCount; ++i)
+ *d++ = NULL;
}
// disable framework and library fallback paths for setuid binaries rdar://problem/4589305
char key[keyLen+1];
strncpy(key, keyEqualsValue, keyLen);
key[keyLen] = '\0';
- processDyldEnvironmentVarible(key, value);
+ processDyldEnvironmentVariable(key, value, NULL);
}
}
else if ( strncmp(keyEqualsValue, "HOME=", 5) == 0 ) {
}
else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) {
const char* path = &keyEqualsValue[16];
- sEnv.LD_LIBRARY_PATH = parseColonList(path);
+ sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL);
}
}
+
+#if SUPPORT_LC_DYLD_ENVIRONMENT
+ checkLoadCommandEnvironmentVariables();
+#endif // SUPPORT_LC_DYLD_ENVIRONMENT
// default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment
if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) {
paths_expand_roots(paths, "$HOME", home);
sEnv.DYLD_FALLBACK_LIBRARY_PATH = paths;
}
+
+#if SUPPORT_VERSIONED_PATHS
+ checkVersionedPaths();
+#endif
}
static void getHostInfo()
{
-#if 1
+#if CPU_SUBTYPES_SUPPORTED
+#if __ARM_ARCH_7A__
+ sHostCPU = CPU_TYPE_ARM;
+ sHostCPUsubtype = CPU_SUBTYPE_ARM_V7;
+#elif __ARM_ARCH_6K__
+ sHostCPU = CPU_TYPE_ARM;
+ sHostCPUsubtype = CPU_SUBTYPE_ARM_V6;
+#else
struct host_basic_info info;
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
mach_port_t hostPort = mach_host_self();
kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count);
if ( result != KERN_SUCCESS )
throw "host_info() failed";
-
sHostCPU = info.cpu_type;
sHostCPUsubtype = info.cpu_subtype;
-#else
- size_t valSize = sizeof(sHostCPU);
- if (sysctlbyname ("hw.cputype", &sHostCPU, &valSize, NULL, 0) != 0)
- throw "sysctlbyname(hw.cputype) failed";
- valSize = sizeof(sHostCPUsubtype);
- if (sysctlbyname ("hw.cpusubtype", &sHostCPUsubtype, &valSize, NULL, 0) != 0)
- throw "sysctlbyname(hw.cpusubtype) failed";
+#endif
#endif
}
static void checkSharedRegionDisable()
{
- #if !__LP64__
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// if main executable has segments that overlap the shared region,
// then disable using the shared region
if ( sMainExecutable->overlapsWithAddressRange((void*)(uintptr_t)SHARED_REGION_BASE, (void*)(uintptr_t)(SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) {
if ( gLinkContext.verboseMapping )
dyld::warn("disabling shared region because main executable overlaps\n");
}
- #endif
+#endif
+ // iPhoneOS cannot run without shared region
}
bool validImage(const ImageLoader* possibleImage)
throw "main executable not a known format";
}
+
#if DYLD_SHARED_CACHE_SUPPORT
-bool inSharedCache(const char* path)
+static bool findInSharedCacheImage(const char* path, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide)
{
if ( sSharedCache != NULL ) {
- struct stat stat_buf;
- if ( stat(path, &stat_buf) == -1 )
- return false;
-
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // Mac OS X always requires inode/mtime to valid cache
+ // if stat() not done yet, do it now
+ struct stat statb;
+ if ( stat_buf == NULL ) {
+ if ( stat(path, &statb) == -1 )
+ return false;
+ stat_buf = &statb;
+ }
+#endif
// walk shared cache to see if there is a cached image that matches the inode/mtime/path desired
const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)sSharedCache + sSharedCache->imagesOffset);
const dyld_cache_image_info* const end = &start[sSharedCache->imagesCount];
for( const dyld_cache_image_info* p = start; p != end; ++p) {
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // just check path
+ const char* aPath = (char*)sSharedCache + p->pathFileOffset;
+ if ( strcmp(path, aPath) == 0 ) {
+ // found image in cache
+ *mh = (macho_header*)(p->address+sSharedCacheSlide);
+ *pathInCache = aPath;
+ *slide = sSharedCacheSlide;
+ return true;
+ }
+#elif __MAC_OS_X_VERSION_MIN_REQUIRED
// check mtime and inode first because it is fast
if ( sSharedCacheIgnoreInodeAndTimeStamp
- || ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) ) {
+ || ( ((time_t)p->modTime == stat_buf->st_mtime) && ((ino_t)p->inode == stat_buf->st_ino) ) ) {
// mod-time and inode match an image in the shared cache, now check path
- const char* pathInCache = (char*)sSharedCache + p->pathFileOffset;
- bool cacheHit = (strcmp(path, pathInCache) == 0);
+ const char* aPath = (char*)sSharedCache + p->pathFileOffset;
+ bool cacheHit = (strcmp(path, aPath) == 0);
if ( ! cacheHit ) {
// path does not match install name of dylib in cache, but inode and mtime does match
// perhaps path is a symlink to the cached dylib
struct stat pathInCacheStatBuf;
- if ( stat(pathInCache, &pathInCacheStatBuf) != -1 )
- cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf.st_dev) && (pathInCacheStatBuf.st_ino == stat_buf.st_ino) );
+ if ( stat(aPath, &pathInCacheStatBuf) != -1 )
+ cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf->st_dev) && (pathInCacheStatBuf.st_ino == stat_buf->st_ino) );
}
if ( cacheHit ) {
- // found image in cache
+ // found image in cache, return info
+ *mh = (macho_header*)(p->address+sSharedCacheSlide);
+ //dyld::log("findInSharedCacheImage(), mh=%p, p->address=0x%0llX, slid=0x%0lX, path=%p\n",
+ // *mh, p->address, sSharedCacheSlide, aPath);
+ *pathInCache = aPath;
+ *slide = sSharedCacheSlide;
return true;
}
}
+#endif
}
}
return false;
}
-static ImageLoader* findSharedCacheImage(const struct stat& stat_buf, const char* path)
+bool inSharedCache(const char* path)
{
- if ( sSharedCache != NULL ) {
- // walk shared cache to see if there is a cached image that matches the inode/mtime/path desired
- const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)sSharedCache + sSharedCache->imagesOffset);
- const dyld_cache_image_info* const end = &start[sSharedCache->imagesCount];
- for( const dyld_cache_image_info* p = start; p != end; ++p) {
- // check mtime and inode first because it is fast
- if ( sSharedCacheIgnoreInodeAndTimeStamp
- || ( ((time_t)p->modTime == stat_buf.st_mtime) && ((ino_t)p->inode == stat_buf.st_ino) ) ) {
- // mod-time and inode match an image in the shared cache, now check path
- const char* pathInCache = (char*)sSharedCache + p->pathFileOffset;
- bool cacheHit = (strcmp(path, pathInCache) == 0);
- if ( ! cacheHit ) {
- // path does not match install name of dylib in cache, but inode and mtime does match
- // perhaps path is a symlink to the cached dylib
- struct stat pathInCacheStatBuf;
- if ( stat(pathInCache, &pathInCacheStatBuf) != -1 )
- cacheHit = ( (pathInCacheStatBuf.st_dev == stat_buf.st_dev) && (pathInCacheStatBuf.st_ino == stat_buf.st_ino) );
- }
- if ( cacheHit ) {
- // found image in cache, instantiate an ImageLoader with it
- return ImageLoaderMachO::instantiateFromCache((macho_header*)(p->address), pathInCache, stat_buf, gLinkContext);
- }
- }
- }
- }
- return NULL;
+ const macho_header* mhInCache;
+ const char* pathInCache;
+ long slide;
+ return findInSharedCacheImage(path, NULL, &mhInCache, &pathInCache, &slide);
}
+
#endif
static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context)
if ( installPath != NULL) {
if ( strcmp(loadedImageInstallPath, installPath) == 0 ) {
//dyld::log("duplicate(%s) => %p\n", installPath, anImage);
+ removeImage(image);
ImageLoader::deleteImage(image);
return anImage;
}
}
// map in file and instantiate an ImageLoader
-static ImageLoader* loadPhase6(int fd, struct stat& stat_buf, const char* path, const LoadContext& context)
+static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* path, const LoadContext& context)
{
//dyld::log("%s(%s)\n", __func__ , path);
uint64_t fileOffset = 0;
}
// try mach-o loader
+ if ( shortPage )
+ throw "file too short";
if ( isCompatibleMachO(firstPage, path) ) {
- if ( shortPage )
- throw "file too short";
+ // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded
+ switch ( ((mach_header*)firstPage)->filetype ) {
+ case MH_EXECUTE:
+ case MH_DYLIB:
+ case MH_BUNDLE:
+ break;
+ default:
+ throw "mach-o, but wrong filetype";
+ }
+
// instantiate an image
ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPage, fileOffset, fileLength, stat_buf, gLinkContext);
}
-// try to open file
-static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, const struct stat& stat_buf, std::vector<const char*>* exceptions)
+{
+ //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
+
+ // open file (automagically closed when this function exits)
+ FileOpener file(path);
+
+ // just return NULL if file not found, but record any other errors
+ if ( file.getFileDescriptor() == -1 ) {
+ int err = errno;
+ if ( err != ENOENT ) {
+ const char* newMsg = dyld::mkstringf("%s: open() failed with errno=%d", path, err);
+ exceptions->push_back(newMsg);
+ }
+ return NULL;
+ }
+
+ try {
+ return loadPhase6(file.getFileDescriptor(), stat_buf, path, context);
+ }
+ catch (const char* msg) {
+ const char* newMsg = dyld::mkstringf("%s: %s", path, msg);
+ exceptions->push_back(newMsg);
+ free((void*)msg);
+ return NULL;
+ }
+}
+
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
#if DYLD_SHARED_CACHE_SUPPORT
// see if this image is in shared cache
- image = findSharedCacheImage(stat_buf, path);
- if ( image != NULL ) {
+ const macho_header* mhInCache;
+ const char* pathInCache;
+ long slideInCache;
+ if ( findInSharedCacheImage(path, &stat_buf, &mhInCache, &pathInCache, &slideInCache) ) {
+ image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext);
return checkandAddImage(image, context);
}
#endif
-
- // open file (automagically closed when this function exits)
- FileOpener file(path);
-
- // just return NULL if file not found, but record any other errors
- if ( file.getFileDescriptor() == -1 ) {
- int err = errno;
- if ( err != ENOENT ) {
- const char* newMsg = dyld::mkstringf("%s: open() failed with errno=%d", path, err);
- exceptions->push_back(newMsg);
+ // file exists and is not in dyld shared cache, so open it
+ return loadPhase5open(path, context, stat_buf, exceptions);
+}
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
+
+
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+static ImageLoader* loadPhase5stat(const char* path, const LoadContext& context, struct stat* stat_buf,
+ int* statErrNo, bool* imageFound, std::vector<const char*>* exceptions)
+{
+ ImageLoader* image = NULL;
+ *imageFound = false;
+ if ( stat(path, stat_buf) == 0 ) {
+ // in case image was renamed or found via symlinks, check for inode match
+ image = findLoadedImage(*stat_buf);
+ if ( image != NULL ) {
+ *imageFound = true;
+ return image;
+ }
+ // do nothing if not already loaded and if RTLD_NOLOAD
+ if ( context.dontLoad ) {
+ *imageFound = true;
+ return NULL;
+ }
+ image = loadPhase5open(path, context, *stat_buf, exceptions);
+ if ( image != NULL ) {
+ *imageFound = true;
+ return image;
}
- return NULL;
}
+ else {
+ *statErrNo = errno;
+ }
+ return NULL;
+}
- try {
- return loadPhase6(file.getFileDescriptor(), stat_buf, path, context);
+// try to open file
+static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
+{
+ //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
+ struct stat stat_buf;
+ bool imageFound;
+ int statErrNo;
+ ImageLoader* image;
+#if DYLD_SHARED_CACHE_SUPPORT
+ if ( sDylibsOverrideCache ) {
+ // flag is set that allows installed framework roots to override dyld shared cache
+ image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions);
+ if ( imageFound )
+ return image;
}
- catch (const char* msg) {
- const char* newMsg = dyld::mkstringf("%s: %s", path, msg);
- exceptions->push_back(newMsg);
- free((void*)msg);
- return NULL;
+ // see if this image is in shared cache
+ const macho_header* mhInCache;
+ const char* pathInCache;
+ long slideInCache;
+ if ( findInSharedCacheImage(path, NULL, &mhInCache, &pathInCache, &slideInCache) ) {
+ // see if this image in the cache was already loaded via a different path
+ for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) {
+ ImageLoader* anImage = *it;
+ if ( anImage->machHeader() == mhInCache )
+ return anImage;
+ }
+ // do nothing if not already loaded and if RTLD_NOLOAD
+ if ( context.dontLoad )
+ return NULL;
+ // nope, so instantiate a new image from dyld shared cache
+ // <rdar://problem/7014995> zero out stat buffer so mtime, etc are zero for items from the shared cache
+ bzero(&stat_buf, sizeof(stat_buf));
+ image = ImageLoaderMachO::instantiateFromCache(mhInCache, pathInCache, slideInCache, stat_buf, gLinkContext);
+ return checkandAddImage(image, context);
}
+
+ if ( !sDylibsOverrideCache ) {
+ // flag is not set, and not in cache to try opening it
+ image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions);
+ if ( imageFound )
+ return image;
+ }
+#else
+ image = loadPhase5stat(path, context, &stat_buf, &statErrNo, &imageFound, exceptions);
+ if ( imageFound )
+ return image;
+#endif
+ // just return NULL if file not found, but record any other errors
+ if ( statErrNo != ENOENT ) {
+ exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, statErrNo));
+ }
+ return NULL;
}
+#endif // __IPHONE_OS_VERSION_MIN_REQUIRED
+
// look for path match with existing loaded images
-static ImageLoader* loadPhase5check(const char* path, const LoadContext& context)
+static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context)
{
- //dyld::log("%s(%s)\n", __func__ , path);
+ //dyld::log("%s(%s, %s)\n", __func__ , path, orgPath);
// search path against load-path and install-path of all already loaded images
uint32_t hash = ImageLoader::hash(path);
//dyld::log("check() hash=%d, path=%s\n", hash, path);
ImageLoader* anImage = *it;
// check hash first to cut down on strcmp calls
//dyld::log(" check() hash=%d, path=%s\n", anImage->getPathHash(), anImage->getPath());
- if ( anImage->getPathHash() == hash )
+ if ( anImage->getPathHash() == hash ) {
if ( strcmp(path, anImage->getPath()) == 0 ) {
// if we are looking for a dylib don't return something else
if ( !context.mustBeDylib || anImage->isDylib() )
return anImage;
}
+ }
if ( context.matchByInstallName || anImage->matchInstallPath() ) {
const char* installPath = anImage->getInstallPath();
if ( installPath != NULL) {
}
}
}
+ // an install name starting with @rpath should match by install name, not just real path
+ if ( (orgPath[0] == '@') && (strncmp(orgPath, "@rpath/", 7) == 0) ) {
+ const char* installPath = anImage->getInstallPath();
+ if ( installPath != NULL) {
+ if ( !context.mustBeDylib || anImage->isDylib() ) {
+ if ( strcmp(orgPath, installPath) == 0 )
+ return anImage;
+ }
+ }
+ }
}
//dyld::log("%s(%s) => NULL\n", __func__, path);
// open or check existing
-static ImageLoader* loadPhase5(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase5(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
+
+ // check for specific dylib overrides
+ for (std::vector<DylibOverride>::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) {
+ if ( strcmp(it->installName, path) == 0 ) {
+ path = it->override;
+ break;
+ }
+ }
+
if ( exceptions != NULL )
- return loadPhase5open(path, context, exceptions);
+ return loadPhase5load(path, orgPath, context, exceptions);
else
- return loadPhase5check(path, context);
+ return loadPhase5check(path, orgPath, context);
}
// try with and without image suffix
-static ImageLoader* loadPhase4(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase4(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
if ( gLinkContext.imageSuffix != NULL ) {
char pathWithSuffix[strlen(path)+strlen( gLinkContext.imageSuffix)+2];
ImageLoader::addSuffix(path, gLinkContext.imageSuffix, pathWithSuffix);
- image = loadPhase5(pathWithSuffix, context, exceptions);
+ image = loadPhase5(pathWithSuffix, orgPath, context, exceptions);
}
if ( image == NULL )
- image = loadPhase5(path, context, exceptions);
+ image = loadPhase5(path, orgPath, context, exceptions);
return image;
}
-static ImageLoader* loadPhase2(const char* path, const LoadContext& context,
+static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context,
const char* const frameworkPaths[], const char* const libraryPaths[],
std::vector<const char*>* exceptions); // forward reference
// expand @ variables
-static ImageLoader* loadPhase3(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase3(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
strcpy(&addPoint[1], &path[17]);
else
strcpy(newPath, &path[17]);
- image = loadPhase4(newPath, context, exceptions);
+ image = loadPhase4(newPath, orgPath, context, exceptions);
if ( image != NULL )
return image;
strcpy(&addPoint[1], &path[17]);
else
strcpy(newRealPath, &path[17]);
- image = loadPhase4(newRealPath, context, exceptions);
+ image = loadPhase4(newRealPath, orgPath, context, exceptions);
if ( image != NULL )
return image;
}
strcpy(&addPoint[1], &path[13]);
else
strcpy(newPath, &path[13]);
- image = loadPhase4(newPath, context, exceptions);
+ image = loadPhase4(newPath, orgPath, context, exceptions);
if ( image != NULL )
return image;
strcpy(&addPoint[1], &path[13]);
else
strcpy(newRealPath, &path[13]);
- image = loadPhase4(newRealPath, context, exceptions);
+ image = loadPhase4(newRealPath, orgPath, context, exceptions);
if ( image != NULL )
return image;
}
strcpy(newPath, anRPath);
strcat(newPath, "/");
strcat(newPath, trailingPath);
- image = loadPhase4(newPath, context, exceptions);
+ image = loadPhase4(newPath, orgPath, context, exceptions);
+ if ( gLinkContext.verboseRPaths && (exceptions != NULL) ) {
+ if ( image != NULL )
+ dyld::log("RPATH successful expansion of %s to: %s\n", orgPath, newPath);
+ else
+ dyld::log("RPATH failed to expanding %s to: %s\n", orgPath, newPath);
+ }
if ( image != NULL )
return image;
}
// substitute @rpath with LD_LIBRARY_PATH
if ( sEnv.LD_LIBRARY_PATH != NULL ) {
- image = loadPhase2(trailingPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);
+ image = loadPhase2(trailingPath, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);
if ( image != NULL )
return image;
}
throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin);
}
- return loadPhase4(path, context, exceptions);
+ return loadPhase4(path, orgPath, context, exceptions);
}
// try search paths
-static ImageLoader* loadPhase2(const char* path, const LoadContext& context,
+static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context,
const char* const frameworkPaths[], const char* const libraryPaths[],
std::vector<const char*>* exceptions)
{
strcat(npath, "/");
strcat(npath, frameworkPartialPath);
//dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath);
- image = loadPhase4(npath, context, exceptions);
+ image = loadPhase4(npath, orgPath, context, exceptions);
if ( image != NULL )
return image;
}
strcat(libpath, "/");
strcat(libpath, libraryLeafName);
//dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath);
- image = loadPhase4(libpath, context, exceptions);
+ image = loadPhase4(libpath, orgPath, context, exceptions);
if ( image != NULL )
return image;
}
}
// try search overrides and fallbacks
-static ImageLoader* loadPhase1(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase1(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
ImageLoader* image = NULL;
// handle LD_LIBRARY_PATH environment variables that force searching
if ( context.useLdLibraryPath && (sEnv.LD_LIBRARY_PATH != NULL) ) {
- image = loadPhase2(path, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);
+ image = loadPhase2(path, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, exceptions);
if ( image != NULL )
return image;
}
// handle DYLD_ environment variables that force searching
if ( context.useSearchPaths && ((sEnv.DYLD_FRAMEWORK_PATH != NULL) || (sEnv.DYLD_LIBRARY_PATH != NULL)) ) {
- image = loadPhase2(path, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, exceptions);
+ image = loadPhase2(path, orgPath, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, exceptions);
if ( image != NULL )
return image;
}
// try raw path
- image = loadPhase3(path, context, exceptions);
+ image = loadPhase3(path, orgPath, context, exceptions);
if ( image != NULL )
return image;
if ( (fallbackLibraryPaths != NULL) && !context.useFallbackPaths )
fallbackLibraryPaths = NULL;
if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (fallbackLibraryPaths != NULL)) ) {
- image = loadPhase2(path, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, exceptions);
+ image = loadPhase2(path, orgPath, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, exceptions);
if ( image != NULL )
return image;
}
}
// try root substitutions
-static ImageLoader* loadPhase0(const char* path, const LoadContext& context, std::vector<const char*>* exceptions)
+static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, std::vector<const char*>* exceptions)
{
//dyld::log("%s(%s, %p)\n", __func__ , path, exceptions);
char newPath[strlen(*rootPath) + strlen(path)+2];
strcpy(newPath, *rootPath);
strcat(newPath, path);
- ImageLoader* image = loadPhase1(newPath, context, exceptions);
+ ImageLoader* image = loadPhase1(newPath, orgPath, context, exceptions);
if ( image != NULL )
return image;
}
}
// try raw path
- return loadPhase1(path, context, exceptions);
+ return loadPhase1(path, orgPath, context, exceptions);
}
//
//
ImageLoader* load(const char* path, const LoadContext& context)
{
+ CRSetCrashLogMessage2(path);
+ const char* orgPath = path;
+
//dyld::log("%s(%s)\n", __func__ , path);
char realPath[PATH_MAX];
// when DYLD_IMAGE_SUFFIX is in used, do a realpath(), otherwise a load of "Foo.framework/Foo" will not match
}
// try all path permutations and check against existing loaded images
- ImageLoader* image = loadPhase0(path, context, NULL);
- if ( image != NULL )
+ ImageLoader* image = loadPhase0(path, orgPath, context, NULL);
+ if ( image != NULL ) {
+ CRSetCrashLogMessage2(NULL);
return image;
+ }
- // try all path permutations and try open() until first sucesss
+ // try all path permutations and try open() until first success
std::vector<const char*> exceptions;
- image = loadPhase0(path, context, &exceptions);
- if ( image != NULL )
+ image = loadPhase0(path, orgPath, context, &exceptions);
+ CRSetCrashLogMessage2(NULL);
+ if ( image != NULL ) {
+ // <rdar://problem/6916014> leak in dyld during dlopen when using DYLD_ variables
+ for (std::vector<const char*>::iterator it = exceptions.begin(); it != exceptions.end(); ++it) {
+ free((void*)(*it));
+ }
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // if loaded image is not from cache, but original path is in cache
+ // set gSharedCacheOverridden flag to disable some ObjC optimizations
+ if ( !gSharedCacheOverridden ) {
+ if ( !image->inSharedCache() && inSharedCache(path) ) {
+ gSharedCacheOverridden = true;
+ }
+ }
+#endif
return image;
+ }
else if ( exceptions.size() == 0 ) {
- if ( context.dontLoad )
+ if ( context.dontLoad ) {
return NULL;
+ }
else
throw "image not found";
}
#if DYLD_SHARED_CACHE_SUPPORT
-// hack until dyld no longer needs to run on Leopard kernels that don't have new shared region syscall
-static bool newSharedRegionSyscallAvailable()
-{
- int shreg_version;
- size_t buffer_size = sizeof(shreg_version);
- if ( sysctlbyname("vm.shared_region_version", &shreg_version, &buffer_size, NULL, 0) == 0 ) {
- if ( shreg_version == 3 )
- return true;
- }
- return false;
-}
+
+
+#if __ppc__
+ #define ARCH_NAME "ppc"
+ #define ARCH_NAME_ROSETTA "rosetta"
+ #define ARCH_CACHE_MAGIC "dyld_v1 ppc"
+#elif __ppc64__
+ #define ARCH_NAME "ppc64"
+ #define ARCH_CACHE_MAGIC "dyld_v1 ppc64"
+#elif __i386__
+ #define ARCH_NAME "i386"
+ #define ARCH_CACHE_MAGIC "dyld_v1 i386"
+#elif __x86_64__
+ #define ARCH_NAME "x86_64"
+ #define ARCH_CACHE_MAGIC "dyld_v1 x86_64"
+ #define SHARED_REGION_READ_ONLY_START 0x7FFF80000000LL
+ #define SHARED_REGION_READ_ONLY_END 0x7FFFC0000000LL
+ #define SHARED_REGION_WRITABLE_START 0x7FFF70000000LL
+ #define SHARED_REGION_WRITABLE_END 0x7FFF80000000LL
+ #define SLIDEABLE_CACHE_SUPPORT 1
+#elif __ARM_ARCH_5TEJ__
+ #define ARCH_NAME "armv5"
+ #define ARCH_CACHE_MAGIC "dyld_v1 armv5"
+#elif __ARM_ARCH_6K__
+ #define ARCH_NAME "armv6"
+ #define ARCH_CACHE_MAGIC "dyld_v1 armv6"
+#elif __ARM_ARCH_7A__
+ #define ARCH_NAME "armv7"
+ #define ARCH_CACHE_MAGIC "dyld_v1 armv7"
+ #define SHARED_REGION_READ_ONLY_START 0x30000000
+ #define SHARED_REGION_READ_ONLY_END 0x3E000000
+ #define SHARED_REGION_WRITABLE_START 0x3E000000
+ #define SHARED_REGION_WRITABLE_END 0x40000000
+ #define SLIDEABLE_CACHE_SUPPORT 1
+#endif
static int __attribute__((noinline)) _shared_region_check_np(uint64_t* start_address)
{
- if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) && newSharedRegionSyscallAvailable() )
+ if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) )
return syscall(294, start_address);
return -1;
}
-static int __attribute__((noinline)) _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[])
+static int __attribute__((noinline)) _shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[],
+ int codeSignatureMappingIndex, int slide, void* slideInfo, uint32_t slideInfoSize)
{
- int result;
- if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) && newSharedRegionSyscallAvailable() ) {
- return syscall(295, fd, count, mappings);
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // register code signature blob for whole dyld cache
+ if ( codeSignatureMappingIndex != -1 ) {
+ fsignatures_t siginfo;
+ siginfo.fs_file_start = 0; // cache always starts at beginning of file
+ siginfo.fs_blob_start = (void*)mappings[codeSignatureMappingIndex].sfm_file_offset;
+ siginfo.fs_blob_size = mappings[codeSignatureMappingIndex].sfm_size;
+ int result = fcntl(fd, F_ADDFILESIGS, &siginfo);
+ if ( result == -1 )
+ dyld::log("dyld: code signature for shared cache failed with errno=%d\n", errno);
+ }
+#endif
+ if ( (gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion) ) {
+ return syscall(438, fd, count, mappings, slide, slideInfo, slideInfoSize);
}
// remove the shared region sub-map
vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE);
+ // notify gdb or other lurkers that this process is no longer using the shared region
+ dyld_all_image_infos.processDetachedFromSharedRegion = true;
+
// map cache just for this process with mmap()
- bool failed = false;
- const shared_file_mapping_np* start = mappings;
- const shared_file_mapping_np* end = &mappings[count];
+ const shared_file_mapping_np* const start = mappings;
+ const shared_file_mapping_np* const end = &mappings[count];
for (const shared_file_mapping_np* p = start; p < end; ++p ) {
void* mmapAddress = (void*)(uintptr_t)(p->sfm_address);
size_t size = p->sfm_size;
+ //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size);
int protection = 0;
if ( p->sfm_init_prot & VM_PROT_EXECUTE )
protection |= PROT_EXEC;
if ( p->sfm_init_prot & VM_PROT_WRITE )
protection |= PROT_WRITE;
off_t offset = p->sfm_file_offset;
- mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset);
- if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress )
- failed = true;
- }
- if ( !failed ) {
- result = 0;
- gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
- }
- else {
- result = -1;
- gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
- if ( gLinkContext.verboseMapping )
- dyld::log("dyld: shared cached cannot be mapped\n");
+ if ( mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset) != mmapAddress ) {
+ // failed to map some chunk of this shared cache file
+ // clear shared region
+ vm_deallocate(mach_task_self(), (vm_address_t)SHARED_REGION_BASE, SHARED_REGION_SIZE);
+ // go back to not using shared region at all
+ gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
+ if ( gLinkContext.verboseMapping ) {
+ dyld::log("dyld: shared cached region cannot be mapped at address %p with size 0x%08lX\n",
+ mmapAddress, size);
+ }
+ // return failure
+ return -1;
+ }
+ }
+
+#if SLIDEABLE_CACHE_SUPPORT
+ // update all __DATA pages with slide info
+ if ( slide != 0 ) {
+ const uintptr_t dataPagesStart = mappings[1].sfm_address;
+ const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
+ const uint16_t* toc = (uint16_t*)((long)(slideInfoHeader) + slideInfoHeader->toc_offset);
+ const uint8_t* entries = (uint8_t*)((long)(slideInfoHeader) + slideInfoHeader->entries_offset);
+ for(uint32_t i=0; i < slideInfoHeader->toc_count; ++i) {
+ const uint8_t* entry = &entries[toc[i]*slideInfoHeader->entries_size];
+ const uint8_t* page = (uint8_t*)(long)(dataPagesStart + (4096*i));
+ //dyld::log("page=%p toc[%d]=%d entries=%p\n", page, i, toc[i], entry);
+ for(int j=0; j < 128; ++j) {
+ uint8_t b = entry[j];
+ //dyld::log(" entry[%d] = 0x%02X\n", j, b);
+ if ( b != 0 ) {
+ for(int k=0; k < 8; ++k) {
+ if ( b & (1<<k) ) {
+ uintptr_t* p = (uintptr_t*)(page + j*8*4 + k*4);
+ uintptr_t value = *p;
+ //dyld::log(" *%p was 0x%lX will be 0x%lX\n", p, value, value+sSharedCacheSlide);
+ *p = value + slide;
+ }
+ }
+ }
+ }
+ }
}
+#endif // SLIDEABLE_CACHE_SUPPORT
- return result;
+ // succesfully mapped shared cache for just this process
+ gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion;
+
+ return 0;
}
-
-#if __ppc__
- #define ARCH_NAME "ppc"
- #define ARCH_NAME_ROSETTA "rosetta"
- #define ARCH_VALUE CPU_TYPE_POWERPC
- #define ARCH_CACHE_MAGIC "dyld_v1 ppc"
-#elif __ppc64__
- #define ARCH_NAME "ppc64"
- #define ARCH_VALUE CPU_TYPE_POWERPC64
- #define ARCH_CACHE_MAGIC "dyld_v1 ppc64"
-#elif __i386__
- #define ARCH_NAME "i386"
- #define ARCH_VALUE CPU_TYPE_I386
- #define ARCH_CACHE_MAGIC "dyld_v1 i386"
-#elif __x86_64__
- #define ARCH_NAME "x86_64"
- #define ARCH_VALUE CPU_TYPE_X86_64
- #define ARCH_CACHE_MAGIC "dyld_v1 x86_64"
-#endif
-
const void* imMemorySharedCacheHeader()
{
return sSharedCache;
return ::open(path, O_RDONLY);
}
+#if SLIDEABLE_CACHE_SUPPORT
+static long pickCacheSlide(uint32_t mappingsCount, shared_file_mapping_np mappings[])
+{
+ // get bounds of cache
+ uint64_t readOnlyLowAddress = 0;
+ uint64_t readOnlyHighAddress = 0;
+ uint64_t writableLowAddress = 0;
+ uint64_t writableHighAddress = 0;
+ for(uint32_t i=0; i < mappingsCount; ++i) {
+ if ( mappings[i].sfm_init_prot & VM_PROT_WRITE ) {
+ writableLowAddress = mappings[i].sfm_address;
+ writableHighAddress = mappings[i].sfm_address + mappings[i].sfm_size;
+ }
+ else {
+ if ( readOnlyLowAddress == 0 ) {
+ readOnlyLowAddress = mappings[i].sfm_address;
+ readOnlyHighAddress = mappings[i].sfm_address + mappings[i].sfm_size;
+ }
+ else {
+ if ( readOnlyLowAddress < mappings[i].sfm_address ) {
+ readOnlyHighAddress = mappings[i].sfm_address + mappings[i].sfm_size;
+ }
+ else {
+ readOnlyLowAddress = mappings[i].sfm_address;
+ }
+ }
+ }
+ }
+
+ // find read-only slop space
+ uint64_t roSpace = SHARED_REGION_READ_ONLY_END - readOnlyHighAddress;
+
+ // find writable slop space
+ uint64_t rwSpace = SHARED_REGION_WRITABLE_END - writableHighAddress;
+
+ // choose new random slide
+ long slideSpace = (roSpace > rwSpace) ? rwSpace : roSpace;
+ long slide = (arc4random() % slideSpace) & (-4096);
+ //dyld::log("roSpace=0x%0llX\n", roSpace);
+ //dyld::log("rwSpace=0x%0llX\n", rwSpace);
+ //dyld::log("slideSpace=0x%0lX\n", slideSpace);
+ //dyld::log("slide=0x%0lX\n", slide);
+
+ // update mappings
+ for(uint32_t i=0; i < mappingsCount; ++i) {
+ mappings[i].sfm_address += slide;
+ }
+
+ return slide;
+}
+#endif // SLIDEABLE_CACHE_SUPPORT
+
static void mapSharedCache()
{
uint64_t cacheBaseAddress;
if ( gLinkContext.verboseMapping )
dyld::log("dyld: existing shared cached in memory is not compatible\n");
}
+ // check if cache file is slidable
+ dyld_cache_header* header = (dyld_cache_header*)sSharedCache;
+ if ( (header->mappingOffset >= 0x48) && (header->slideInfoSize != 0) ) {
+ // solve for slide by comparing loaded address to address of first region
+ const uint8_t* loadedAddress = (uint8_t*)sSharedCache;
+ const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)(loadedAddress+header->mappingOffset);
+ const uint8_t* preferedLoadAddress = (uint8_t*)(long)(mappings[0].address);
+ sSharedCacheSlide = loadedAddress - preferedLoadAddress;
+ dyld_all_image_infos.sharedCacheSlide = sSharedCacheSlide;
+ //dyld::log("sSharedCacheSlide=0x%08lX, loadedAddress=%p, preferedLoadAddress=%p\n", sSharedCacheSlide, loadedAddress, preferedLoadAddress);
+ }
}
else {
#if __i386__ || __x86_64__
// user booted machine in safe-boot mode
struct stat dyldCacheStatInfo;
// Don't use custom DYLD_SHARED_CACHE_DIR if provided, use standard path
- if ( ::stat(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) {
+ if ( ::stat(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &dyldCacheStatInfo) == 0 ) {
struct timeval bootTimeValue;
size_t bootTimeValueSize = sizeof(bootTimeValue);
if ( (sysctlbyname("kern.boottime", &bootTimeValue, &bootTimeValueSize, NULL, 0) == 0) && (bootTimeValue.tv_sec != 0) ) {
// if the cache file was created before this boot, then throw it away and let it rebuild itself
if ( dyldCacheStatInfo.st_mtime < bootTimeValue.tv_sec ) {
- ::unlink(DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME);
+ ::unlink(MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME);
gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion;
return;
}
if ( ::read(fd, firstPages, 8192) == 8192 ) {
dyld_cache_header* header = (dyld_cache_header*)firstPages;
if ( strcmp(header->magic, ARCH_CACHE_MAGIC) == 0 ) {
- const shared_file_mapping_np* mappings = (shared_file_mapping_np*)&firstPages[header->mappingOffset];
- const shared_file_mapping_np* const end = &mappings[header->mappingCount];
+ const dyld_cache_mapping_info* const fileMappingsStart = (dyld_cache_mapping_info*)&firstPages[header->mappingOffset];
+ const dyld_cache_mapping_info* const fileMappingsEnd = &fileMappingsStart[header->mappingCount];
+ shared_file_mapping_np mappings[header->mappingCount+1]; // add room for code-sig
+ unsigned int mappingCount = header->mappingCount;
+ int codeSignatureMappingIndex = -1;
// validate that the cache file has not been truncated
bool goodCache = false;
struct stat stat_buf;
if ( fstat(fd, &stat_buf) == 0 ) {
goodCache = true;
- for (const shared_file_mapping_np* p = mappings; p < end; ++p) {
+ int i=0;
+ for (const dyld_cache_mapping_info* p = fileMappingsStart; p < fileMappingsEnd; ++p, ++i) {
+ mappings[i].sfm_address = p->address;
+ mappings[i].sfm_size = p->size;
+ mappings[i].sfm_file_offset = p->fileOffset;
+ mappings[i].sfm_max_prot = p->maxProt;
+ mappings[i].sfm_init_prot = p->initProt;
// rdar://problem/5694507 old update_dyld_shared_cache tool could make a cache file
// that is not page aligned, but otherwise ok.
- if ( p->sfm_file_offset+p->sfm_size > (uint64_t)(stat_buf.st_size+4095 & (-4096)) ) {
+ if ( p->fileOffset+p->size > (uint64_t)(stat_buf.st_size+4095 & (-4096)) ) {
dyld::log("dyld: shared cached file is corrupt: %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
goodCache = false;
}
}
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // if shared cache is code signed, add a mapping for the code signature
+ uint32_t signatureSize = header->codeSignatureSize;
+ // zero size in header means signature runs to end-of-file
+ if ( signatureSize == 0 )
+ signatureSize = stat_buf.st_size - header->codeSignatureOffset;
+ if ( signatureSize != 0 ) {
+ int linkeditMapping = mappingCount-1;
+ codeSignatureMappingIndex = mappingCount++;
+ mappings[codeSignatureMappingIndex].sfm_address = mappings[linkeditMapping].sfm_address + mappings[linkeditMapping].sfm_size;
+ mappings[codeSignatureMappingIndex].sfm_size = (signatureSize+4095) & (-4096);
+ mappings[codeSignatureMappingIndex].sfm_file_offset = header->codeSignatureOffset;
+ mappings[codeSignatureMappingIndex].sfm_max_prot = VM_PROT_READ;
+ mappings[codeSignatureMappingIndex].sfm_init_prot = VM_PROT_READ;
+ }
+#endif
}
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// sanity check that /usr/lib/libSystem.B.dylib stat() info matches cache
if ( header->imagesCount * sizeof(dyld_cache_image_info) + header->imagesOffset < 8192 ) {
bool foundLibSystem = false;
}
}
if ( !sSharedCacheIgnoreInodeAndTimeStamp && !foundLibSystem ) {
- dyld::log("dyld: shared cached file was build against a different libSystem.dylib, ignoring cache\n");
+ dyld::log("dyld: shared cached file was built against a different libSystem.dylib, ignoring cache.\n"
+ "to update dyld shared cache run: 'sudo update_dyld_shared_cache' then reboot.\n");
goodCache = false;
}
}
-
+#endif
if ( goodCache ) {
- const shared_file_mapping_np* mappings = (shared_file_mapping_np*)&firstPages[header->mappingOffset];
- if (_shared_region_map_np(fd, header->mappingCount, mappings) == 0) {
- // sucessfully mapped cache into shared region
+ long cacheSlide = 0;
+ void* slideInfo = NULL;
+ uint32_t slideInfoSize = 0;
+ #if SLIDEABLE_CACHE_SUPPORT
+ // check if shared cache contains slid info
+ if ( header->slideInfoSize != 0 ) {
+ // <rdar://problem/8611968> don't slide shared cache if ASLR disabled (main executable didn't slide)
+ if ( sMainExecutable->isPositionIndependentExecutable() && (sMainExecutable->getSlide() == 0) )
+ cacheSlide = 0;
+ else {
+ // generate random slide amount
+ cacheSlide = pickCacheSlide(mappingCount, mappings);
+ slideInfo = (void*)(long)(mappings[2].sfm_address + (header->slideInfoOffset - mappings[2].sfm_file_offset));
+ slideInfoSize = header->slideInfoSize;
+ // add VM_PROT_SLIDE bit to __DATA area of cache
+ mappings[1].sfm_max_prot |= VM_PROT_SLIDE;
+ mappings[1].sfm_init_prot |= VM_PROT_SLIDE;
+ }
+ }
+ #endif
+ if (_shared_region_map_and_slide_np(fd, mappingCount, mappings, codeSignatureMappingIndex, cacheSlide, slideInfo, slideInfoSize) == 0) {
+ // successfully mapped cache into shared region
sSharedCache = (dyld_cache_header*)mappings[0].sfm_address;
+ sSharedCacheSlide = cacheSlide;
+ dyld_all_image_infos.sharedCacheSlide = cacheSlide;
+ //dyld::log("sSharedCache=%p sSharedCacheSlide=0x%08lX\n", sSharedCache, sSharedCacheSlide);
+ }
+ else {
+ if ( gLinkContext.verboseMapping )
+ dyld::log("dyld: shared cached file could not be mapped\n");
}
}
}
// tell gdb where the shared cache is
if ( sSharedCache != NULL ) {
- const shared_file_mapping_np* const start = (shared_file_mapping_np*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset);
+ const dyld_cache_mapping_info* const start = (dyld_cache_mapping_info*)((uint8_t*)sSharedCache + sSharedCache->mappingOffset);
dyld_shared_cache_ranges.sharedRegionsCount = sSharedCache->mappingCount;
// only room to tell gdb about first four regions
if ( dyld_shared_cache_ranges.sharedRegionsCount > 4 )
dyld_shared_cache_ranges.sharedRegionsCount = 4;
if ( gLinkContext.verboseMapping ) {
if ( gLinkContext.sharedRegionMode == ImageLoader::kUseSharedRegion )
- dyld::log("dyld: Mapping shared cache from %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
+ dyld::log("dyld: Mapping shared cache from %s/" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
else if ( gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion )
- dyld::log("dyld: Mapping private shared cache from %s" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
+ dyld::log("dyld: Mapping private shared cache from %s/" DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME "\n", sSharedCacheDir);
}
- const shared_file_mapping_np* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount];
+ const dyld_cache_mapping_info* const end = &start[dyld_shared_cache_ranges.sharedRegionsCount];
int index = 0;
- for (const shared_file_mapping_np* p = start; p < end; ++p, ++index ) {
- dyld_shared_cache_ranges.ranges[index].start = p->sfm_address;
- dyld_shared_cache_ranges.ranges[index].length = p->sfm_size;
+ for (const dyld_cache_mapping_info* p = start; p < end; ++p, ++index ) {
+ dyld_shared_cache_ranges.ranges[index].start = p->address+sSharedCacheSlide;
+ dyld_shared_cache_ranges.ranges[index].length = p->size;
if ( gLinkContext.verboseMapping ) {
- dyld::log(" 0x%08llX->0x%08llX %s%s%s init=%x, max=%x\n", p->sfm_address, p->sfm_address+p->sfm_size-1,
- ((p->sfm_init_prot & VM_PROT_READ) ? "read " : ""),
- ((p->sfm_init_prot & VM_PROT_WRITE) ? "write " : ""),
- ((p->sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : ""), p->sfm_init_prot, p->sfm_max_prot);
+ dyld::log(" 0x%08llX->0x%08llX %s%s%s init=%x, max=%x\n",
+ p->address+sSharedCacheSlide, p->address+sSharedCacheSlide+p->size-1,
+ ((p->initProt & VM_PROT_READ) ? "read " : ""),
+ ((p->initProt & VM_PROT_WRITE) ? "write " : ""),
+ ((p->initProt & VM_PROT_EXECUTE) ? "execute " : ""), p->initProt, p->maxProt);
}
#if __i386__
// If a non-writable and executable region is found in the R/W shared region, then this is __IMPORT segments
// This is an old cache. Make writable. dyld no longer supports turn W on and off as it binds
- if ( (p->sfm_init_prot == (VM_PROT_READ|VM_PROT_EXECUTE)) && ((p->sfm_address & 0xF0000000) == 0xA0000000) ) {
- if ( p->sfm_size != 0 ) {
+ if ( (p->initProt == (VM_PROT_READ|VM_PROT_EXECUTE)) && ((p->address & 0xF0000000) == 0xA0000000) ) {
+ if ( p->size != 0 ) {
vm_prot_t prot = VM_PROT_EXECUTE | PROT_READ | VM_PROT_WRITE;
- vm_protect(mach_task_self(), p->sfm_address, p->sfm_size, false, prot);
+ vm_protect(mach_task_self(), p->address, p->size, false, prot);
if ( gLinkContext.verboseMapping ) {
- dyld::log("%18s at 0x%08llX->0x%08llX altered permissions to %c%c%c\n", "", p->sfm_address,
- p->sfm_address+p->sfm_size-1,
+ dyld::log("%18s at 0x%08llX->0x%08llX altered permissions to %c%c%c\n", "", p->address,
+ p->address+p->size-1,
(prot & PROT_READ) ? 'r' : '.', (prot & PROT_WRITE) ? 'w' : '.', (prot & PROT_EXEC) ? 'x' : '.' );
}
}
}
#endif
}
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ if ( gLinkContext.verboseMapping ) {
+ // list the code blob
+ dyld_cache_header* header = (dyld_cache_header*)sSharedCache;
+ uint32_t signatureSize = header->codeSignatureSize;
+ // zero size in header means signature runs to end-of-file
+ if ( signatureSize == 0 ) {
+ struct stat stat_buf;
+ if ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &stat_buf) == 0 )
+ signatureSize = stat_buf.st_size - header->codeSignatureOffset;
+ }
+ if ( signatureSize != 0 ) {
+ const dyld_cache_mapping_info* const last = &start[dyld_shared_cache_ranges.sharedRegionsCount-1];
+ uint64_t codeBlobStart = last->address + last->size;
+ dyld::log(" 0x%08llX->0x%08llX (code signature)\n", codeBlobStart, codeBlobStart+signatureSize);
+ }
+ }
+ // check for file that enables dyld shared cache dylibs to be overridden
+ struct stat enableStatBuf;
+ sDylibsOverrideCache = ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0 );
+#endif
}
}
context.mustBeBundle = true;
context.mustBeDylib = false;
context.canBePIE = false;
- context.origin = false;
- context.rpath = false;
+ context.origin = NULL;
+ context.rpath = NULL;
return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context);
}
dyld::log("dyld: %s\n", message);
setErrorMessage(message);
uintptr_t terminationFlags = 0;
- if ( !gLinkContext.startedInitializingMainExecutable )
+ if ( !gLinkContext.startedInitializingMainExecutable )
terminationFlags = 1;
setAlImageInfosHalt(error_string, terminationFlags);
dyld_fatal_error(error_string);
}
+static void setErrorStrings(unsigned errorCode, const char* errorClientOfDylibPath,
+ const char* errorTargetDylibPath, const char* errorSymbol)
+{
+ dyld_all_image_infos.errorKind = errorCode;
+ dyld_all_image_infos.errorClientOfDylibPath = errorClientOfDylibPath;
+ dyld_all_image_infos.errorTargetDylibPath = errorTargetDylibPath;
+ dyld_all_image_infos.errorSymbol = errorSymbol;
+}
+
uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer)
{
}
-#if COMPRESSED_DYLD_INFO_SUPPORT
uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset)
{
uintptr_t result = 0;
// bind lazy pointer and return it
try {
- result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext);
+ result = (*imageLoaderCache)->doBindFastLazySymbol(lazyBindingInfoOffset, gLinkContext,
+ (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->acquireGlobalDyldLock : NULL,
+ (dyld::gLibSystemHelpers != NULL) ? dyld::gLibSystemHelpers->releaseGlobalDyldLock : NULL);
}
catch (const char* message) {
dyld::log("dyld: lazy symbol binding failed: %s\n", message);
// return target address to glue which jumps to it with real parameters restored
return result;
}
-#endif // COMPRESSED_DYLD_INFO_SUPPORT
#if DYLD_SHARED_CACHE_SUPPORT
gLinkContext.inSharedCache = &inSharedCache;
#endif
+ gLinkContext.setErrorStrings = &setErrorStrings;
#if SUPPORT_OLD_CRT_INITIALIZATION
gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay;
#endif
return false;
}
+
+
+//
+// Peeks at a dylib file and returns its current_version and install_name.
+// Returns false on error.
+//
+static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName)
+{
+ // open file (automagically closed when this function exits)
+ FileOpener file(dylibPath);
-
+ if ( file.getFileDescriptor() == -1 )
+ return false;
+
+ uint8_t firstPage[4096];
+ if ( pread(file.getFileDescriptor(), firstPage, 4096, 0) != 4096 )
+ return false;
+
+ // if fat wrapper, find usable sub-file
+ const fat_header* fileStartAsFat = (fat_header*)firstPage;
+ if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) {
+ uint64_t fileOffset;
+ uint64_t fileLength;
+ if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) {
+ if ( pread(file.getFileDescriptor(), firstPage, 4096, fileOffset) != 4096 )
+ return false;
+ }
+ else {
+ return false;
+ }
+ }
+
+ // check mach-o header
+ const mach_header* mh = (mach_header*)firstPage;
+ if ( mh->magic != sMainExecutableMachHeader->magic )
+ return false;
+ if ( mh->cputype != sMainExecutableMachHeader->cputype )
+ return false;
+
+ // scan load commands for LC_ID_DYLIB
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
+ const struct load_command* const cmdsReadEnd = (struct load_command*)(((char*)mh)+4096);
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ switch (cmd->cmd) {
+ case LC_ID_DYLIB:
+ {
+ const struct dylib_command* id = (struct dylib_command*)cmd;
+ *version = id->dylib.current_version;
+ if ( installName != NULL )
+ strlcpy(installName, (char *)id + id->dylib.name.offset, PATH_MAX);
+ return true;
+ }
+ break;
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ if ( cmd > cmdsReadEnd )
+ return false;
+ }
+
+ return false;
+}
+
#if 0
static void printAllImages()
{
}
#endif
-
void link(ImageLoader* image, bool forceLazysBound, const ImageLoader::RPathChain& loaderRPaths)
{
// add to list of known images. This did not happen at creation time for bundles
void runInitializers(ImageLoader* image)
{
// do bottom up initialization
- image->runInitializers(gLinkContext);
+ ImageLoader::InitializerTimingList initializerTimes[sAllImages.size()];
+ initializerTimes[0].count = 0;
+ image->runInitializers(gLinkContext, initializerTimes[0]);
}
void garbageCollectImages()
}
}
+static bool processRestricted(const macho_header* mainExecutableMH)
+{
+ // all processes with setuid or setgid bit set are restricted
+ if ( issetugid() )
+ return true;
+
+ if ( hasRestrictedSegment(mainExecutableMH) && (geteuid() != 0) ) {
+ // existence of __RESTRICT/__restrict section make process restricted
+ return true;
+ }
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // ask kernel if code signature of program makes it restricted
+ uint32_t flags;
+ if ( syscall(SYS_csops /* 169 */,
+ 0 /* asking about myself */,
+ CS_OPS_STATUS,
+ &flags,
+ sizeof(flags)) != -1) {
+ if (flags & CS_RESTRICT)
+ return true;
+ }
+#endif
+ return false;
+}
+
+
//
// Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which
// sets up some registers and call this function.
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], const char* apple[])
{
+ CRSetCrashLogMessage("dyld: launch started");
#ifdef ALTERNATIVE_LOGFILE
sLogfile = open(ALTERNATIVE_LOGFILE, O_WRONLY | O_CREAT | O_APPEND);
if ( sLogfile == -1 ) {
dyld::log("error opening alternate log file %s, errno = %d\n", ALTERNATIVE_LOGFILE, errno);
}
#endif
+
+#if LOG_BINDINGS
+ char bindingsLogPath[256];
+ const char* shortProgName = "unknown";
+ if ( argc > 0 ) {
+ shortProgName = strrchr(argv[0], '/');
+ if ( shortProgName == NULL )
+ shortProgName = argv[0];
+ else
+ ++shortProgName;
+ }
+ mysprintf(bindingsLogPath, "/tmp/bindings/%d-%s", getpid(), shortProgName);
+ sBindingsLogfile = open(bindingsLogPath, O_WRONLY | O_CREAT, 0666);
+ if ( sBindingsLogfile == -1 ) {
+ ::mkdir("/tmp/bindings", 0777);
+ sBindingsLogfile = open(bindingsLogPath, O_WRONLY | O_CREAT, 0666);
+ }
+ //dyld::log("open(%s) => %d, errno = %d\n", bindingsLogPath, sBindingsLogfile, errno);
+#endif
setContext(mainExecutableMH, argc, argv, envp, apple);
// Pickup the pointer to the exec path.
}
uintptr_t result = 0;
sMainExecutableMachHeader = mainExecutableMH;
- sProcessIsRestricted = issetugid();
- if ( geteuid() != 0 ) {
- // if we are not root, see if the binary is requesting restricting the use of DYLD_ env vars.
- sProcessIsRestricted |= hasRestrictedSegment(mainExecutableMH);
- }
- if ( sProcessIsRestricted )
+ sProcessIsRestricted = processRestricted(mainExecutableMH);
+ if ( sProcessIsRestricted ) {
+#if SUPPORT_LC_DYLD_ENVIRONMENT
+ checkLoadCommandEnvironmentVariables();
+#if SUPPORT_VERSIONED_PATHS
+ checkVersionedPaths();
+#endif
+#endif
pruneEnvironmentVariables(envp, &apple);
+ // set again because envp and apple may have changed or moved
+ setContext(mainExecutableMH, argc, argv, envp, apple);
+ }
else
checkEnvironmentVariables(envp, ignoreEnvironmentVariables);
if ( sEnv.DYLD_PRINT_OPTS )
getHostInfo();
// install gdb notifier
stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
+ stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
// make initial allocations large enough that it is unlikely to need to be re-alloced
sAllImages.reserve(INITIAL_IMAGE_COUNT);
sImageRoots.reserve(16);
#endif
try {
+ CRSetCrashLogMessage("dyld: launch, loading dependent libraries");
// instantiate ImageLoader for main executable
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);
sMainExecutable->setNeverUnload();
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
link(image, sEnv.DYLD_BIND_AT_LAUNCH, ImageLoader::RPathChain(NULL, NULL));
+ // only INSERTED libraries can interpose
+ image->registerInterposing();
}
}
+ CRSetCrashLogMessage("dyld: launch, running initializers");
#if SUPPORT_OLD_CRT_INITIALIZATION
// Old way is to run initializers via a callback from crt1.o
if ( ! gRunInitializersOldWay )
initializeMainExecutable(); // run all initializers
}
catch(const char* message) {
+ syncAllImages();
halt(message);
}
catch(...) {
sLogfile = STDERR_FILENO;
}
#endif
+ CRSetCrashLogMessage(NULL);
return result;
}
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
extern ImageLoader::LinkContext gLinkContext;
extern bool gLogAPIs;
+ extern bool gSharedCacheOverridden;
extern const struct LibSystemHelpers* gLibSystemHelpers;
#if SUPPORT_OLD_CRT_INITIALIZATION
extern bool gRunInitializersOldWay;
extern void clearErrorMessage();
extern bool mainExecutablePrebound();
extern ImageLoader* mainExecutable();
- extern void processDyldEnvironmentVarible(const char* key, const char* value);
+ extern void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainDir);
extern void registerImageStateSingleChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler);
extern void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler);
extern void garbageCollectImages();
extern const void* imMemorySharedCacheHeader();
extern uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset);
extern bool inSharedCache(const char* path);
-
+#if LOG_BINDINGS
+ extern void logBindings(const char* format, ...);
+#endif
};
--- /dev/null
+#
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+#
+
+#
+# Cluster these __DATA symbols in dyld to reduce pages dirtied
+#
+_sDyldInfo.0
+_sDyldInfo.1
+_sDyldInfo.2
+_sDyldInfo.3
+_sDyldInfo.4
+_sDyldTextEnd
+_mach_init_inited.7154.b
+__task_reply_port
+_mach_task_self_
+_mach_host_self_
+_vm_page_size
+_vm_page_mask
+_vm_page_shift
+___stack_chk_guard
+__ZN4dyldL23sFrameworkFallbackPathsE
+__ZN4dyldL21sLibraryFallbackPathsE
+__ZL11initialPool
+__ZN4dyld12gLinkContextE
+__ZN4dyld17gLibSystemHelpersE
+__ZN4dyldL17sSharedCacheSlideE
+_dyld_shared_cache_ranges
+__ZN11ImageLoader27fgImagesUsedFromSharedCacheE
+__ZN11ImageLoader19fgInterposingTuplesE
+__ZN11ImageLoader24fgTotalLoadLibrariesTimeE
+__ZN11ImageLoader24fgTotalLoadLibrariesTimeE
+__ZN11ImageLoader17fgTotalRebaseTimeE
+__ZN11ImageLoader15fgTotalBindTimeE
+__ZN11ImageLoader19fgTotalWeakBindTimeE
+__ZN11ImageLoader10fgTotalDOFE
+__ZN11ImageLoader15fgTotalInitTimeE
+__ZN11ImageLoader22fgTotalBytesPreFetchedE
+__ZN11ImageLoader18fgTotalBytesMappedE
+__ZN11ImageLoader21fgTotalSegmentsMappedE
+__ZN11ImageLoader19fgTotalRebaseFixupsE
+__ZN11ImageLoader17fgTotalBindFixupsE
+__ZN11ImageLoader26fgTotalBindSymbolsResolvedE
+__ZN11ImageLoader24fgTotalBindImageSearchesE
+__ZN11ImageLoader29fgTotalPossibleLazyBindFixupsE
+__ZN11ImageLoader21fgTotalLazyBindFixupsE
+__ZN11ImageLoader27fgImagesRequiringCoalescingE
+__ZN11ImageLoader21fgNextPIEDylibAddressE
+__ZN11ImageLoader26fgImagesWithUsedPrebindingE
+__ZN11ImageLoader26fgImagesHasWeakDefinitionsE
+__ZN16ImageLoaderMachO26fgSymbolTableBinarySearchsE
+__ZN16ImageLoaderMachO19fgSymbolTrieSearchsE
+__ZN11ImageLoader13fgLoadOrdinalE
+__ZN4dyldL9sExecPathE
+__ZN4dyldL25sMainExecutableMachHeaderE
+__ZN4dyldL12sSharedCacheE
+__ZN4dyldL17sUndefinedHandlerE
+__ZN4dyldL10sAllImagesE
+__ZN4dyldL18sAddImageCallbacksE
+__ZN4dyldL11sImageRootsE
+__ZN4dyldL29sImageFilesNeedingTerminationE
+__ZN4dyldL35sImageFilesNeedingDOFUnregistrationE
+__ZN4dyldL35sImageFilesNeedingDOFUnregistrationE
+__ZN4dyldL21sRemoveImageCallbacksE
+__ZN4dyldL15sSingleHandlersE
+__ZN4dyldL14sBatchHandlersE
+__ZN4dyldL15sMainExecutableE
+__ZN4dyldL4sEnvE
+__ZL11sImageInfos
+__ZL11sImageUUIDs
+__ZN4dyldL19sInsertedDylibCountE
+__ZN4dyldL20sProcessIsRestrictedE
+__ZL17sObjectFileImages
+__ZN11ImageLoader23fgDynamicImageReExportsE
+__ZN4dyldL15sDylibOverridesE
+__ZN4dyldL19sInsertedDylibCountE
+__ZN4dyldL20sProcessIsRestrictedE
+__ZN4dyldL18sMappedRangesStartE
+__thread
#undef _POSIX_C_SOURCE
#include "dlfcn.h"
+// from dyldExceptions.c
+extern "C" void __Unwind_SjLj_SetThreadKey(pthread_key_t key);
+
+// from dyld_gdb.cpp
+extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]);
// deprecated APIs are still availble on Mac OS X, but not on iPhone OS
#if __IPHONE_OS_VERSION_MIN_REQUIRED
// The following functions have no prototype in any header. They are special cases
// where _dyld_func_lookup() is used directly.
-static void _dyld_fork_child();
static void _dyld_make_delayed_module_initializer_calls();
static void registerThreadHelpers(const dyld::LibSystemHelpers*);
#if DEPRECATED_APIS_SUPPORTED
static void client_dyld_lookup_and_bind(const char* symbolName, void** address, NSModule* module);
static bool client_NSIsSymbolNameDefined(const char* symbolName);
#endif // DEPRECATED_APIS_SUPPORTED
+#if !__arm__
static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info);
+#endif
static void unimplemented()
{
{"__dyld_dlopen", (void*)dlopen },
{"__dyld_dlsym", (void*)dlsym },
{"__dyld_dlopen_preflight", (void*)dlopen_preflight },
- {"__dyld_get_image_header_containing_address", (void*)_dyld_get_image_header_containing_address },
{"__dyld_image_count", (void*)_dyld_image_count },
{"__dyld_get_image_header", (void*)_dyld_get_image_header },
{"__dyld_get_image_vmaddr_slide", (void*)_dyld_get_image_vmaddr_slide },
#if !__arm__
{"__dyld_find_unwind_sections", (void*)client_dyld_find_unwind_sections },
#endif
-#if __i386__ || __x86_64__
+#if __i386__ || __x86_64__ || __arm__
{"__dyld_fast_stub_entry", (void*)dyld::fastBindLazySymbol },
#endif
{"__dyld_image_path_containing_address", (void*)dyld_image_path_containing_address },
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ {"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden },
+#endif
// deprecated
#if DEPRECATED_APIS_SUPPORTED
+ {"__dyld_get_image_header_containing_address", (void*)_dyld_get_image_header_containing_address },
{"__dyld_lookup_and_bind", (void*)client_dyld_lookup_and_bind },
{"__dyld_lookup_and_bind_with_hint", (void*)_dyld_lookup_and_bind_with_hint },
{"__dyld_lookup_and_bind_fully", (void*)_dyld_lookup_and_bind_fully },
// already linked, so clone a new one and link it
objectFileImage->image = dyld::cloneImage(objectFileImage->image);
}
-
+
// for memory based images, set moduleName as the name anyone calling _dyld_get_image_name() will see
- if ( objectFileImage->image->getPath() == NULL )
+ if ( objectFileImage->image->getPath() == NULL ) {
objectFileImage->image->setPath(moduleName);
+ // <rdar://problem/8812589> dyld has NULL paths in image info array
+ dyld_image_info info;
+ info.imageLoadAddress = objectFileImage->image->machHeader();
+ info.imageFilePath = moduleName;
+ info.imageFileModDate = 0;
+ addImagesToAllImages(1, &info);
+ }
// support private bundles
if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 )
// Call by fork() in libSystem after the kernel trap is done on the child side
-static void _dyld_fork_child()
+void _dyld_fork_child()
{
if ( dyld::gLogAPIs )
dyld::log("%s()\n", __func__);
return false;
}
+
static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers)
{
dyld::gLibSystemHelpers = helpers;
// let gdb know it is safe to run code in inferior that might call malloc()
dyld_all_image_infos.libSystemInitialized = true;
+
+#if __arm__
+ if ( helpers->version >= 5 ) {
+ // create key use by dyld exception handling
+ pthread_key_t key;
+ int result = helpers->pthread_key_create(&key, NULL);
+ if ( result == 0 )
+ __Unwind_SjLj_SetThreadKey(key);
+ }
+#endif
}
return true;
#endif
+ CRSetCrashLogMessage("dyld: in dlopen_preflight()");
+
bool result = false;
std::vector<const char*> rpathsFromCallerImage;
try {
ImageLoader* image = NULL;
const bool leafName = (strchr(path, '/') == NULL);
const bool absolutePath = (path[0] == '/');
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ char canonicalPath[PATH_MAX];
+ // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
+ if ( !leafName ) {
+ // make path canonical if it contains a // or ./
+ if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
+ const char* lastSlash = strrchr(path, '/');
+ char dirPath[PATH_MAX];
+ if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
+ dirPath[lastSlash-path] = '\0';
+ if ( realpath(dirPath, canonicalPath) ) {
+ strlcat(canonicalPath, "/", sizeof(canonicalPath));
+ if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
+ // if all fit in buffer, use new canonical path
+ path = canonicalPath;
+ }
+ }
+ }
+ }
+ }
+#endif
dyld::LoadContext context;
context.useSearchPaths = true;
context.useFallbackPaths= leafName; // a partial path implies don't use fallback paths
const char* str = *it;
free((void*)str);
}
+ CRSetCrashLogMessage(NULL);
return result;
}
bool lockHeld = false;
if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 4) ) {
dyld::gLibSystemHelpers->acquireGlobalDyldLock();
+ CRSetCrashLogMessage("dyld: in dlopen()");
lockHeld = true;
}
const bool leafName = (strchr(path, '/') == NULL);
const bool absolutePath = (path[0] == '/');
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ char canonicalPath[PATH_MAX];
+ // <rdar://problem/7017050> dlopen() not opening frameworks from shared cache with // or ./ in path
+ if ( !leafName ) {
+ // make path canonical if it contains a // or ./
+ if ( (strstr(path, "//") != NULL) || (strstr(path, "./") != NULL) ) {
+ const char* lastSlash = strrchr(path, '/');
+ char dirPath[PATH_MAX];
+ if ( strlcpy(dirPath, path, sizeof(dirPath)) < sizeof(dirPath) ) {
+ dirPath[lastSlash-path] = '\0';
+ if ( realpath(dirPath, canonicalPath) ) {
+ strlcat(canonicalPath, "/", sizeof(canonicalPath));
+ if ( strlcat(canonicalPath, lastSlash+1, sizeof(canonicalPath)) < sizeof(canonicalPath) ) {
+ // if all fit in buffer, use new canonical path
+ path = canonicalPath;
+ }
+ }
+ }
+ }
+ }
+#endif
dyld::LoadContext context;
context.useSearchPaths = true;
context.useFallbackPaths= leafName; // a partial path means no fallback paths
// release global dyld lock early, this enables initializers to do threaded operations
if ( lockHeld ) {
+ CRSetCrashLogMessage(NULL);
dyld::gLibSystemHelpers->releaseGlobalDyldLock();
lockHeld = false;
}
dlerrorSet("image not already loaded");
}
- if ( lockHeld )
+ if ( lockHeld ) {
+ CRSetCrashLogMessage(NULL);
dyld::gLibSystemHelpers->releaseGlobalDyldLock();
+ }
return result;
}
if ( dyld::gLogAPIs )
dyld::log("%s(%p, %p)\n", __func__, address, info);
+ CRSetCrashLogMessage("dyld: in dladdr()");
ImageLoader* image = dyld::findImageContainingAddress(address);
if ( image != NULL ) {
info->dli_fname = image->getPath();
// special case lookup of header
info->dli_sname = "__dso_handle";
info->dli_saddr = info->dli_fbase;
+ CRSetCrashLogMessage(NULL);
return 1; // success
}
// find closest symbol in the image
if ( info->dli_sname[0] == '_' )
info->dli_sname = info->dli_sname +1; // strip off leading underscore
//dyld::log("dladdr(%p) => %p %s\n", address, info->dli_saddr, info->dli_sname);
+ CRSetCrashLogMessage(NULL);
return 1; // success
}
info->dli_sname = NULL;
info->dli_saddr = NULL;
+ CRSetCrashLogMessage(NULL);
return 1; // success
}
+ CRSetCrashLogMessage(NULL);
return 0; // failure
}
if ( dyld::gLogAPIs )
dyld::log("%s(%p, %s)\n", __func__, handle, symbolName);
+ CRSetCrashLogMessage("dyld: in dlsym()");
dlerrorClear();
const ImageLoader* image;
// magic "search all" handle
if ( handle == RTLD_DEFAULT ) {
if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) {
+ CRSetCrashLogMessage(NULL);
return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
}
const char* str = dyld::mkstringf("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
+ CRSetCrashLogMessage(NULL);
return NULL;
}
image = dyld::mainExecutable();
sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way
if ( sym != NULL ) {
+ CRSetCrashLogMessage(NULL);
return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
}
const char* str = dyld::mkstringf("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
+ CRSetCrashLogMessage(NULL);
return NULL;
}
// magic "search what I would see" handle
if ( handle == RTLD_NEXT ) {
+#if __ppc__
+ // <rdar://problem/7628929> work around for llvmgcc bug
+ void* fa = __builtin_frame_address(0);
+ fa = *(void**)fa;
+ fa = *(void**)fa;
+ void* callerAddress = *((void**)(((int)fa)+8));
+#else
void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
+#endif
ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
sym = callerImage->findExportedSymbolInDependentImages(underscoredName, dyld::gLinkContext, &image); // don't search image, but do search what it links against
if ( sym != NULL ) {
+ CRSetCrashLogMessage(NULL);
return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
}
const char* str = dyld::mkstringf("dlsym(RTLD_NEXT, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
+ CRSetCrashLogMessage(NULL);
return NULL;
}
// magic "search me, then what I would see" handle
ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress);
sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against
if ( sym != NULL ) {
+ CRSetCrashLogMessage(NULL);
return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
}
const char* str = dyld::mkstringf("dlsym(RTLD_SELF, %s): symbol not found", symbolName);
dlerrorSet(str);
free((void*)str);
+ CRSetCrashLogMessage(NULL);
return NULL;
}
// real handle
sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against
if ( sym != NULL ) {
+ CRSetCrashLogMessage(NULL);
return (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext);
}
const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName);
else {
dlerrorSet("invalid handle passed to dlsym()");
}
+ CRSetCrashLogMessage(NULL);
return NULL;
}
dyld::registerImageStateSingleChangeHandler(state, handler);
}
-
const char* dyld_image_path_containing_address(const void* address)
{
if ( dyld::gLogAPIs )
}
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+bool dyld_shared_cache_some_image_overridden()
+{
+ return dyld::gSharedCacheOverridden;
+}
+#endif
+
+
+
#include "dyldLock.h"
-extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
+extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
+extern "C" void __cxa_finalize(const void *dso);
-#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__)
+#ifndef LC_LOAD_UPWARD_DYLIB
+ #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */
+#endif
+
+#define DYLD_SHARED_CACHE_SUPPORT (__ppc__ || __i386__ || __ppc64__ || __x86_64__ || __arm__)
// deprecated APIs are still availble on Mac OS X, but not on iPhone OS
#if __IPHONE_OS_VERSION_MIN_REQUIRED
const NSLinkEditErrorHandlers* handlers)
{
DYLD_LOCK_THIS_BLOCK;
- static void (*p)(
- void (*undefined)(const char* symbol_name),
- NSModule (*multiple)(NSSymbol s, NSModule old, NSModule newhandler),
- void (*linkEdit)(NSLinkEditErrors c, int errorNumber,
- const char* fileName, const char* errorString)) = NULL;
+ typedef void (*ucallback_t)(const char* symbol_name);
+ typedef NSModule (*mcallback_t)(NSSymbol s, NSModule old, NSModule newhandler);
+ typedef void (*lcallback_t)(NSLinkEditErrors c, int errorNumber,
+ const char* fileName, const char* errorString);
+ static void (*p)(ucallback_t undefined, mcallback_t multiple, lcallback_t linkEdit) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_install_handlers", (void**)&p);
- p(handlers->undefined, handlers->multiple, handlers->linkEdit);
+ mcallback_t m = handlers->multiple;
+ p(handlers->undefined, m, handlers->linkEdit);
}
const char*
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)
void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
{
DYLD_LOCK_THIS_BLOCK;
- static void (*p)(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) = NULL;
+ typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
+ static void (*p)(callback_t func) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_register_func_for_add_image", (void**)&p);
void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide))
{
DYLD_LOCK_THIS_BLOCK;
- static void (*p)(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) = NULL;
+ typedef void (*callback_t)(const struct mach_header *mh, intptr_t vmaddr_slide);
+ static void (*p)(callback_t func) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_register_func_for_remove_image", (void**)&p);
void (*monaddition)(char *lowpc, char *highpc))
{
DYLD_LOCK_THIS_BLOCK;
- static void (*p)(void (*monaddition)(char *lowpc, char *highpc)) = NULL;
+ typedef void (*monproc)(char *lowpc, char *highpc);
+ static void (*p)(monproc monaddition) = NULL;
if(p == NULL)
_dyld_func_lookup("__dyld_moninit", (void**)&p);
// the table passed to dyld containing thread helpers
-static dyld::LibSystemHelpers sHelpers = { 6, &dyldGlobalLockAcquire, &dyldGlobalLockRelease,
+static dyld::LibSystemHelpers sHelpers = { 8, &dyldGlobalLockAcquire, &dyldGlobalLockRelease,
&getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit,
#if DYLD_SHARED_CACHE_SUPPORT
&shared_cache_missing, &shared_cache_out_of_date,
#endif
NULL, NULL,
&pthread_key_create, &pthread_setspecific,
- &malloc_size };
+ &malloc_size,
+ &pthread_getspecific,
+ &__cxa_finalize};
//
// during initialization of libSystem this routine will run
// and call dyld, registering the helper functions.
//
-extern "C" void _dyld_initializer() __attribute__((visibility("hidden")));
+extern "C" void tlv_initializer();
+extern "C" void _dyld_initializer();
void _dyld_initializer()
{
DYLD_LOCK_INITIALIZER;
_dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p);
if(p != NULL)
p(&sHelpers);
+
+ tlv_initializer();
}
}
#if !__arm__
-__attribute__((visibility("hidden")))
bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info)
{
DYLD_NO_LOCK_THIS_BLOCK;
#endif
-#if __i386__ || __x86_64__
+#if __i386__ || __x86_64__ || __arm__
__attribute__((visibility("hidden")))
void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo)
{
return p(addr);
}
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+bool dyld_shared_cache_some_image_overridden()
+{
+ DYLD_NO_LOCK_THIS_BLOCK;
+ static bool (*p)() = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_shared_cache_some_image_overridden", (void**)&p);
+ return p();
+}
+#endif
+
+
+// SPI called __fork
+void _dyld_fork_child()
+{
+ DYLD_NO_LOCK_THIS_BLOCK;
+ static void (*p)() = NULL;
+
+ if(p == NULL)
+ _dyld_func_lookup("__dyld_fork_child", (void**)&p);
+ return p();
+}
+
+
+
#include <stdbool.h>
#include <stdarg.h>
#include <pthread.h>
+#include <Availability.h>
#include "mach-o/dyld_priv.h"
#include "dyldLibSystemInterface.h"
#endif
-#if __i386__ || __x86_64 || __ppc__
+//
+// The standard versions of __cxa_get_globals*() from libstdc++-static.a cannot be used.
+// On Mac OS X, they use keymgr which dyld does not implement.
+// On iPhoneOS, they use pthread_key_create which dyld cannot use.
+//
+// Implement these ourselves to make upcalls into libSystem to malloc and create a pthread key
+//
+static pthread_key_t sCxaKey = 0;
+static char sPreMainCxaGlobals[2*sizeof(long)];
+
+// called by libstdc++.a
+char* __cxa_get_globals()
+{
+ // if libSystem.dylib not yet initialized, or is old libSystem, use shared global
+ if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) )
+ return sPreMainCxaGlobals;
+
+ if ( sCxaKey == 0 ) {
+ // create key
+ // we don't need a lock because only one thread can be in dyld at a time
+ _ZN4dyld17gLibSystemHelpersE->pthread_key_create(&sCxaKey, &free);
+ }
+ char* data = (char*)pthread_getspecific(sCxaKey);
+ if ( data == NULL ) {
+ data = calloc(2,sizeof(void*));
+ _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data);
+ }
+ return data;
+}
+
+// called by libstdc++.a
+char* __cxa_get_globals_fast()
+{
+ // if libSystem.dylib not yet initialized, or is old libSystem, use shared global
+ if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) )
+ return sPreMainCxaGlobals;
+
+ return pthread_getspecific(sCxaKey);
+}
+
+
+
+
+#if __x86_64__ || __i386__ || __ppc__
+//
+// The intel/ppc versions of dyld uses zero-cost exceptions which are handled by
+// linking with a special copy of libunwind.a
+//
static struct dyld_unwind_sections sDyldInfo;
static void* sDyldTextEnd;
-static pthread_key_t sCxaKey = 0;
-static char sPreMainCxaGlobals[2*sizeof(long)];
// called by dyldStartup.s very early
void dyld_exceptions_init(struct mach_header* mh, intptr_t slide)
}
}
-
-
-// called by libstdc++.a
-char* __cxa_get_globals()
-{
- // if libSystem.dylib not yet initialized, or is old libSystem, use shared global
- if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) )
- return sPreMainCxaGlobals;
-
- if ( sCxaKey == 0 ) {
- // create key
- // we don't need a lock because only one thread can be in dyld at a time
- _ZN4dyld17gLibSystemHelpersE->pthread_key_create(&sCxaKey, &free);
- }
- char* data = (char*)pthread_getspecific(sCxaKey);
- if ( data == NULL ) {
- data = calloc(2,sizeof(void*));
- _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sCxaKey, data);
- }
- return data;
-}
-
-// called by libstdc++.a
-char* __cxa_get_globals_fast()
-{
- // if libSystem.dylib not yet initialized, or is old libSystem, use shared global
- if ( (_ZN4dyld17gLibSystemHelpersE == NULL) || (_ZN4dyld17gLibSystemHelpersE->version < 5) )
- return sPreMainCxaGlobals;
-
- return pthread_getspecific(sCxaKey);
-}
-
#if __ppc__
// the ppc version of _Znwm in libstdc++.a uses keymgr
// need to override that
void* _Znwm(size_t size) { return malloc(size); }
#endif
+#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
+#if __arm__
+struct _Unwind_FunctionContext
+{
+ // next function in stack of handlers
+ struct _Unwind_FunctionContext* prev;
-#else /* __i386__ || __x86_64 || __ppc__ */
-
-
-
-
-
-
-//
-// BEGIN copy of code from libgcc.a source file unwind-dw2-fde-darwin.c
-//
-#define KEYMGR_API_MAJOR_GCC3 3
-/* ... with these keys. */
-#define KEYMGR_GCC3_LIVE_IMAGE_LIST 301 /* loaded images */
-#define KEYMGR_GCC3_DW2_OBJ_LIST 302 /* Dwarf2 object list */
-#define KEYMGR_EH_GLOBALS_KEY 13
-
-/* Node of KEYMGR_GCC3_LIVE_IMAGE_LIST. Info about each resident image. */
-struct live_images {
- unsigned long this_size; /* sizeof (live_images) */
- struct mach_header *mh; /* the image info */
- unsigned long vm_slide;
- void (*destructor)(struct live_images *); /* destructor for this */
- struct live_images *next;
- unsigned int examined_p;
- void *fde;
- void *object_info;
- unsigned long info[2]; /* Future use. */
};
-//
-// END copy of code from libgcc.a source file unwind-dw2-fde-darwin.c
-//
-
-//
-// dyld is built as stand alone executable with a static copy of libc, libstdc++, and libgcc.
//
-// In order for C++ exceptions to work within dyld, the C++ exception handling code
-// must be able to find the exception handling frame data inside dyld. The standard
-// exception handling code uses crt and keymgr to keep track of all images and calls
-// getsectdatafromheader("__eh_frame") to find the EH data for each image. We implement
-// our own copy of those functions below to enable exceptions within dyld.
+// The ARM of dyld use SL-LJ based exception handling
+// which does not require any initialization until libSystem is initialized.
//
-// Note: This exception handling is completely separate from any user code exception .
-// handling which has its own keymgr (in libSystem).
-//
-
-
-static struct live_images sDyldImage; // zero filled
-static void* sObjectList = NULL;
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4))
-static void* sEHGlobals = NULL;
-#endif
-
-
-// called by dyldStartup.s very early
void dyld_exceptions_init(struct mach_header *mh, uintptr_t slide)
{
- sDyldImage.this_size = sizeof(struct live_images);
- sDyldImage.mh = mh;
- sDyldImage.vm_slide = slide;
}
+static pthread_key_t sThreadChainKey = 0;
+static struct _Unwind_FunctionContext* sStaticThreadChain = NULL;
-
-// Hack for gcc 3.5's use of keymgr includes accessing __keymgr_global
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4))
-typedef struct Sinfo_Node {
- uint32_t size; /* Size of this node. */
- uint16_t major_version; /* API major version. */
- uint16_t minor_version; /* API minor version. */
-} Tinfo_Node;
-static const Tinfo_Node keymgr_info = { sizeof (Tinfo_Node), KEYMGR_API_MAJOR_GCC3, 0 };
-const Tinfo_Node* __keymgr_global[3] = { NULL, NULL, &keymgr_info };
-#endif
-
-static __attribute__((noreturn))
-void dyld_abort()
-{
- //dyld::log("internal dyld error\n");
- _exit(1);
-}
-
-void* _keymgr_get_and_lock_processwide_ptr(unsigned int key)
+//
+// When libSystem's initializers are run, they call back into dyld's
+// registerThreadHelpers which creates a pthread key and calls
+// __Unwind_SjLj_SetThreadKey().
+//
+void __Unwind_SjLj_SetThreadKey(pthread_key_t key)
{
- // The C++ exception handling code uses two keys. No other keys should be seen
- if ( key == KEYMGR_GCC3_LIVE_IMAGE_LIST ) {
- return &sDyldImage;
- }
- else if ( key == KEYMGR_GCC3_DW2_OBJ_LIST ) {
- return sObjectList;
- }
- dyld_abort();
+ sThreadChainKey = key;
+ //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetThreadKey(key=%d), sStaticThreadChain=%p\n", key, sStaticThreadChain);
+ // switch static chain to be per thread
+ _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sThreadChainKey, sStaticThreadChain);
+ sStaticThreadChain = NULL;
+ //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetThreadKey(key=%d), result=%d, new top=%p\n", key, result, pthread_getspecific(sThreadChainKey));
}
-void _keymgr_set_and_unlock_processwide_ptr(unsigned int key, void* value)
-{
- // The C++ exception handling code uses just this key. No other keys should be seen
- if ( key == KEYMGR_GCC3_DW2_OBJ_LIST ) {
- sObjectList = value;
- return;
- }
- dyld_abort();
-}
-void _keymgr_unlock_processwide_ptr(unsigned int key)
-{
- // The C++ exception handling code uses just this key. No other keys should be seen
- if ( key == KEYMGR_GCC3_LIVE_IMAGE_LIST ) {
- return;
- }
- dyld_abort();
-}
-
-void* _keymgr_get_per_thread_data(unsigned int key)
-{
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4))
- // gcc 3.5 and later use this key
- if ( key == KEYMGR_EH_GLOBALS_KEY )
- return sEHGlobals;
-#endif
+//static void printChain()
+//{
+// _ZN4dyld3logEPKcz("chain: ");
+// struct _Unwind_FunctionContext* start = sStaticThreadChain;
+// if ( sThreadChainKey != 0 ) {
+// start = (struct _Unwind_FunctionContext*)pthread_getspecific(sThreadChainKey);
+// }
+// for (struct _Unwind_FunctionContext* p = start; p != NULL; p = p->prev) {
+// _ZN4dyld3logEPKcz("%p -> ", p);
+// }
+// _ZN4dyld3logEPKcz("\n");
+//}
- // used by std::termination which dyld does not use
- dyld_abort();
-}
-void _keymgr_set_per_thread_data(unsigned int key, void *keydata)
+struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack()
{
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 5)) || (__GNUC__ >= 4))
- // gcc 3.5 and later use this key
- if ( key == KEYMGR_EH_GLOBALS_KEY ) {
- sEHGlobals = keydata;
- return;
+ //_ZN4dyld3logEPKcz("__Unwind_SjLj_GetTopOfFunctionStack(), key=%d, ", sThreadChainKey);
+ //printChain();
+ if ( sThreadChainKey != 0 ) {
+ return (struct _Unwind_FunctionContext*)pthread_getspecific(sThreadChainKey);
+ }
+ else {
+ return sStaticThreadChain;
}
-#endif
- // used by std::termination which dyld does not use
- dyld_abort();
}
-
-// needed by C++ exception handling code to find __eh_frame section
-const void* getsectdatafromheader(struct mach_header* mh, const char* segname, const char* sectname, unsigned long* size)
+void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc)
{
- const struct load_command* cmd;
- unsigned long i;
-
- cmd = (struct load_command*) ((char *)mh + sizeof(struct macho_header));
- for(i = 0; i < mh->ncmds; i++) {
- if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
- const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
- if ( strcmp(seg->segname, segname) == 0 ) {
- const struct macho_section* sect = (struct macho_section*)( (char*)seg + sizeof(struct macho_segment_command) );
- unsigned long j;
- for (j = 0; j < seg->nsects; j++) {
- if ( strcmp(sect[j].sectname, sectname) == 0 ) {
- *size = sect[j].size;
- return (void*)(sect[j].addr);
- }
- }
- }
- }
- cmd = (struct load_command*)( (char*)cmd + cmd->cmdsize );
- }
- return NULL;
+ //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetTopOfFunctionStack(%p), key=%d, prev=%p\n",
+ // fc, sThreadChainKey, (fc != NULL) ? fc->prev : NULL);
+ if ( sThreadChainKey != 0 )
+ _ZN4dyld17gLibSystemHelpersE->pthread_setspecific(sThreadChainKey, fc);
+ else
+ sStaticThreadChain = fc;
+ //_ZN4dyld3logEPKcz("__Unwind_SjLj_SetTopOfFunctionStack(%p), key=%d, ", fc, sThreadChainKey);
+ //printChain();
}
-
-// Hack for transition of rdar://problem/3933738
-// Can be removed later.
-// Allow C++ runtime to call getsectdatafromheader or getsectdatafromheader_64
-#if __LP64__
- #undef getsectdatafromheader
- const void* getsectdatafromheader(struct mach_header* mh, const char* segname, const char* sectname, unsigned long* size)
- {
- return getsectdatafromheader_64(mh, segname, sectname, size);
- }
-#endif
-
#endif
}
}
+
+//
+// The kernel may have slid a Position Independent Executable
+//
+static uintptr_t slideOfMainExecutable(const struct macho_header* mh)
+{
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
+ const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd;
+ if ( strcmp(segCmd->segname, "__TEXT") == 0 ) {
+ return (uintptr_t)mh - segCmd->vmaddr;
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ return 0;
+}
+
+
//
// If the kernel does not load dyld at its preferred address, we need to apply
// fixups to various initialized parts of the __DATA segment
}
-//
-// For some reason the kernel loads dyld with __TEXT and __LINKEDIT writable
-// rdar://problem/3702311
-//
-static void segmentProtectDyld(const struct macho_header* mh, intptr_t slide)
-{
- const uint32_t cmd_count = mh->ncmds;
- const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header));
- const struct load_command* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- switch (cmd->cmd) {
- case LC_SEGMENT_COMMAND:
- {
- const struct macho_segment_command* seg = (struct macho_segment_command*)cmd;
- vm_address_t addr = seg->vmaddr + slide;
- vm_size_t size = seg->vmsize;
- const bool setCurrentPermissions = false;
- vm_protect(mach_task_self(), addr, size, setCurrentPermissions, seg->initprot);
- //dyld::log("dyld: segment %s, 0x%08X -> 0x%08X, set to %d\n", seg->segname, addr, addr+size-1, seg->initprot);
- }
- break;
- }
- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
- }
-
-}
-
-
-//
-// re-map the main executable to a new random address
-//
-static const struct macho_header* randomizeExecutableLoadAddress(const struct macho_header* orgMH, const char* envp[], uintptr_t* appsSlide)
-{
-#if __ppc__
- // don't slide PIE programs running under rosetta
- if ( dyld::isRosetta() )
- return orgMH;
-#endif
- // environment variable DYLD_NO_PIE can disable PIE
- for(const char** p = envp; *p != NULL; p++) {
- if ( strncmp(*p, "DYLD_NO_PIE=", 12) == 0 )
- return orgMH;
- }
-
- // count segments
- uint32_t segCount = 0;
- const uint32_t cmd_count = orgMH->ncmds;
- const struct load_command* const cmds = (struct load_command*)(((char*)orgMH)+sizeof(macho_header));
- const struct load_command* cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
- const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd;
- // page-zero and custom stacks don't move
- if ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) )
- ++segCount;
- }
- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
- }
-
- // make copy of segment info
- macho_segment_command segs[segCount];
- uint32_t index = 0;
- uintptr_t highestAddressUsed = 0;
- uintptr_t lowestAddressUsed = UINTPTR_MAX;
- cmd = cmds;
- for (uint32_t i = 0; i < cmd_count; ++i) {
- if ( cmd->cmd == LC_SEGMENT_COMMAND ) {
- const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd;
- if ( (strcmp(segCmd->segname, "__PAGEZERO") != 0) && (strcmp(segCmd->segname, "__UNIXSTACK") != 0) ) {
- segs[index++] = *segCmd;
- if ( (segCmd->vmaddr + segCmd->vmsize) > highestAddressUsed )
- highestAddressUsed = ((segCmd->vmaddr + segCmd->vmsize) + 4095) & -4096;
- if ( segCmd->vmaddr < lowestAddressUsed )
- lowestAddressUsed = segCmd->vmaddr;
- // do nothing if kernel has already randomized load address
- if ( (strcmp(segCmd->segname, "__TEXT") == 0) && (segCmd->vmaddr != (uintptr_t)orgMH) )
- return orgMH;
- }
- }
- cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
- }
-
- // choose a random new base address
-#if __LP64__
- uintptr_t highestAddressPossible = highestAddressUsed + 0x100000000ULL;
-#elif __arm__
- uintptr_t highestAddressPossible = 0x2fe00000;
-#else
- uintptr_t highestAddressPossible = 0x80000000;
-#endif
- uintptr_t sizeNeeded = highestAddressUsed-lowestAddressUsed;
- if ( (highestAddressPossible-sizeNeeded) < highestAddressUsed ) {
- // new and old segments will overlap
- // need better algorithm for remapping
- // punt and don't re-map
- return orgMH;
- }
- uintptr_t possibleRange = (highestAddressPossible-sizeNeeded) - highestAddressUsed;
- uintptr_t newBaseAddress = highestAddressUsed + ((arc4random() % possibleRange) & -4096);
-
- vm_address_t addr = newBaseAddress;
- // reserve new address range
- if ( vm_allocate(mach_task_self(), &addr, sizeNeeded, VM_FLAGS_FIXED) == KERN_SUCCESS ) {
- // copy each segment to new address
- for (uint32_t i = 0; i < segCount; ++i) {
- uintptr_t newSegAddress = segs[i].vmaddr - lowestAddressUsed + newBaseAddress;
- if ( (vm_copy(mach_task_self(), segs[i].vmaddr, segs[i].vmsize, newSegAddress) != KERN_SUCCESS)
- #if !__arm__ // work around for <rdar://problem/5736393>
- || (vm_protect(mach_task_self(), newSegAddress, segs[i].vmsize, true, segs[i].maxprot) != KERN_SUCCESS)
- #endif
- || (vm_protect(mach_task_self(), newSegAddress, segs[i].vmsize, false, segs[i].initprot) != KERN_SUCCESS) ) {
- // can't copy so dealloc new region and run with original base address
- vm_deallocate(mach_task_self(), newBaseAddress, sizeNeeded);
- dyld::warn("could not relocate position independent executable\n");
- return orgMH;
- }
- }
- // unmap original segments
- vm_deallocate(mach_task_self(), lowestAddressUsed, highestAddressUsed-lowestAddressUsed);
-
- // run with newly mapped executable
- *appsSlide = newBaseAddress - lowestAddressUsed;
- return (const struct macho_header*)newBaseAddress;
- }
-
- // can't get new range, so don't slide to random address
- return orgMH;
-}
-
-
extern "C" void dyld_exceptions_init(const struct macho_header*, uintptr_t slide); // in dyldExceptions.cpp
extern "C" void mach_init();
// This is code to bootstrap dyld. This work in normally done for a program by dyld and crt.
// In dyld we have to do this manually.
//
-uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[], intptr_t slide)
+uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[],
+ intptr_t slide, const struct macho_header* dyldsMachHeader)
{
- // _mh_dylinker_header is magic symbol defined by static linker (ld), see <mach-o/ldsyms.h>
- const struct macho_header* dyldsMachHeader = (const struct macho_header*)(((char*)&_mh_dylinker_header)+slide);
-
// if kernel had to slide dyld, we need to fix up load sensitive locations
// we have to do this before using any global variables
if ( slide != 0 ) {
rebaseDyld(dyldsMachHeader, slide);
}
-
- uintptr_t appsSlide = 0;
-
+
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ // set pthread keys to dyld range
+ __pthread_tsd_first = 1;
+ _pthread_keys_init();
+#endif
+
// enable C++ exceptions to work inside dyld
dyld_exceptions_init(dyldsMachHeader, slide);
// allow dyld to use mach messaging
mach_init();
- // set protection on segments (has to be done after mach_init)
- segmentProtectDyld(dyldsMachHeader, slide);
-
// kernel sets up env pointer to be just past end of agv array
const char** envp = &argv[argc+1];
// run all C++ initializers inside dyld
runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple);
-
- // if main executable was linked -pie, then randomize its load address
- if ( appsMachHeader->flags & MH_PIE )
- appsMachHeader = randomizeExecutableLoadAddress(appsMachHeader, envp, &appsSlide);
-
+
// now that we are done bootstrapping dyld, call dyld's main
+ uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader);
return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple);
}
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* @APPLE_LICENSE_HEADER_END@
*/
-#include <stdlib.h>
+#include <Availability.h>
//
-// <rdar://problem/6536810> Alter libdyld.a to not need libsystem to link with dylib1.o
// This is the temporary private interface between libSystem.B.dylib and dyld
//
-#if __i386__ || __x86_64__
-// The compiler driver will continue to add -ldylib1.o to ppc and arm links
-// so only i386 and x86_64 need this extra glue.
-
+//
+// Long ago, the compiler driver added -ldylib1.o to every dylib which caused a
+// __DATA,__dyld section to be added every dylib. The new LINKEDIT format no longer requires
+// images to have a __DATA,__dyld section. But until libdyld.dylib and dyld update
+// to some sort of vtable based interface, libdyld still needs a __DATA,__dyld section.
+// The code below adds that section.
+//
struct __DATA__dyld { long lazy; int (*lookup)(const char*, void**); };
-static struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, NULL };
+static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) = { 0, 0 };
-
-__attribute__((weak, visibility("hidden"))) int _dyld_func_lookup(const char* dyld_func_name, void **address)
+#if __arm__ && __MAC_OS_X_VERSION_MIN_REQUIRED
+// <rdar://problem/8755380>
+// For historical reasons, gcc and llvm-gcc added -ldylib1.o to the link line of armv6
+// dylibs when targeting MacOSX (but not iOS). clang cleans up that mistake, but doing
+// so would break the libdyld build. Making _dyld_func_lookup weak,hidden means if
+// dylib1.o is used, it overrides this, otherwise this implementation is used.
+__attribute__((weak))
+#endif
+__attribute__((visibility("hidden")))
+int _dyld_func_lookup(const char* dyld_func_name, void **address)
{
return (*myDyldSection.lookup)(dyld_func_name, address);
}
-#endif
void (*acquireDyldInitializerLock)();
void (*releaseDyldInitializerLock)();
// added in version 5
- int (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*));
- int (*pthread_setspecific)(pthread_key_t, const void*);
+ int (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*));
+ int (*pthread_setspecific)(pthread_key_t, const void*);
// added in version 6
size_t (*malloc_size)(const void *ptr);
+ // added in version 7
+ void* (*pthread_getspecific)(pthread_key_t);
+ // added in version 8
+ void (*cxa_finalize)(const void*);
};
#if __cplusplus
};
.data
__dyld_start_static_picbase:
.long L__dyld_start_picbase
-
+Lmh: .long ___dso_handle
.text
.align 2
movl %esp,%ebp # pointer to base of kernel frame
andl $-16,%esp # force SSE alignment
- # call dyldbootstrap::start(app_mh, argc, argv, slide)
+ # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh)
+ subl $12,%esp
call L__dyld_start_picbase
L__dyld_start_picbase:
popl %ebx # set %ebx to runtime value of picbase
- movl __dyld_start_static_picbase-L__dyld_start_picbase(%ebx), %eax
- subl %eax, %ebx # slide = L__dyld_start_picbase - [__dyld_start_static_picbase]
+ movl Lmh-L__dyld_start_picbase(%ebx), %ecx # ecx = prefered load address
+ movl __dyld_start_static_picbase-L__dyld_start_picbase(%ebx), %eax
+ subl %eax, %ebx # ebx = slide = L__dyld_start_picbase - [__dyld_start_static_picbase]
+ addl %ebx, %ecx # ecx = actual load address
+ pushl %ecx # param5 = actual load address
pushl %ebx # param4 = slide
lea 12(%ebp),%ebx
pushl %ebx # param3 = argv
pushl %ebx # param2 = argc
movl 4(%ebp),%ebx
pushl %ebx # param1 = mh
- call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl
+ call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_
# clean up stack and jump to result
movl %ebp,%esp # restore the unaligned stack pointer
movq %rsp,%rbp # pointer to base of kernel frame
andq $-16,%rsp # force SSE alignment
- # call dyldbootstrap::start(app_mh, argc, argv, slide)
+ # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh)
movq 8(%rbp),%rdi # param1 = mh into %rdi
movl 16(%rbp),%esi # param2 = argc into %esi
leaq 24(%rbp),%rdx # param3 = &argv[0] into %rdx
movq __dyld_start_static(%rip), %r8
leaq __dyld_start(%rip), %rcx
subq %r8, %rcx # param4 = slide into %rcx
- call __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl
+ leaq ___dso_handle(%rip),%r8 # param5 = dyldsMachHeader
+ call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_
# clean up stack and jump to result
movq %rbp,%rsp # restore the unaligned stack pointer
.data
.align 2
__dyld_start_static_picbase:
- .g_long L__dyld_start_picbase
+ .g_long L__dyld_start_picbase
+Lmh: .g_long ___dso_handle
#if __ppc__
.set L_mh_offset,0
stg r0,0(r1) ; terminate initial stack frame
stgu r1,-SF_MINSIZE(r1); allocate minimal stack frame
- ; call dyldbootstrap::start(app_mh, argc, argv, slide)
+ # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh)
lg r3,L_mh_offset(r26) ; r3 = mach_header
lwz r4,L_argc_offset(r26) ; r4 = argc (int == 4 bytes)
addi r5,r26,L_argv_offset ; r5 = argv
addis r6,r31,ha16(__dyld_start_static_picbase-L__dyld_start_picbase)
lg r6,lo16(__dyld_start_static_picbase-L__dyld_start_picbase)(r6)
subf r6,r6,r31 ; r6 = slide
- bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl
+ addis r7,r31,ha16(Lmh-L__dyld_start_picbase)
+ lg r7,lo16(Lmh-L__dyld_start_picbase)(r7)
+ add r7,r6,r7 ; r7 = dyld_mh
+ bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_
; clean up stack and jump to result
mtctr r3 ; Put entry point in count register
.space 16
+ // Hack to make ___dso_handle work
+ // Without this local symbol, assembler will error out about in subtraction expression
+ // The real ___dso_handle (non-weak) sythesized by the linker
+ // Since this one is weak, the linker will throw this one away and use the real one instead.
+ .data
+ .globl ___dso_handle
+ .weak_definition ___dso_handle
+___dso_handle: .long 0
+
.text
.align 2
__dyld_start:
- // call dyldbootstrap::start(app_mh, argc, argv, slide)
+ mov r8, sp // save stack pointer
+ sub sp, #8 // make room for outgoing dyld_mh parameter
+ bic sp, sp, #7 // force 8-byte alignment
+
+ // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh)
ldr r3, L__dyld_start_picbase_ptr
L__dyld_start_picbase:
ldr r3, [r0, r3] // load expected PC
sub r3, r0, r3 // r3 = slide
- ldr r0, [sp] // r0 = mach_header
- ldr r1, [sp, #4] // r1 = argc
- add r2, sp, #8 // r2 = argv
+ ldr r0, [r8] // r0 = mach_header
+ ldr r1, [r8, #4] // r1 = argc
+ add r2, r8, #8 // r2 = argv
- mov r8, sp // save stack pointer
- bic sp, sp, #7 // force 8-byte alignment
+ ldr r4, Lmh
+L3: add r4, r4, pc // r4 = dyld_mh
+ str r4, [sp, #0]
- bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKcl
+ bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_
// clean up stack and jump to result
add sp, r8, #4 // remove the mach_header argument.
bx r0 // jump to the program's entry point
+
.align 2
L__dyld_start_picbase_ptr:
.long __dyld_start_static_picbase-L__dyld_start_picbase
-
+Lmh: .long ___dso_handle-L3-8
.text
.align 2
#error unknown architecture
#endif
-
-
+#if __arm__
+ // work around for: <rdar://problem/6530727> gdb-1109: notifier in dyld does not work if it is in thumb
+ .text
+ .align 2
+ .globl _gdb_image_notifier
+ .private_extern _gdb_image_notifier
+_gdb_image_notifier:
+ bx lr
+#endif
+
+
#include "mach-o/dyld_debug.h"
#include "mach-o/dyld_gdb.h"
+#include "mach-o/dyld_priv.h"
// global state set up by _dyld_debug_subscribe_to_events() and accessed by _dyld_debug_module_name()
static const struct dyld_image_info* sImages = NULL;
#include "mach-o/dyld_images.h"
#include "ImageLoader.h"
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ #define INITIAL_UUID_IMAGE_COUNT 4
+#else
+ #define INITIAL_UUID_IMAGE_COUNT 32
+#endif
static std::vector<dyld_image_info> sImageInfos;
+static std::vector<dyld_uuid_info> sImageUUIDs;
void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[])
{
- // make initial size large enought that we probably won't need to re-alloc it
+ // make initial size large enough that we probably won't need to re-alloc it
if ( sImageInfos.size() == 0 )
sImageInfos.reserve(INITIAL_IMAGE_COUNT);
-
+ if ( sImageUUIDs.capacity() == 0 )
+ sImageUUIDs.reserve(4);
// set infoArray to NULL to denote it is in-use
dyld_all_image_infos.infoArray = NULL;
sImageInfos.push_back(info[i]);
dyld_all_image_infos.infoArrayCount = sImageInfos.size();
- // set infoArray back to base address of vector
+ // set infoArray back to base address of vector (other process can now read)
dyld_all_image_infos.infoArray = &sImageInfos[0];
+}
+
+const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[])
+{
// tell gdb that about the new images
dyld_all_image_infos.notification(dyld_image_adding, infoCount, info);
+ // <rdar://problem/7739489> record initial count of images
+ // so CrashReporter can note which images were dynamically loaded
+ if ( dyld_all_image_infos.initialImageCount == 0 )
+ dyld_all_image_infos.initialImageCount = infoCount;
+ return NULL;
+}
+
+
+
+void addNonSharedCacheImageUUID(const dyld_uuid_info& info)
+{
+ // set uuidArray to NULL to denote it is in-use
+ dyld_all_image_infos.uuidArray = NULL;
+
+ // append all new images
+ sImageUUIDs.push_back(info);
+ dyld_all_image_infos.uuidArrayCount = sImageUUIDs.size();
+
+ // set uuidArray back to base address of vector (other process can now read)
+ dyld_all_image_infos.uuidArray = &sImageUUIDs[0];
}
void removeImageFromAllImages(const struct mach_header* loadAddress)
// set infoArray back to base address of vector
dyld_all_image_infos.infoArray = &sImageInfos[0];
+
+ // set uuidArrayCount to NULL to denote it is in-use
+ dyld_all_image_infos.uuidArray = NULL;
+
+ // remove image from infoArray
+ for (std::vector<dyld_uuid_info>::iterator it=sImageUUIDs.begin(); it != sImageUUIDs.end(); it++) {
+ if ( it->imageLoadAddress == loadAddress ) {
+ sImageUUIDs.erase(it);
+ break;
+ }
+ }
+ dyld_all_image_infos.uuidArrayCount = sImageUUIDs.size();
+
+ // set infoArray back to base address of vector
+ dyld_all_image_infos.uuidArray = &sImageUUIDs[0];
+
// tell gdb that about the new images
dyld_all_image_infos.notification(dyld_image_removing, 1, &goingAway);
}
-
+#if __arm__
+// work around for: <rdar://problem/6530727> gdb-1109: notifier in dyld does not work if it is in thumb
+extern "C" void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]);
+#else
static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[])
{
// do nothing
//for (uint32_t i=0; i < dyld_all_image_infos.infoArrayCount; ++i)
// dyld::log("dyld: %d loading at %p %s\n", i, dyld_all_image_infos.infoArray[i].imageLoadAddress, dyld_all_image_infos.infoArray[i].imageFilePath);
}
+#endif
void setAlImageInfosHalt(const char* message, uintptr_t flags)
{
#define XSTR(s) STR(s)
struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info")))
- = { 7, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
- XSTR(DYLD_VERSION) , NULL, 0, 0 };
+ = {
+ 12, 0, NULL, &gdb_image_notifier, false, false, (const mach_header*)&__dso_handle, NULL,
+ XSTR(DYLD_VERSION), NULL, 0, NULL, 0, 0, NULL, &dyld_all_image_infos,
+ 0, dyld_error_kind_none, NULL, NULL, NULL, 0
+ };
struct dyld_shared_cache_ranges dyld_shared_cache_ranges;
movq %r8,R8_SAVE(%rsp)
movq %r9,R9_SAVE(%rsp)
movq %rax,RAX_SAVE(%rsp)
+misaligned_stack_error_entering_dyld_stub_binder:
movdqa %xmm0,XMMM0_SAVE(%rsp)
movdqa %xmm1,XMMM1_SAVE(%rsp)
movdqa %xmm2,XMMM2_SAVE(%rsp)
movdqa %xmm5,XMMM5_SAVE(%rsp)
movdqa %xmm6,XMMM6_SAVE(%rsp)
movdqa %xmm7,XMMM7_SAVE(%rsp)
+dyld_stub_binder_:
movq MH_PARAM_BP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo)
movq LP_PARAM_BP(%rbp),%rsi
call __Z21_dyld_fast_stub_entryPvl
#endif
+#if __arm__
+ /*
+ * sp+4 lazy binding info offset
+ * sp+0 address of ImageLoader cache
+ */
+
+ .text
+ .align 2
+ .globl dyld_stub_binder
+dyld_stub_binder:
+ stmfd sp!, {r0,r1,r2,r3,r7,lr} // save registers
+ add r7, sp, #16 // point FP to previous FP
+
+ ldr r0, [sp, #24] // move address ImageLoader cache to 1st parameter
+ ldr r1, [sp, #28] // move lazy info offset 2nd parameter
+
+ // call dyld::fastBindLazySymbol(loadercache, lazyinfo)
+ bl __Z21_dyld_fast_stub_entryPvl
+ mov ip, r0 // move the symbol`s address into ip
+
+ ldmfd sp!, {r0,r1,r2,r3,r7,lr} // restore registers
+ add sp, sp, #8 // remove meta-parameters
+
+ bx ip // jump to the symbol`s address that was bound
+
+#endif /* __arm__ */
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
- * Copyright (c) 2004-2009 Apple Inc. All rights reserved.
+ * Copyright (c) 2004-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <unistd.h>
#include <stdarg.h>
#include <stdio.h>
+#include <mach/mach_error.h>
// from _simple.h in libc
typedef struct _SIMPLE* _SIMPLE_STRING;
return (struct tm*)NULL;
}
+// malloc calls exit(-1) in case of errors...
+void exit(int x)
+{
+ _ZN4dyld4haltEPKc("exit()");
+}
+
+// static initializers make calls to __cxa_atexit
+void __cxa_atexit()
+{
+ // do nothing, dyld never terminates
+}
//
// The stack protector routines in lib.c bring in too much stuff, so
// make our own custom ones.
//
long __stack_chk_guard = 0;
-static __attribute__((constructor)) void __guard_setup(void)
+static __attribute__((constructor))
+void __guard_setup(int argc, const char* argv[], const char* envp[], const char* apple[])
{
+ for (const char** p = apple; *p != NULL; ++p) {
+ if ( strncmp(*p, "stack_guard=", 12) == 0 ) {
+ // kernel has provide a random value for us
+ for (const char* s = *p + 12; *s != '\0'; ++s) {
+ char c = *s;
+ long value = 0;
+ if ( (c >= 'a') && (c <= 'f') )
+ value = c - 'a' + 10;
+ else if ( (c >= 'A') && (c <= 'F') )
+ value = c - 'A' + 10;
+ else if ( (c >= '0') && (c <= '9') )
+ value = c - '0';
+ __stack_chk_guard <<= 4;
+ __stack_chk_guard |= value;
+ }
+ if ( __stack_chk_guard != 0 )
+ return;
+ }
+ }
+
#if __LP64__
__stack_chk_guard = ((long)arc4random() << 32) | arc4random();
#else
}
+// std::_throw_bad_alloc()
+void _ZSt17__throw_bad_allocv()
+{
+ _ZN4dyld4haltEPKc("__throw_bad_alloc()");
+}
+
+// std::_throw_length_error(const char* x)
+void _ZSt20__throw_length_errorPKc()
+{
+ _ZN4dyld4haltEPKc("_throw_length_error()");
+}
+
+// the libc.a version of this drags in ASL
+void __chk_fail()
+{
+ _ZN4dyld4haltEPKc("__chk_fail()");
+}
+
+
+// referenced by libc.a(pthread.o) but unneeded in dyld
+void _init_cpu_capabilities() { }
+void _cpu_capabilities() {}
+void set_malloc_singlethreaded() {}
+int PR_5243343_flag = 0;
+
+
+// used by some pthread routines
+char* mach_error_string(mach_error_t err)
+{
+ return (char *)"unknown error code";
+}
+char* mach_error_type(mach_error_t err)
+{
+ return (char *)"(unknown/unknown)";
+}
+
+// _pthread_reap_thread calls fprintf(stderr).
+// We map fprint to _simple_vdprintf and ignore FILE* stream, so ok for it to be NULL
+#if !__ppc__
+FILE* __stderrp = NULL;
+FILE* __stdoutp = NULL;
+#endif
+
+// work with c++abi.a
+void (*__cxa_terminate_handler)() = _ZSt9terminatev;
+void (*__cxa_unexpected_handler)() = _ZSt10unexpectedv;
+
+void abort_message(const char* format, ...)
+{
+ va_list list;
+ va_start(list, format);
+ _simple_vdprintf(STDERR_FILENO, format, list);
+ va_end(list);
+}
+
+void __cxa_bad_typeid()
+{
+ _ZN4dyld4haltEPKc("__cxa_bad_typeid()");
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#if __x86_64__
+ // returns address of TLV in %rax, all other registers preserved
+ .globl _tlv_get_addr
+ .private_extern _tlv_get_addr
+_tlv_get_addr:
+ movq 8(%rdi),%rax // get key from descriptor
+ movq %gs:0x0(,%rax,8),%rax // get thread value
+ testq %rax,%rax // if NULL, lazily allocate
+ je LlazyAllocate
+ addq 16(%rdi),%rax // add offset from descriptor
+ ret
+LlazyAllocate:
+ pushq %rbp
+ movq %rsp, %rbp
+ subq $592,%rsp
+ movq %rdi,-8(%rbp)
+ movq %rsi,-16(%rbp)
+ movq %rdx,-24(%rbp)
+ movq %rcx,-32(%rbp)
+ movq %r8,-40(%rbp)
+ movq %r9,-48(%rbp)
+ movq %r10,-56(%rbp)
+ movq %r11,-64(%rbp)
+ fxsave -592(%rbp)
+ movq 8(%rdi),%rdi // get key from descriptor
+ call _tlv_allocate_and_initialize_for_key
+ fxrstor -592(%rbp)
+ movq -64(%rbp),%r11
+ movq -56(%rbp),%r10
+ movq -48(%rbp),%r9
+ movq -40(%rbp),%r8
+ movq -32(%rbp),%rcx
+ movq -24(%rbp),%rdx
+ movq -16(%rbp),%rsi
+ movq -8(%rbp),%rdi
+ addq 16(%rdi),%rax // result = buffer + offset
+ addq $592,%rsp
+ popq %rbp
+ ret
+#endif
+
+
+
+#if __i386__
+ // returns address of TLV in %eax, all other registers (except %ecx) preserved
+ .globl _tlv_get_addr
+ .private_extern _tlv_get_addr
+_tlv_get_addr:
+ movl 4(%eax),%ecx // get key from descriptor
+ movl %gs:0x0(,%ecx,4),%ecx // get thread value
+ testl %ecx,%ecx // if NULL, lazily allocate
+ je LlazyAllocate
+ movl 8(%eax),%eax // add offset from descriptor
+ addl %ecx,%eax
+ ret
+LlazyAllocate:
+ pushl %ebp
+ movl %esp,%ebp
+ pushl %edx // save edx
+ subl $548,%esp
+ movl %eax,-8(%ebp) // save descriptor
+ lea -528(%ebp),%ecx // get 512 byte buffer in frame
+ and $-16, %ecx // 16-byte align buffer for fxsave
+ fxsave (%ecx)
+ movl 4(%eax),%ecx // get key from descriptor
+ movl %ecx,(%esp) // push key parameter, also leaves stack aligned properly
+ call _tlv_allocate_and_initialize_for_key
+ movl -8(%ebp),%ecx // get descriptor
+ movl 8(%ecx),%ecx // get offset from descriptor
+ addl %ecx,%eax // add offset to buffer
+ lea -528(%ebp),%ecx
+ and $-16, %ecx // 16-byte align buffer for fxrstor
+ fxrstor (%ecx)
+ addl $548,%esp
+ popl %edx // restore edx
+ popl %ebp
+ ret
+#endif
+
+
+#if 0
+#if __arm__
+ // returns address of TLV in r0, all other registers preserved
+ .globl _tlv_get_addr
+ .private_extern _tlv_get_addr
+_tlv_get_addr:
+ push {r1,r2,r3,r7,lr}
+ mov r7,r0 // save descriptor in r7
+ ldr r0, [r7, #4] // get key from descriptor
+ bl _pthread_getspecific // get thread value
+ cmp r0, #0
+ bne L2 // if NULL, lazily allocate
+ ldr r0, [r7, #4] // get key from descriptor
+ bl _tlv_allocate_and_initialize_for_key
+L2: ldr r1, [r7, #8] // get offset from descriptor
+ add r0, r1, r0 // add offset into allocation block
+ pop {r1,r2,r3,r7,pc}
+#endif
+#endif
+
+
--- /dev/null
+/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
+ *
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <Block.h>
+#include <malloc/malloc.h>
+#include <mach-o/loader.h>
+#include <libkern/OSAtomic.h>
+
+#include "dyld_priv.h"
+
+
+#if __LP64__
+ typedef struct mach_header_64 macho_header;
+ #define LC_SEGMENT_COMMAND LC_SEGMENT_64
+ typedef struct segment_command_64 macho_segment_command;
+ typedef struct section_64 macho_section;
+#else
+ typedef struct mach_header macho_header;
+ #define LC_SEGMENT_COMMAND LC_SEGMENT
+ typedef struct segment_command macho_segment_command;
+ typedef struct section macho_section;
+#endif
+
+#ifndef S_THREAD_LOCAL_REGULAR
+#define S_THREAD_LOCAL_REGULAR 0x11
+#endif
+
+#ifndef S_THREAD_LOCAL_ZEROFILL
+#define S_THREAD_LOCAL_ZEROFILL 0x12
+#endif
+
+#ifndef S_THREAD_LOCAL_VARIABLES
+#define S_THREAD_LOCAL_VARIABLES 0x13
+#endif
+
+#ifndef S_THREAD_LOCAL_VARIABLE_POINTERS
+#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14
+#endif
+
+#ifndef S_THREAD_LOCAL_INIT_FUNCTION_POINTERS
+#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15
+#endif
+
+#ifndef MH_HAS_TLV_DESCRIPTORS
+ #define MH_HAS_TLV_DESCRIPTORS 0x800000
+#endif
+
+#if __i386__ || __x86_64__
+
+typedef struct TLVHandler {
+ struct TLVHandler *next;
+ dyld_tlv_state_change_handler handler;
+ enum dyld_tlv_states state;
+} TLVHandler;
+
+// lock-free prepend-only linked list
+static TLVHandler * volatile tlv_handlers = NULL;
+
+
+struct TLVDescriptor
+{
+ void* (*thunk)(struct TLVDescriptor*);
+ unsigned long key;
+ unsigned long offset;
+};
+typedef struct TLVDescriptor TLVDescriptor;
+
+
+// implemented in assembly
+extern void* tlv_get_addr(TLVDescriptor*);
+
+struct TLVImageInfo
+{
+ pthread_key_t key;
+ const struct mach_header* mh;
+};
+typedef struct TLVImageInfo TLVImageInfo;
+
+static TLVImageInfo* tlv_live_images = NULL;
+static unsigned int tlv_live_image_alloc_count = 0;
+static unsigned int tlv_live_image_used_count = 0;
+static pthread_mutex_t tlv_live_image_lock = PTHREAD_MUTEX_INITIALIZER;
+
+static void tlv_set_key_for_image(const struct mach_header* mh, pthread_key_t key)
+{
+ pthread_mutex_lock(&tlv_live_image_lock);
+ if ( tlv_live_image_used_count == tlv_live_image_alloc_count ) {
+ unsigned int newCount = (tlv_live_images == NULL) ? 8 : 2*tlv_live_image_alloc_count;
+ struct TLVImageInfo* newBuffer = malloc(sizeof(TLVImageInfo)*newCount);
+ if ( tlv_live_images != NULL ) {
+ memcpy(newBuffer, tlv_live_images, sizeof(TLVImageInfo)*tlv_live_image_used_count);
+ free(tlv_live_images);
+ }
+ tlv_live_images = newBuffer;
+ tlv_live_image_alloc_count = newCount;
+ }
+ tlv_live_images[tlv_live_image_used_count].key = key;
+ tlv_live_images[tlv_live_image_used_count].mh = mh;
+ ++tlv_live_image_used_count;
+ pthread_mutex_unlock(&tlv_live_image_lock);
+}
+
+static const struct mach_header* tlv_get_image_for_key(pthread_key_t key)
+{
+ const struct mach_header* result = NULL;
+ pthread_mutex_lock(&tlv_live_image_lock);
+ for(unsigned int i=0; i < tlv_live_image_used_count; ++i) {
+ if ( tlv_live_images[i].key == key ) {
+ result = tlv_live_images[i].mh;
+ break;
+ }
+ }
+ pthread_mutex_unlock(&tlv_live_image_lock);
+ return result;
+}
+
+
+static void
+tlv_notify(enum dyld_tlv_states state, void *buffer)
+{
+ if (!tlv_handlers) return;
+
+ // Always use malloc_size() to ensure allocated and deallocated states
+ // send the same size. tlv_free() doesn't have anything else recorded.
+ dyld_tlv_info info = { sizeof(info), buffer, malloc_size(buffer) };
+
+ for (TLVHandler *h = tlv_handlers; h != NULL; h = h->next) {
+ if (h->state == state && h->handler) {
+ h->handler(h->state, &info);
+ }
+ }
+}
+
+
+// called lazily when TLV is first accessed
+__attribute__((visibility("hidden")))
+void* tlv_allocate_and_initialize_for_key(pthread_key_t key)
+{
+ const struct mach_header* mh = tlv_get_image_for_key(key);
+ // first pass, find size and template
+ uint8_t* start = NULL;
+ unsigned long size;
+ intptr_t slide = 0;
+ bool slideComputed = false;
+ bool hasInitializers = false;
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND) {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ if ( !slideComputed && (seg->filesize != 0) ) {
+ slide = (uintptr_t)mh - seg->vmaddr;
+ slideComputed = true;
+ }
+ const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command));
+ const macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ switch ( sect->flags & SECTION_TYPE ) {
+ case S_THREAD_LOCAL_INIT_FUNCTION_POINTERS:
+ hasInitializers = true;
+ break;
+ case S_THREAD_LOCAL_ZEROFILL:
+ case S_THREAD_LOCAL_REGULAR:
+ if ( start == NULL ) {
+ // first of N contiguous TLV template sections, record as if this was only section
+ start = (uint8_t*)(sect->addr + slide);
+ size = sect->size;
+ }
+ else {
+ // non-first of N contiguous TLV template sections, accumlate values
+ const uint8_t* newEnd = (uint8_t*)(sect->addr + slide + sect->size);
+ size = newEnd - start;
+ }
+ break;
+ }
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+
+ // allocate buffer and fill with template
+ void* buffer = malloc(size);
+ memcpy(buffer, start, size);
+
+ // set this thread's value for key to be the new buffer.
+ pthread_setspecific(key, buffer);
+
+ // send tlv state notifications
+ tlv_notify(dyld_tlv_state_allocated, buffer);
+
+ // second pass, run initializers
+ if ( hasInitializers ) {
+ cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND) {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command));
+ const macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_INIT_FUNCTION_POINTERS ) {
+ typedef void (*InitFunc)(void);
+ InitFunc* funcs = (InitFunc*)(sect->addr + slide);
+ const uint32_t count = sect->size / sizeof(uintptr_t);
+ for (uint32_t i=count; i > 0; --i) {
+ InitFunc func = funcs[i-1];
+ func();
+ }
+ }
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+ }
+ return buffer;
+}
+
+
+// pthread destructor for TLV storage
+static void
+tlv_free(void *storage)
+{
+ tlv_notify(dyld_tlv_state_deallocated, storage);
+ free(storage);
+}
+
+
+// called when image is loaded
+static void tlv_initialize_descriptors(const struct mach_header* mh)
+{
+ pthread_key_t key = 0;
+ intptr_t slide = 0;
+ bool slideComputed = false;
+ const uint32_t cmd_count = mh->ncmds;
+ const struct load_command* const cmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header));
+ const struct load_command* cmd = cmds;
+ for (uint32_t i = 0; i < cmd_count; ++i) {
+ if ( cmd->cmd == LC_SEGMENT_COMMAND) {
+ const macho_segment_command* seg = (macho_segment_command*)cmd;
+ if ( !slideComputed && (seg->filesize != 0) ) {
+ slide = (uintptr_t)mh - seg->vmaddr;
+ slideComputed = true;
+ }
+ const macho_section* const sectionsStart = (macho_section*)((char*)seg + sizeof(macho_segment_command));
+ const macho_section* const sectionsEnd = §ionsStart[seg->nsects];
+ for (const macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) {
+ if ( (sect->flags & SECTION_TYPE) == S_THREAD_LOCAL_VARIABLES ) {
+ if ( sect->size != 0 ) {
+ // allocate pthread key when we first discover this image has TLVs
+ if ( key == 0 ) {
+ int result = pthread_key_create(&key, &tlv_free);
+ if ( result != 0 )
+ abort();
+ tlv_set_key_for_image(mh, key);
+ }
+ // initialize each descriptor
+ TLVDescriptor* start = (TLVDescriptor*)(sect->addr + slide);
+ TLVDescriptor* end = (TLVDescriptor*)(sect->addr + sect->size + slide);
+ for (TLVDescriptor* d=start; d < end; ++d) {
+ d->thunk = tlv_get_addr;
+ d->key = key;
+ //d->offset = d->offset; // offset unchanged
+ }
+ }
+ }
+ }
+ }
+ cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize);
+ }
+}
+
+// called by dyld when a image is loaded
+static const char* tlv_load_notification(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[])
+{
+ // this is called on all images, even those without TLVs, so we want
+ // this to be fast. The linker sets MH_HAS_TLV_DESCRIPTORS so we don't
+ // have to search images just to find the don't have TLVs.
+ for (uint32_t i=0; i < infoCount; ++i) {
+ if ( info[i].imageLoadAddress->flags & MH_HAS_TLV_DESCRIPTORS )
+ tlv_initialize_descriptors(info[i].imageLoadAddress);
+ }
+ return NULL;
+}
+
+
+void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler)
+{
+ TLVHandler *h = malloc(sizeof(TLVHandler));
+ h->state = state;
+ h->handler = Block_copy(handler);
+
+ TLVHandler *old;
+ do {
+ old = tlv_handlers;
+ h->next = old;
+ } while (! OSAtomicCompareAndSwapPtrBarrier(old, h, (void * volatile *)&tlv_handlers));
+}
+
+
+void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler)
+{
+ pthread_mutex_lock(&tlv_live_image_lock);
+ unsigned int count = tlv_live_image_used_count;
+ void *list[count];
+ for (unsigned int i = 0; i < count; ++i) {
+ list[i] = pthread_getspecific(tlv_live_images[i].key);
+ }
+ pthread_mutex_unlock(&tlv_live_image_lock);
+
+ for (unsigned int i = 0; i < count; ++i) {
+ if (list[i]) {
+ dyld_tlv_info info = { sizeof(info), list[i], malloc_size(list[i]) };
+ handler(dyld_tlv_state_allocated, &info);
+ }
+ }
+}
+
+
+//
+// thread_local terminators
+//
+// C++ 0x allows thread_local C++ objects which have constructors run
+// on the thread before any use of the object and the object's destructor
+// is run on the thread when the thread terminates.
+//
+// To support this libdyld gets a pthread key early in process start up and
+// uses tlv_finalize and the key's destructor function. This key must be
+// allocated before any thread local variables are instantiated because when
+// a thread is terminated, the pthread package runs the destructor function
+// on each key's storage values in key allocation order. Since we want
+// C++ objects to be destructred before they are deallocated, we need the
+// destructor key to come before the deallocation key.
+//
+
+typedef void (*TermFunc)(void*);
+struct TLVTerminatorListEntry
+{
+ TermFunc termFunc;
+ void* objAddr;
+};
+
+struct TLVTerminatorList
+{
+ uint32_t allocCount;
+ uint32_t useCount;
+ struct TLVTerminatorListEntry entries[1]; // variable length
+};
+
+
+static pthread_key_t tlv_terminators_key = 0;
+
+void _tlv_atexit(TermFunc func, void* objAddr)
+{
+ // NOTE: this does not need locks because it only operates on current thread data
+ struct TLVTerminatorList* list = (struct TLVTerminatorList*)pthread_getspecific(tlv_terminators_key);
+ if ( list == NULL ) {
+ // handle first allocation
+ list = (struct TLVTerminatorList*)malloc(offsetof(struct TLVTerminatorList, entries[1]));
+ list->allocCount = 1;
+ list->useCount = 1;
+ list->entries[0].termFunc = func;
+ list->entries[0].objAddr = objAddr;
+ pthread_setspecific(tlv_terminators_key, list);
+ }
+ else {
+ if ( list->allocCount == list->allocCount ) {
+ // handle resizing allocation
+ uint32_t newAllocCount = list->allocCount * 2;
+ uint32_t newAllocSize = offsetof(struct TLVTerminatorList, entries[newAllocCount]);
+ struct TLVTerminatorList* newlist = (struct TLVTerminatorList*)malloc(newAllocSize);
+ newlist->allocCount = newAllocCount;
+ newlist->useCount = list->useCount;
+ for(uint32_t i=0; i < list->useCount; ++i)
+ newlist->entries[i] = list->entries[i];
+ pthread_setspecific(tlv_terminators_key, newlist);
+ free(list);
+ list = newlist;
+ }
+ // handle appending new entry
+ list->entries[list->useCount].termFunc = func;
+ list->entries[list->useCount].objAddr = objAddr;
+ list->useCount += 1;
+ }
+}
+
+// called by pthreads when the current thread is going way and
+// _tlv_atexit() has been called on the thread.
+static void tlv_finalize(void* storage)
+{
+ struct TLVTerminatorList* list = (struct TLVTerminatorList*)storage;
+ for(uint32_t i=0; i < list->useCount; ++i) {
+ struct TLVTerminatorListEntry* entry = &list->entries[i];
+ if ( entry->termFunc != NULL ) {
+ (*entry->termFunc)(entry->objAddr);
+ }
+ }
+ free(storage);
+}
+
+
+__attribute__((visibility("hidden")))
+void tlv_initializer()
+{
+ // create pthread key to handle thread_local destructors
+ // NOTE: this key must be allocated before any keys for TLV
+ // so that _pthread_tsd_cleanup will run destructors before deallocation
+ (void)pthread_key_create(&tlv_terminators_key, &tlv_finalize);
+
+ // register with dyld for notification when images are loaded
+ dyld_register_image_state_change_handler(dyld_image_state_bound, true, tlv_load_notification);
+}
+
+
+// linked images with TLV have references to this symbol, but it is never used at runtime
+void _tlv_bootstrap()
+{
+ abort();
+}
+
+
+// __i386__ || __x86_64__
+#else
+// !(__i386__ || __x86_64__)
+
+
+void dyld_register_tlv_state_change_handler(enum dyld_tlv_states state, dyld_tlv_state_change_handler handler)
+{
+}
+
+void dyld_enumerate_tlv_storage(dyld_tlv_state_change_handler handler)
+{
+}
+
+__attribute__((visibility("hidden")))
+void tlv_initializer()
+{
+}
+
+
+// !(__i386__ || __x86_64__)
+#endif
+
+
--- /dev/null
+#!/usr/bin/perl -w
+
+use strict;
+use Data::Dumper;
+use File::Find;
+use Cwd;
+
+$Data::Dumper::Terse = 1;
+
+my $root = undef;
+my $entry = '';
+my $pass_count = 0;
+my $total_count = 0;
+
+# first match "root: "
+
+# a line starting with "cwd:" marks the beginning of a new test case
+# call process_entry() on each test case
+while(<>)
+{
+ if(m/^root:\s+(.*?)$/)
+ {
+ $root = $1;
+ }
+ elsif(m/^cwd:\s+(.*?)$/)
+ {
+ if(length($entry))
+ {
+ &process_entry($root, $entry);
+ $entry = '';
+ }
+ $entry .= $_;
+ }
+ else
+ {
+ $entry .= $_;
+ }
+}
+# don't forget last test case (no cwd: to mark end)
+if(length($entry))
+{
+ &process_entry($root, $entry);
+}
+
+# show totals
+my $percentage = $pass_count * 100 / $total_count;
+printf " * * * %d of %d unit-tests passed (%.1f percent) * * *\n", $pass_count, $total_count, $percentage;
+
+
+sub process_entry
+{
+ my ($root, $lines) = @_;
+
+ # build an associative array of keys to value(s)
+ my $lines_seq = [split /\n/, $lines];
+ #print Dumper($lines_seq);
+ my $tbl = { 'root' => $root, 'stdout' => [], 'stderr' => [] };
+ my $line;
+ foreach $line (@$lines_seq)
+ {
+ if($line =~ m/^(\w+):\s+(.*)$/)
+ {
+ my $key = $1;
+ my $val = $2;
+ if(!exists($$tbl{$key}))
+ { $$tbl{$key} = ''; }
+
+ if($key eq 'stdout' || $key eq 'stderr') # if type is @array
+ {
+ push @{$$tbl{$key}}, $val;
+ }
+ else
+ {
+ $$tbl{$key} .= $val;
+ }
+ }
+ else
+ {
+ print "ERROR: $line";
+ }
+ }
+ #print Dumper($tbl);
+ #return;
+
+ my $test_name = $$tbl{cwd};
+ if ($test_name =~ m|.*/([a-zA-Z0-9-+_]+)$|)
+ {
+ $test_name = $1;
+ }
+
+ #if there was any output to stderr, mark this as a failure
+ my $some_errors = 0;
+ foreach $line (@{$$tbl{stderr}})
+ {
+ printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line;
+ $total_count++;
+ $some_errors = 1;
+ }
+ if ( $some_errors )
+ {
+ return;
+ }
+
+ #if make failed (exit was non-zero), mark this as a failure
+ if(0 ne $$tbl{exit})
+ {
+ printf "%-40s FAIL Makefile failure\n", $test_name;
+ $total_count++;
+ return;
+ }
+
+ $pass_count++;
+ $total_count++;
+}
print $_;
}
close(OUT) || die("$!");
+unlink "/tmp/exit-non-zero.tmp";
exit 0;
print $_;
}
close(OUT) || die("$!");
+unlink "/tmp/exit-zero-pass.tmp";
exit 0;
--- /dev/null
+#!/bin/sh
+
+# need to be root to build test suite
+sudo ./build-iPhoneOS-unit-tests
+
+# transfer to device
+echo " * * * Transfering to device * * *"
+rsync -a /tmp/unpack-and-run-all-tests /tmp/dyld-testing.cpgz rsync://root@localhost:10873/root/tmp
+
+
+# running on device
+echo " * * * Running on device * * *"
+/Developer/Platforms/iPhoneOS.platform/usr/local/bin/PurpleExec /tmp/unpack-and-run-all-tests
+
--- /dev/null
+#!/bin/sh
+
+# cd into test-cases directory
+TEST_CASE_DIR=`echo "$0" | sed 's/build-iPhoneOS-unit-tests/test-cases/'`
+cd ${TEST_CASE_DIR}
+TEST_CASE_DIR=`pwd`
+
+# make clean
+../bin/make-recursive.pl clean > /dev/null
+
+# clean staging area
+rm -rf /var/root/testing
+mkdir /var/root/testing
+
+# create scripts to run test cases on device
+echo "#!/bin/sh" > /var/root/testing/run-all-tests
+chmod +x /var/root/testing/run-all-tests
+
+# do every combination of OS version and architectures
+for os in "4.3" "3.0"
+do
+ for arch in armv6 thumb armv7 thumb2
+ do
+ # make copy of tests
+ cp -r ${TEST_CASE_DIR}/../../unit-tests /var/root/testing/unit-tests-${arch}-${os}
+ # build but don't run test cases
+ echo " * * * Building all unit tests for ${arch} iPhoneOS ${os} * * *"
+ cd /var/root/testing/unit-tests-${arch}-${os}/test-cases
+ ../bin/make-recursive.pl ARCH=${arch} OS_VERSION=${os} OS_NAME=iPhoneOS all | ../bin/build-results-filter.pl
+ # update script
+ echo "cd /var/root/testing/unit-tests-${arch}-${os}" >> /var/root/testing/run-all-tests
+ echo "echo \" * * * Running all unit tests for ${arch} iPhoneOS ${os} * * *\"" >> /var/root/testing/run-all-tests
+ echo "bin/make-recursive.pl OS_NAME=iPhoneOS check | bin/result-filter.pl" >> /var/root/testing/run-all-tests
+ done
+done
+
+# tar up all test cases
+echo " * * * Making archive * * *"
+cd /var/root
+ditto -c -z testing /tmp/dyld-testing.cpgz
+
+# create script to unpack on device
+echo "#!/bin/sh" > /tmp/unpack-and-run-all-tests
+echo "echo \" * * * Unpacking test cases * * *\"" >> /tmp/unpack-and-run-all-tests
+echo "/sbin/mount -u /private/var" >> /tmp/unpack-and-run-all-tests
+echo "chmod +x /var/root" >> /tmp/unpack-and-run-all-tests
+echo "cd /var/root" >> /tmp/unpack-and-run-all-tests
+echo "rm -rf /var/root/testing" >> /tmp/unpack-and-run-all-tests
+echo "ditto -x /tmp/dyld-testing.cpgz testing" >> /tmp/unpack-and-run-all-tests
+echo "/var/root/testing/run-all-tests" >> /tmp/unpack-and-run-all-tests
+chmod +x /tmp/unpack-and-run-all-tests
+
SHELL = /bin/sh
-# set default to be host
-ARCH ?= $(shell arch)
+# set default OS to be current Mac OS X
+OS_NAME ?= MacOSX
+ifeq "$(OS_NAME)" "iPhoneOS"
+ OS_VERSION ?= 3.1
+ ifeq "$(OS_VERSION)" "4.3"
+ OS_BAROLO_FEATURES = 1
+ endif
+ ARCH ?= armv6
+ VALID_ARCHS ?= armv6
+else
+ OS_VERSION ?= 10.7
+ ifeq "$(OS_VERSION)" "10.7"
+ OS_BAROLO_FEATURES = 1
+ endif
+ ARCH ?= $(shell arch)
+ VALID_ARCHS ?= "i386 x86_64"
+endif
-# set default to be all
-VALID_ARCHS ?= "ppc i386 x86_64"
+ifeq "$(OS_NAME)" "iPhoneOS"
+ CC = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.Internal.sdk
+ CXX = /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/g++-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.Internal.sdk
+# CC = gcc-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION)
+# CXX = g++-4.2 -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION)
+else
+ CC = gcc-4.2 -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION)
+ CXX = g++-4.2 -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION)
+endif
-CC = gcc-4.2 -arch ${ARCH}
-CCFLAGS = -Wall -std=c99
-
-CXX = g++-4.2 -arch ${ARCH}
-CXXFLAGS = -Wall
+CCFLAGS = -Wall -std=c99
+CXXFLAGS = -Wall
RM = rm
RMFLAGS = -rf
SAFE_RUN = ${TESTROOT}/bin/fail-if-non-zero.pl
PASS_IFF = ${TESTROOT}/bin/pass-iff-exit-zero.pl
+PASS_IFF_FAILURE = $(TESTROOT)/bin/exit-non-zero-pass.pl
+
+ifeq ($(ARCH),armv7)
+ CCFLAGS += -mno-thumb
+ CXXFLAGS += -mno-thumb
+ override FILEARCH = arm
+else
+ FILEARCH = $(ARCH)
+endif
+
+ifeq ($(ARCH),thumb)
+ CCFLAGS += -mthumb
+ CXXFLAGS += -mthumb
+ override ARCH = armv6
+ override FILEARCH = arm
+else
+ FILEARCH = $(ARCH)
+endif
+ifeq ($(ARCH),thumb2)
+ CCFLAGS += -mthumb
+ CXXFLAGS += -mthumb
+ override ARCH = armv7
+ override FILEARCH = arm
+else
+ FILEARCH = $(ARCH)
+endif
CRSTATE=`defaults read com.apple.CrashReporter DialogType`
defaults write com.apple.CrashReporter DialogType basic
-echo ""
-echo " * * * Running all unit tests for 32-bits * * *"
-
-# make clean
-../bin/make-recursive.pl clean > /dev/null
-
-# build default architecture
-../bin/make-recursive.pl | ../bin/result-filter.pl
-
-# if G5, then also run all test cases built for ppc64
-if [ `machine` = "ppc970" ]
-then
+# run test targeting different OS versions
+for OSVERSION in 10.7 10.6 10.5 10.4
+do
echo ""
- echo " * * * Running all unit tests for 64-bits * * *"
-
+ echo " * * * Running all unit tests i386 built for $OSVERSION * * *"
+
# make clean
../bin/make-recursive.pl clean > /dev/null
- # build 64-bit architecture
- ../bin/make-recursive.pl ARCH="ppc64" | ../bin/result-filter.pl
-fi
-
-# if Intel, then also run all test cases under emulation
-if [ "`sysctl -n hw.machine`" = "i386" ]
-then
-
- if [ -x /usr/libexec/oah/translate ]
- then
- echo ""
- echo " * * * Running all unit tests for emulated 32-bits * * *"
-
- # make clean
- ../bin/make-recursive.pl clean > /dev/null
-
- # build ppc architecture
- ../bin/make-recursive.pl ARCH="ppc" | ../bin/result-filter.pl
- fi
+ # build default architecture
+ ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl
# if 64-bit capable Intel, then also run all test cases for 64-bits
if [ `sysctl -n hw.optional.x86_64` = "1" ]
then
echo ""
- echo " * * * Running all unit tests for 64-bits * * *"
+ echo " * * * Running all unit tests x86_64 built for $OSVERSION * * *"
# make clean
../bin/make-recursive.pl clean > /dev/null
# build x86_64 architecture
- ../bin/make-recursive.pl ARCH="x86_64" | ../bin/result-filter.pl
+ ../bin/make-recursive.pl ARCH="x86_64" OS_VERSION=$OSVERSION OS_NAME=MacOSX | ../bin/result-filter.pl
fi
-fi
+
+done
# restore crash reporter state
defaults write com.apple.CrashReporter DialogType ${CRSTATE}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+PWD = $(shell pwd)
+
+all-check: all check
+
+check:
+ ./main10 10
+ ./main11 11
+ ./main911 9
+ ./main911b 9
+ ./main1112 11
+ ./main1112b 11
+ ./main1211 12
+ ./main1211b 12
+ export DYLD_LIBRARY_PATH=${PWD}/alt11 && ./main10 11
+ export DYLD_LIBRARY_PATH=${PWD}/alt9 && ./main11 9
+ export DYLD_LIBRARY_PATH=${PWD}/alt20 && ./main11 11
+
+all:
+ mkdir -p alt11 alt9 alt12
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -o "${PWD}/libfoo.dylib"
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/libfoo.dylib" -o alt11/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/libfoo.dylib" -o alt9/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/libfoo.dylib" -o alt12/libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main10 main.c libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main11 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911b main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt12
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112b main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211 main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211b main.c libfoo.dylib -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11
+
+
+clean:
+ ${RM} -rf libfoo.dylib alt9 alt11 alt12 main10 main11 main9 main911 main911b main1112 main1112b main1211 main1211b
+
--- /dev/null
+
+int foo()
+{
+ return RESULT;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h> // for atoi()
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+
+extern int foo();
+
+int main(int argc, const char* argv[])
+{
+ int expectedResult = atoi(argv[1]);
+ int actualResult = foo();
+ //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
+ if ( actualResult != expectedResult )
+ FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+ else
+ PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
+
+ return EXIT_SUCCESS;
+}
+
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+PWD = $(shell pwd)
+
+all-check: all check
+
+check:
+ ./main 10
+ export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11" && ./main 11
+ export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9" && ./main 10
+ export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9:${PWD}/alt11" && ./main 11
+ export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11:${PWD}/alt12" && ./main 12
+
+all:
+ mkdir -p Foo.framework alt11/Foo.framework alt9/Foo.framework alt12/Foo.framework
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -install_name "${PWD}/Foo.framework/Foo" -o Foo.framework/Foo
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c Foo.framework/Foo
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/Foo.framework/Foo" -o alt11/Foo.framework/Foo
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/Foo.framework/Foo" -o alt9/Foo.framework/Foo
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/Foo.framework/Foo" -o alt12/Foo.framework/Foo
+
+
+clean:
+ ${RM} -rf main Foo.framework alt9 alt11 alt12
+
--- /dev/null
+
+int foo()
+{
+ return RESULT;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h> // for atoi()
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+
+extern int foo();
+
+int main(int argc, const char* argv[])
+{
+ int expectedResult = atoi(argv[1]);
+ int actualResult = foo();
+ //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
+ if ( actualResult != expectedResult )
+ FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+ else
+ PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
+
+ return EXIT_SUCCESS;
+}
+
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+PWD = $(shell pwd)
+
+all-check: all check
+
+check:
+ ./main 10
+ export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt11" && ./main 11
+ export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt9" && ./main 10
+ export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt9:${PWD}/alt11" && ./main 11
+ export DYLD_VERSIONED_LIBRARY_PATH="${PWD}/alt11:${PWD}/alt12" && ./main 12
+
+all:
+ mkdir -p alt11 alt9 alt12
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -o "${PWD}/libfoo.dylib"
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/libfoo.dylib" -o alt11/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/libfoo.dylib" -o alt9/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/libfoo.dylib" -o alt12/libfoo.dylib
+
+
+clean:
+ ${RM} -rf main libfoo.dylib alt9 alt11 alt12
+
--- /dev/null
+
+int foo()
+{
+ return RESULT;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h> // for atoi()
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+
+extern int foo();
+
+int main(int argc, const char* argv[])
+{
+ int expectedResult = atoi(argv[1]);
+ int actualResult = foo();
+ //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
+ if ( actualResult != expectedResult )
+ FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+ else
+ PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
+
+ return EXIT_SUCCESS;
+}
+
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+PWD = $(shell pwd)
+
+all-check: all check
+
+check:
+ ./main10 10
+ ./main11 11
+ ./main911 11
+ ./main911b 11
+ ./main1112 12
+ ./main1112b 12
+ ./main1211 12
+ ./main1211b 12
+
+all:
+ mkdir -p alt11 alt9 alt12
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -o "${PWD}/libfoo.dylib"
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/libfoo.dylib" -o alt11/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/libfoo.dylib" -o alt9/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/libfoo.dylib" -o alt12/libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main10 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main11 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911b main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112b main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211 main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211b main.c libfoo.dylib -Wl,-sectcreate,__RESTRICT,__restrict,/dev/null -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11
+
+
+clean:
+ ${RM} -rf libfoo.dylib alt9 alt11 alt12 main10 main11 main9 main911 main911b main1112 main1112b main1211 main1211b
+
--- /dev/null
+
+int foo()
+{
+ return RESULT;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h> // for atoi()
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+
+extern int foo();
+
+int main(int argc, const char* argv[])
+{
+ int expectedResult = atoi(argv[1]);
+ int actualResult = foo();
+ //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
+ if ( actualResult != expectedResult )
+ FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+ else
+ PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
+
+ return EXIT_SUCCESS;
+}
+
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+PWD = $(shell pwd)
+
+all-check: all check
+
+check:
+ ./main10 10
+ ./main11 11
+ ./main911 11
+ ./main911b 11
+ ./main911c 11
+ ./main1112 12
+ ./main1112b 12
+ ./main1211 12
+ ./main1211b 12
+ export DYLD_LIBRARY_PATH=./alt9 && ./main11 9
+
+all:
+ mkdir -p alt11 alt9 alt12
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -current_version 10 -o "${PWD}/libfoo.dylib"
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/libfoo.dylib" -o alt11/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=9 -current_version 9 -install_name "${PWD}/libfoo.dylib" -o alt9/libfoo.dylib
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=12 -current_version 12 -install_name "${PWD}/libfoo.dylib" -o alt12/libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main10 main.c libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main11 main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911 main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911b main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt9:@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main911c main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@executable_path/alt9:@executable_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112 main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1112b main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11:@loader_path/alt12
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211 main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12 -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt11
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main1211b main.c libfoo.dylib -Wl,-dyld_env,DYLD_VERSIONED_LIBRARY_PATH=@loader_path/alt12:@loader_path/alt11
+
+
+clean:
+ ${RM} -rf libfoo.dylib alt9 alt11 alt12 main10 main11 main9 main911 main911b main911c main1112 main1112b main1211 main1211b
+
--- /dev/null
+
+int foo()
+{
+ return RESULT;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h> // for atoi()
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+
+extern int foo();
+
+int main(int argc, const char* argv[])
+{
+ int expectedResult = atoi(argv[1]);
+ int actualResult = foo();
+ //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult);
+ if ( actualResult != expectedResult )
+ FAIL("DYLD_VERSIONED_LIBRARY_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult);
+ else
+ PASS("DYLD_VERSIONED_LIBRARY_PATH-basic");
+
+ return EXIT_SUCCESS;
+}
+
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <stdlib.h>
#include <mach-o/dyld.h>
-
+#include <Availability.h>
+
#include "test.h"
/// rdar://problem/4058724
int main()
{
+// NSAddImage is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// test that image can be found via install path
const struct mach_header * mh1 = NSAddImage("libbar.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME);
const struct mach_header * mh2 = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME);
- if ( mh2 != NULL )
- PASS("NSAddImage-MATCH_BY_INSTALLNAME");
- else
+ if ( mh2 == NULL )
FAIL("NSAddImage-MATCH_BY_INSTALLNAME");
+ else
+#endif
+ PASS("NSAddImage-MATCH_BY_INSTALLNAME");
return EXIT_SUCCESS;
}
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <stdlib.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h"
int main()
{
+// NSAddImage is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// test that NSAddImage() does not crash if image is not loaded
const struct mach_header * mh = NSAddImage("/System/Library/Frameworks/Cocoa.framework/Cocoa", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED);
- if ( mh == NULL )
- PASS("NSAddImage-RETURN_ONLY_IF_LOADED");
- else
+ if ( mh != NULL )
FAIL("NSAddImage-RETURN_ONLY_IF_LOADED");
+ else
+#endif
+ PASS("NSAddImage-RETURN_ONLY_IF_LOADED");
return EXIT_SUCCESS;
}
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <stdlib.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h"
int main()
{
+// NSAddImage is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// test that NSAddImage() uses fallback path when given a leaf name
const struct mach_header * mh = NSAddImage("libzzz.dylib", NSADDIMAGE_OPTION_WITH_SEARCHING);
- if ( mh != NULL )
- PASS("NSAddImage-leafname");
- else
+ if ( mh == NULL )
FAIL("NSAddImage-leafname");
+ else
+#endif
+ PASS("NSAddImage-leafname");
return EXIT_SUCCESS;
}
/*
- * Copyright (c) 2008 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h> // fprintf(), NULL
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <string.h>
+#include <Availability.h>
#include <mach-o/dyld.h>
int main()
{
+// NSAddressOfSymbol is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSAddressOfSymbol(NULL);
+#endif
PASS("NSAddressOfSymbol-NULL");
return 0;
}
PASS("addend");
+ return 0;
}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+#include <mach/mach.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+static uintptr_t libSystemSlide()
+{
+ Dl_info info;
+ if ( dladdr(&malloc, &info) == 0 ) {
+ FAIL("all_image_infos-cache-slide: dladdr(&malloc, xx) failed");
+ exit(0);
+ }
+
+ const struct mach_header* mallocMh = (struct mach_header*)info.dli_fbase;
+ if ( (mallocMh->flags & 0x80000000) == 0 ) {
+ FAIL("all_image_infos-cache-slide: image containing _malloc not in shared cache");
+ exit(0);
+ }
+
+ int count = _dyld_image_count();
+ for(int i=0; i < count; ++i) {
+ if ( mallocMh == _dyld_get_image_header(i) ) {
+ return _dyld_get_image_vmaddr_slide(i);
+ }
+ }
+
+ FAIL("all_image_infos-cache-slide: slide of image containing _malloc not found");
+ exit(0);
+}
+
+
+int main(int argc, const char* argv[])
+{
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+
+ if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
+ FAIL("all_image_infos-cache-slide: task_info() failed");
+ return 0;
+ }
+ struct dyld_all_image_infos* infos = (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr;
+
+ if ( infos->version < 12 ) {
+ FAIL("all_image_infos-cache-slide: version < 12");
+ return 0;
+ }
+
+ if ( libSystemSlide() != infos->sharedCacheSlide ) {
+ FAIL("all_image_infos-cache-slide: dyld infos sharedCacheSlide (0x%08X) does not match computed slide (0x%08X)",
+ infos->sharedCacheSlide, libSystemSlide());
+ return 0;
+ }
+
+ PASS("all_image_infos-cache-slide");
+ return EXIT_SUCCESS;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -install_name /usr/local/lib/libfoo.dylib
+ cp libfoo.dylib libfoodup.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoodup.dylib
--- /dev/null
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+int foo()
+{
+ return 10;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+#include <mach/mach.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+struct dyld_all_image_infos* getImageInfosFromKernel()
+{
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+
+ if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
+ FAIL("all_image_infos: task_info() failed");
+ exit(0);
+ }
+ return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr;
+}
+
+
+int main(int argc, const char* argv[])
+{
+ void* handle = dlopen("libfoo.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror());
+ exit(0);
+ }
+
+ void* handle2 = dlopen("libfoodup.dylib", RTLD_LAZY);
+ if ( handle2 == NULL ) {
+ FAIL("dlopen(\"%s\") failed with: %s", "libfoodup.dylib", dlerror());
+ exit(0);
+ }
+
+ struct dyld_all_image_infos* infos = getImageInfosFromKernel();
+ for( int i=0; i < infos->infoArrayCount; ++i) {
+ //fprintf(stderr, "[%d] %s\n", i , infos->infoArray[i].imageFilePath);
+ if ( strlen(infos->infoArray[i].imageFilePath) < 5 ) {
+ FAIL("all_image_infos-duplicates: bad entry for address 0x%08X", infos->infoArray[i].imageLoadAddress);
+ return EXIT_SUCCESS;
+ }
+ }
+
+ PASS("all_image_infos-duplicates");
+ return EXIT_SUCCESS;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main `pwd`/test.bundle
+ ./main `pwd`/test.dylib
+
+all:
+ ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main test.bundle test.dylib
+
--- /dev/null
+/*
+ * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+int foo()
+{
+ return 10;
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+#include <mach/mach.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+struct dyld_all_image_infos* getImageInfosFromKernel()
+{
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+
+ if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
+ FAIL("all_image_infos: task_info() failed");
+ exit(0);
+ }
+ return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr;
+}
+
+
+int main(int argc, const char* argv[])
+{
+ char path[1024];
+ strcpy(path, argv[1]);
+
+ void* handle = dlopen(path, RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlopen(\"%s\") failed with: %s", path, dlerror());
+ exit(0);
+ }
+ strcpy(path, "bad");
+
+ struct dyld_all_image_infos* infos = getImageInfosFromKernel();
+ for( int i=0; i < infos->infoArrayCount; ++i) {
+ //fprintf(stderr, "[%d] %s\n", i , infos->infoArray[i].imageFilePath);
+ if ( strcmp(infos->infoArray[i].imageFilePath, argv[1]) == 0 ) {
+ PASS("all_image_infos-path");
+ return EXIT_SUCCESS;
+ }
+ }
+
+ FAIL("all_image_infos-path");
+ return EXIT_SUCCESS;
+}
/*
- * Copyright (c) 2008 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <mach-o/dyld.h>
#include <mach-o/dyld_images.h>
#include <mach/mach.h>
+#include <Availability.h>
#include "test.h"
extern struct mach_header __dso_handle;
-#ifndef DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET
- #define DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET 0x1010
-#endif
-
-
-#if __i386__ || __ppc__
- #define DYLD_BASE_ADDRESS 0x8fe00000
-#elif __x86_64__ || __ppc64__
- #define DYLD_BASE_ADDRESS 0x7fff5fc00000
-#elif __arm__
- #define DYLD_BASE_ADDRESS 0x2fe00000
-#endif
-
-struct dyld_all_image_infos* getImageInfos()
-{
- uint32_t offset = *((uint32_t*)(DYLD_BASE_ADDRESS+DYLD_ALL_IMAGE_INFOS_OFFSET_OFFSET));
- if ( offset > 300000 ) {
- FAIL("all_image_infos: offset appears to be outside dyld");
- exit(0);
- }
- uintptr_t addr = DYLD_BASE_ADDRESS + offset;
- return (struct dyld_all_image_infos*)addr;
-}
-
struct dyld_all_image_infos* getImageInfosFromKernel()
{
int
main()
{
- struct dyld_all_image_infos* infos = getImageInfos();
- if ( infos->version != 7 ) {
- FAIL("all_image_infos: dyld_all_image_infos is not version 7");
+ struct dyld_all_image_infos* infos = getImageInfosFromKernel();
+ if ( infos->version < 9 ) {
+ FAIL("all_image_infos: dyld_all_image_infos is not version >= 9, is %d", infos->version);
exit(0);
}
exit(0);
}
+ //for( int i=0; i < infos->infoArrayCount; ++i) {
+ // fprintf(stderr, "infos->infoArray[%d].imageLoadAddress=%p %s\n", i, infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath);
+ //}
+
if ( infos->infoArray[0].imageLoadAddress != &__dso_handle ) {
- FAIL("all_image_infos: dyld_all_image_infos.infoArray for main executable is wrong");
+ FAIL("all_image_infos: dyld_all_image_infos.infoArray for main executable is wrong, infos->infoArray[0].imageLoadAddress=0x%08lX vs 0x%08X",
+ infos->infoArray[0].imageLoadAddress, &__dso_handle);
exit(0);
}
- if ( getImageInfosFromKernel() != infos ) {
- FAIL("all_image_infos: task_info and dyld disagree");
- exit(0);
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ for (const struct dyld_image_info* p = infos->infoArray; p < &infos->infoArray[infos->infoArrayCount]; ++p) {
+ //fprintf(stderr, "addr=0x%p, mTime=0x%lX, path=%s\n", p->imageLoadAddress, p->imageFileModDate, p->imageFilePath);
+ if ( (strncmp(p->imageFilePath, "/usr/lib", 8) == 0) && (p->imageFileModDate != 0) ) {
+ FAIL("all_image_infos, mTime not zero for cache image %s", p->imageFilePath);
+ exit(0);
+ }
}
+#endif
PASS("all_image_infos");
return EXIT_SUCCESS;
check:
./main
+ifneq "$(OS_NAME)" "iPhoneOS"
+ # "avoid" does not work on iPhoneOS because the original dylibs don't exist
export DYLD_SHARED_REGION=avoid && ./main
+endif
all: main
main: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
endif
-ifeq "armv6" "$(ARCH)"
+ifeq "iPhoneOS" "$(OS_NAME)"
STACK_SIZE = 0x20000000
endif
--- /dev/null
+##
+# Copyright (c) 2008-2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+all-check: all check
+
+check:
+ ./main
+
+all: main
+
+main : main.c space.s extra.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c space.s extra.c
+
+
+
+clean:
+ rm ${RMFLAGS} main
--- /dev/null
+#include <stdbool.h>
+
+
+bool test1()
+{
+ return false;
+}
+
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <stdbool.h>
+#include <string.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+// returns false on success
+extern bool test1();
+
+const char* str = "hello";
+
+
+int main()
+{
+ if ( test1() )
+ FAIL("branch-islands: test1");
+
+ if ( strncmp(str, "he", 2) != 0 )
+ FAIL("branch-islands: strncmp stub");
+
+
+ PASS("branch-islands");
+ return EXIT_SUCCESS;
+}
+
--- /dev/null
+
+#if __ppc__
+
+ .text
+
+_prejunk:
+ mr r3,r5
+ mr r3,r4
+ blr
+
+
+_space1:
+ .space 15*1024*1024 + 2
+
+ .align 5
+_junk:
+ mr r3,r5
+ mr r3,r4
+ blr
+
+
+_space2:
+ .space 2*1024*1024
+
+#endif
+
+
+#if __arm__
+
+ .text
+_prejunk:
+ mov r0, #1
+ nop
+
+#if __thumb2__
+ // thumb2 branches are +/- 16MB
+_space1:
+ .space 12*1024*1024
+_space2:
+ .space 12*1024*1024
+_space3:
+ .space 12*1024*1024
+
+
+#elif __thumb__
+ // thumb1 branches are +/- 4MB
+_space1:
+ .space 3*1024*1024
+_space2:
+ .space 3*1024*1024
+_space3:
+ .space 3*1024*1024
+
+#else
+
+ // ARM branches are +/- 32MB
+_space1:
+ .space 24*1024*1024
+_space2:
+ .space 24*1024*1024
+_space3:
+ .space 24*1024*1024
+
+#endif
+
+ .align 5
+_junk:
+ mov r0, #1
+ nop
+
+
+_space4:
+ .space 2*1024*1024
+#endif
+
+ .subsections_via_symbols
+
\ No newline at end of file
#include <stdio.h>
#include <stdbool.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// these APIs are only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSCreateObjectFileImageFromFile failed");
FAIL("NSDestroyObjectFileImage failed");
return 1;
}
-
+#endif
PASS("bundle-basic");
return 0;
}
\ No newline at end of file
int main(int argc, const char* argv[])
{
+// NSObjectFile* APIs are only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// load foo.bundle with old API
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("foo.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSUnLinkModule failed");
return 1;
}
-
-
+#endif
+
PASS("bundle-dont-gc");
return EXIT_SUCCESS;
}
--- /dev/null
+##
+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main
+
+all: main test.bundle
+
+main : main.c
+ ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c
+
+test.bundle : bundle.c
+ ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c
+
+clean:
+ ${RM} ${RMFLAGS} *~ main test.bundle
+
--- /dev/null
+/*
+ * 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 <stdbool.h>
+
+// test to see if bss section is properly expanded
+
+static int mydata[1000000];
+
+bool checkdata()
+{
+ return ( mydata[500000] == 0 );
+}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <Availability.h>
+
+#include "test.h" // PASS(), FAIL()
+
+
+struct dyld_all_image_infos* getImageInfosFromKernel()
+{
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+
+ if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
+ FAIL("all_image_infos: task_info() failed");
+ exit(0);
+ }
+ return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr;
+}
+
+
+int main()
+{
+// NSObjectFileImage APIs are only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ int fd = open("test.bundle", O_RDONLY, 0);
+ if ( fd == -1 ) {
+ FAIL("open() failed");
+ return 1;
+ }
+
+ struct stat stat_buf;
+ if ( fstat(fd, &stat_buf) == -1) {
+ FAIL("fstat() failed");
+ return 0;
+ }
+
+ void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+ if ( loadAddress == ((void*)(-1)) ) {
+ FAIL("mmap() failed");
+ return 0;
+ }
+
+ close(fd);
+
+ NSObjectFileImage ofi;
+ if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) {
+ FAIL("NSCreateObjectFileImageFromMemory failed");
+ return 0;
+ }
+
+ NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE);
+ if ( mod == NULL ) {
+ FAIL("NSLinkModule failed");
+ return 0;
+ }
+
+ // look for he_he string in list of images loaded
+ struct dyld_all_image_infos* infos = getImageInfosFromKernel();
+
+ if ( infos->infoArrayCount < 2 ) {
+ FAIL("bundle-memory-load-all-images: dyld_all_image_infos.infoArrayCount is < 2");
+ return 0;
+ }
+
+ bool found = false;
+ for( int i=0; i < infos->infoArrayCount; ++i) {
+ //fprintf(stderr, "infos->infoArray[%d].imageLoadAddress=%p %s\n", i, infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath);
+ if ( infos->infoArray[i].imageFilePath == NULL ) {
+ FAIL("bundle-memory-load-all-images: NULL image path found");
+ exit(0);
+ }
+ if ( strcmp(infos->infoArray[i].imageFilePath, "he_he") == 0 )
+ found = true;
+ }
+
+ if ( !found ) {
+ FAIL("bundle-memory-load-all-images: loaded memory bundle 'he_he' nout found");
+ return 0;
+ }
+
+ if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) {
+ FAIL("NSUnLinkModule failed");
+ return 0;
+ }
+
+ if ( !NSDestroyObjectFileImage(ofi) ) {
+ FAIL("NSDestroyObjectFileImage failed");
+ return 0;
+ }
+
+ // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage)
+#endif
+
+ PASS("bundle-memory-load-all-images");
+ return 0;
+}
\ No newline at end of file
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSAddImage is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
int fd = open("test.bundle", O_RDONLY, 0);
if ( fd == -1 ) {
FAIL("open() failed");
// we are using a file not of type MH_BUNDLE, so NSCreateObjectFileImageFromMemory should fail
NSObjectFileImage ofi;
- if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess )
- PASS("bundle-memory-load-bad");
- else
+ if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) == NSObjectFileImageSuccess )
FAIL("bundle-memory-load-bad");
+ else
+#endif
+ PASS("bundle-memory-load-bad");
return 0;
}
\ No newline at end of file
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSObjectFileImage APIs only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
int fd = open("test.bundle", O_RDONLY, 0);
if ( fd == -1 ) {
FAIL("open() failed");
}
// Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage)
+#endif
PASS("bundle-memory-load-fat");
return 0;
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <fcntl.h>
#include <malloc/malloc.h>
#include <stdlib.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
int fd = open("test.bundle", O_RDONLY, 0);
if ( fd == -1 ) {
FAIL("open() failed");
return 1;
}
-
-
// Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage)
+#endif
PASS("bundle-memory-load-malloc");
return 0;
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSObjectFileImage APIs are only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
int fd = open("test.bundle", O_RDONLY, 0);
if ( fd == -1 ) {
FAIL("open() failed");
}
// Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage)
+#endif
PASS("bundle-memory-load");
return 0;
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
#include <stdio.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSCreateObjectFileImageFromFile failed");
FAIL("NSDestroyObjectFileImage failed");
return 0;
}
-
+#endif
+
PASS("bundle-multi-link");
return 0;
}
*/
#include <stdio.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSCreateObjectFileImageFromFile failed");
FAIL("3rd NSDestroyObjectFileImage failed");
return 0;
}
-
+#endif
PASS("bundle-multi-load");
return 0;
}
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
const char* path = "test.bundle";
NSObjectFileImage ofi;
FAIL("NSDestroyObjectFileImage failed");
return 0;
}
-
+#endif
PASS("bundle-name-ownership");
return 0;
}
\ No newline at end of file
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <stdbool.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSCreateObjectFileImageFromFile failed");
FAIL("NSDestroyObjectFileImage failed");
return 1;
}
-
+#endif
PASS("bundle-private");
return 0;
}
\ No newline at end of file
#include <stdio.h>
#include <stdlib.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
// test.bundle
void doit()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSCreateObjectFileImageFromFile failed");
FAIL("NSDestroyObjectFileImage failed");
exit(0);
}
+#endif
}
static void myRemoveImage(const struct mach_header *mh, intptr_t vmaddr_slide)
{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// calling _dyld_get_image_header_containing_address() during the remove image hook
// could cause dyld to not flush the address->image cache
_dyld_get_image_header_containing_address(mh);
+#endif
}
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <string.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSCreateObjectFileImageFromFile failed");
return 1;
}
}
-
+#endif
PASS("bundle-unlinkable");
return 0;
}
\ No newline at end of file
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <stdbool.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
FAIL("NSCreateObjectFileImageFromFile failed");
// call function again, even though bundle is unloaded
func();
-
+#endif
PASS("bundle-basic");
return 0;
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
+extern void bar();
+
+
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
void loadAsBundleFromMemory(const char* path)
{
}
}
-extern void bar();
+#endif
int main()
{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
int dummy;
// verify that NSAddImage fails to load MH_BUNDLE
// verify that dyld data structures are not wanked by scanning all images
_dyld_get_image_header_containing_address(&dummy);
-
+#endif
PASS("bundle-v-dylib");
return 0;
}
\ No newline at end of file
--- /dev/null
+##
+# Copyright (c) 2009 Apple, Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+SHELL = bash # use bash shell so we can redirect just stderr
+
+all-check: all check
+
+check:
+ export DYLD_PRINT_CS_NOTIFICATIONS=1 && ./main 2> notifications.log
+ grep " load" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null
+ grep " load" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null
+ grep " unload" notifications.log | grep foo.bundle | wc -l | grep 1 >/dev/null
+ grep " unload" notifications.log | grep bar.dylib | wc -l | grep 1 >/dev/null
+ echo "PASS coreSymbolication-notify"
+
+all: main foo.bundle
+
+main: main.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
+
+foo.bundle: foo.c libbar.dylib
+ ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib
+
+libbar.dylib : bar.c
+ ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib notifications.log
+
--- /dev/null
+
+int bar = 10;
--- /dev/null
+
+void foo() {}
+
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdbool.h> // fprintf(), NULL
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <stdbool.h>
+#include <string.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_priv.h>
+#include <dlfcn.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+int main(int argc, const char* argv[])
+{
+
+ if ( ! dlopen_preflight("foo.bundle") ) {
+ FAIL("coreSymbolication-notify foo.bundle should not be loadable");
+ exit(0);
+ }
+
+ void* h = dlopen("foo.bundle", RTLD_LAZY);
+ if ( h == NULL ) {
+ FAIL("coreSymbolication-notify foo.bundle failed: %s", dlerror());
+ exit(0);
+ }
+
+ dlclose(h);
+
+
+ return EXIT_SUCCESS;
+}
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2010 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
all-check: all check
check:
- ./main-10.4 ./main-10.4
- ./main-10.5 ./main-10.5
- ./main-10.4.stripped ./main-10.4.stripped
- ./main-10.5.stripped ./main-10.5.stripped
- `pwd`/main-10.4 `pwd`/main-10.4
- `pwd`/main-10.5 `pwd`/main-10.5
- `pwd`/main-10.4.stripped `pwd`/main-10.4.stripped
- `pwd`/main-10.5.stripped `pwd`/main-10.5.stripped
+ ./main ./main
+ ./main.stripped ./main.stripped
+ `pwd`/main `pwd`/main
+ `pwd`/main.stripped `pwd`/main.stripped
+ export DYLD_LIBRARY_PATH=. && export DYLD_FRAMEWORK_PATH=. && ./main-setuid ./main-setuid
-all: main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped
+all: main main.stripped main-setuid
-main-10.4: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4 -w
+main: main.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -w
-main-10.4.stripped: main-10.4
- strip main-10.4 -o main-10.4.stripped
-
-main-10.5: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5 -w
+main.stripped: main
+ strip main -o main.stripped
+
+main-setuid: main
+ cp main main-setuid
+ sudo chown root main-setuid
+ sudo chmod 4755 main-setuid
-main-10.5.stripped: main-10.5
- strip main-10.5 -o main-10.5.stripped
clean:
- ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.4.stripped main-10.5.stripped
+ ${RM} ${RMFLAGS} *~ main main.stripped main-setuid
/*
- * Copyright (c) 2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
///
+/// verify parameters passed to initializer are same as passed to main()
/// verify that apple[0] parameter is correct by comparing to argv[1]
///
+static int initializer_argc = 0;
+static const char** initializer_argv = NULL;
+static const char** initializer_env = NULL;
+static const char** initializer_apple = NULL;
+
+
+__attribute__((constructor))
+void init(int argc, const char* argv[], const char* env[], const char* apple[])
+{
+ initializer_argc = argc;
+ initializer_argv = argv;
+ initializer_env = env;
+ initializer_apple = apple;
+}
+
int
main(int argc, const char* argv[], const char* env[], const char* apple[])
{
+ if ( argc != initializer_argc ) {
+ FAIL("crt-apple argc changed");
+ exit(EXIT_SUCCESS);
+ }
+
+ if ( argv != initializer_argv ) {
+ FAIL("crt-apple argv changed");
+ exit(EXIT_SUCCESS);
+ }
+
+ if ( env != initializer_env ) {
+ FAIL("crt-apple envp changed");
+ exit(EXIT_SUCCESS);
+ }
+
+ if ( apple != initializer_apple ) {
+ FAIL("crt-apple apple changed");
+ exit(EXIT_SUCCESS);
+ }
+
if ( strcmp(apple[0], argv[1]) == 0 )
PASS("crt-apple %s", argv[0]);
else
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
all-check: all check
check:
- ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main-10.4" "crt-argv-NULL main-10.4" ./main-10.4
- ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main-10.5" "crt-argv-NULL main-10.5" ./main-10.5
+ ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main" "crt-argv-NULL main" ./main
-all: main-10.4 main-10.5
+all: main
-main-10.4: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4
-main-10.5: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5
+main: main.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
clean:
- ${RM} ${RMFLAGS} *~ main-10.4 main-10.5
+ ${RM} ${RMFLAGS} *~ main
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
all-check: all check
check:
- ./main-10.4
- ./main-10.5
+ ./main
-all: main-10.4 main-10.5
+all: main
-main-10.4: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 mystart.s main.c -mmacosx-version-min=10.4 -e _mystart
-
-main-10.5: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 mystart.s main.c -mmacosx-version-min=10.5 -e _mystart
+main: main.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main mystart.s main.c -e _mystart
clean:
- ${RM} ${RMFLAGS} *~ main-10.4 main-10.5
+ ${RM} ${RMFLAGS} *~ main
/*
- * Copyright (c) 2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h> // fprintf(), NULL
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <dlfcn.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
// for 64-bit binaries initializers are always called before entry point
#define ENTRY_BEFORE_INIT 0
#else
- // for pre 10.5, 32-bit binaries, entry point is called which then calls initializers
- #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
- #define ENTRY_BEFORE_INIT 1
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED
+ // for pre 10.5, 32-bit binaries, entry point is called which then calls initializers
+ #if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5
+ #define ENTRY_BEFORE_INIT 1
+ #else
+ #define ENTRY_BEFORE_INIT 0
+ #endif
#else
+ // for iPhoneOS, initializers are always called before entry point
#define ENTRY_BEFORE_INIT 0
#endif
#endif
+# Built output for (null)
+# Generated at (null)
+# Using (null) configuration, (null) architecture for (null) target of (null) project
+
.globl _mystart
_mystart:
#if __i386__
- movl $2, _flag
+ call L1
+L1: popl %eax
+ movl L_flag$non_lazy_ptr-L1(%eax), %eax
+ movl $2, (%eax)
jmp start
#elif __x86_64__
movl $2, _flag(%rip)
lis r2,ha16(_flag)
stw r0,lo16(_flag)(r2)
b start
+#elif __arm__
+ ldr r3, L4
+ mov r2, #2
+ add r3, pc
+ ldr r3, [r3]
+ str r2, [r3, #0]
+ b start
+L4: .long L_flag$non_lazy_ptr-(L4-8)
+#endif
+
+#if __i386__ || __arm__
+ .section __DATA,__nl_symbol_ptr,non_lazy_symbol_pointers
+ .align 2
+L_flag$non_lazy_ptr:
+.indirect_symbol _flag
+ .long 0
#endif
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
# the mechanism for 10.4 and 10.5 is different
#
+
all-check: all check
check:
- ./main-10.4
- ./main-10.5
- ./main-10.6
- ./main-10.4.stripped
- ./main-10.5.stripped
- ./main-10.6.stripped
-
-all: main-10.4 main-10.5 main-10.6 main-10.4.stripped main-10.5.stripped main-10.6.stripped
-
-main-10.4: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.4 main.c -mmacosx-version-min=10.4
+ ./main
+ ./main.stripped
-main-10.4.stripped: main-10.4
- strip main-10.4 -o main-10.4.stripped
-
-main-10.5: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.5 main.c -mmacosx-version-min=10.5
+all: main main.stripped
-main-10.5.stripped: main-10.5
- strip main-10.5 -o main-10.5.stripped
-main-10.6: main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main-10.6 main.c -mmacosx-version-min=10.6 -nostdlib -lcrt1.10.6.o -lSystem
+main: main.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include $(CRTLIB) -o main main.c
-main-10.6.stripped: main-10.6
- strip main-10.6 -o main-10.6.stripped
+main.stripped: main
+ strip main -o main.stripped
clean:
- ${RM} ${RMFLAGS} *~ main-10.4 main-10.5 main-10.6 main-10.4.stripped main-10.5.stripped main-10.6.stripped
+ ${RM} ${RMFLAGS} *~ main main.stripped
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
all-check: all check
check:
- ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good-10.4" "crt-result good-10.4" ./good-10.4
- ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good-10.5" "crt-result good-10.5" ./good-10.5
- ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad-10.4" "crt-result bad-10.4" ./bad-10.4
- ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad-10.5" "crt-result bad-10.5" ./bad-10.5
+ ${TESTROOT}/bin/exit-zero-pass.pl "crt-result good" "crt-result good" ./good
+ ${TESTROOT}/bin/exit-non-zero-pass.pl "crt-result bad" "crt-result bad" ./bad
-all: good-10.4 good-10.5 bad-10.4 bad-10.5
+all: good bad
-good-10.4: good.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good-10.4 good.c -mmacosx-version-min=10.4
-good-10.5: good.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good-10.5 good.c -mmacosx-version-min=10.5
+good: good.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good good.c
-bad-10.4: bad.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad-10.4 bad.c -mmacosx-version-min=10.4
-
-bad-10.5: bad.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad-10.5 bad.c -mmacosx-version-min=10.5
+bad: bad.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad bad.c
clean:
- ${RM} ${RMFLAGS} *~ good-10.4 good-10.5 bad-10.4 bad-10.5
+ ${RM} ${RMFLAGS} *~ good bad
* @APPLE_LICENSE_HEADER_END@
*/
+#include <stdlib.h>
class A
{
public:
A() { f = 10; }
- ~A() { f = 0; }
+ ~A() { if ( f == 0 ) abort(); f = 0; }
int get() { return f; }
private:
int f;
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <mach-o/dyld.h>
#include <pthread.h>
#include <unistd.h>
+#include <Availability.h>
#include "test.h"
extern void foo();
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
static volatile int sBarrier = 0;
static pthread_mutex_t sBarrierMutex;
pthread_mutex_unlock(&sMyLock);
}
}
+#endif
int main()
{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
pthread_mutex_init(&sBarrierMutex, NULL);
pthread_cond_init(&sBarrierFree, NULL);
pthread_mutex_init(&sMyLock, NULL);
// thread 1
_dyld_register_func_for_add_image(&myImageHandler);
NSAddImage("bar.dylib", 0);
+#endif
PASS("deadlock");
+ return 0;
}
##
-# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
./main
all:
- ${CC} ${CCFLAGS} -Wno-deprecated-declarations -g -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -Wno-deprecated-declarations -g -I${TESTROOT}/include -o main main.c
clean:
#include <string.h>
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
FAIL("dladdr()->dli_saddr is not &bar");
exit(0);
}
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
if ( info.dli_fbase != _dyld_get_image_header_containing_address(&bar) ) {
FAIL("dladdr()->dli_fbase is not image that contains &bar");
exit(0);
}
+#endif
}
// checks local symbol
FAIL("dladdr()->dli_saddr is not &foo");
exit(0);
}
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
if ( info.dli_fbase != _dyld_get_image_header_containing_address(&foo) ) {
FAIL("dladdr()->dli_fbase is not image that contains &foo");
exit(0);
}
+#endif
}
// checks hidden symbol
FAIL("dladdr()->dli_saddr is not &hide");
exit(0);
}
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
if ( info.dli_fbase != _dyld_get_image_header_containing_address(&hide) ) {
FAIL("dladdr()->dli_fbase is not image that contains &hide");
exit(0);
}
+#endif
}
##
-# Copyright (c) 2008 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2008-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
+
all-check: all check
check:
- ./main
+ chmod -r libnoread.dylib
+ ${RUN_AS_USER} ./main
all:
${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
${CC} ${CCFLAGS} foo.c -dynamiclib -o libnoread.dylib
- chmod -r libnoread.dylib
clean:
${RM} ${RMFLAGS} *~ main libnoread.dylib
foo.exe : foo.c
- ${CC} ${CCFLAGS} foo.c -o foo.exe
+ ${CC} ${CCFLAGS} foo.c -o foo.exe -Wl,-no_pie
foo.pie : foo.c
${CC} ${CCFLAGS} foo.c -o foo.pie -Wl,-pie
#include <dlfcn.h>
#include <libkern/OSCacheControl.h> // sys_icache_invalidate
#include <sys/mman.h> // for mprotext
+#include <mach/mach.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
return (*dlopen_proc)(path, mode);
}
+#if __thumb__
+ #define START_OF_FUNC(x) ((void*)((long)x & (-2)))
+ #define ADDR_FROM_BLOCK(x) ((void*)((long)x | 1))
+#else
+ #define START_OF_FUNC(x) (x)
+ #define ADDR_FROM_BLOCK(x) (x)
+#endif
+
//
// try calling dlopen() from code not owned by dyld
//
int main()
{
- void* codeBlock = malloc(4096);
- memcpy(codeBlock, &calldlopen, 4096);
+ // now try to create a page where foo() was
+ vm_address_t addr = 0;
+ kern_return_t r = vm_allocate(mach_task_self(), &addr, 4096, VM_FLAGS_ANYWHERE);
+ if ( r != KERN_SUCCESS ) {
+ FAIL("vm_allocate returned %d", r);
+ return 0;
+ }
+ void* codeBlock = (void*)(addr);
+ memcpy(codeBlock, START_OF_FUNC(calldlopen), 4096);
sys_icache_invalidate(codeBlock, 4096);
mprotect(codeBlock, 4096, PROT_READ | PROT_EXEC);
//fprintf(stderr, "codeBlock=%p\n", codeBlock);
- void* (*caller)(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) = codeBlock;
+ void* (*caller)(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) = ADDR_FROM_BLOCK(codeBlock);
void* handle = (*caller)("foo.bundle", RTLD_LAZY, &dlopen);
if ( handle == NULL ) {
+#include <string.h>
+
void bar()
{
+ strcpy(NULL, NULL);
}
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
+
+clean:
+ ${RM} ${RMFLAGS} main
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdlib.h> // EXIT_SUCCESS
+#include <stdio.h>
+#include <dlfcn.h>
+#include <Availability.h>
+
+#include "test.h"
+
+static void tryPath(const char* path)
+{
+ if ( dlopen_preflight(path) ) {
+ void* handle = dlopen(path, RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("dlopen-non-canonical-path: dlopen(%s)", path);
+ exit(0);
+ }
+ }
+ else {
+ FAIL("dlopen-non-canonical-path: dlopen_preflight(%s)", path);
+ exit(0);
+ }
+}
+
+//
+// <rdar://problem/7017050> dlopen() not opening frameworks with non-canonical paths
+//
+
+int main()
+{
+ tryPath("//usr/lib/libSystem.B.dylib");
+ tryPath("/usr/bin/../lib/libSystem.B.dylib");
+ tryPath("/usr/lib/./libSystem.B.dylib");
+ tryPath("/usr/lib//libSystem.B.dylib");
+
+ PASS("dlopen-non-canonical-path");
+ return EXIT_SUCCESS;
+}
+
+
all: main
main : main.c libfoo.dylib
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -mmacosx-version-min=10.4
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wno-deprecated-declarations -o main main.c
libfoo.dylib : foo.c
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <dlfcn.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
static void notify(const struct mach_header *mh, intptr_t vmaddr_slide)
{
+// NSLookupSymbolInImage is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
//fprintf(stderr, "mh=%p\n", mh);
NSLookupSymbolInImage(mh, "_bar", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY | NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR);
+#endif
}
--- /dev/null
+##
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+#
+# verify there are no leaks with dlopen/close on success and failure
+#
+
+# leaks does not work on rosetta processes
+CHECK = check-real
+ifeq "ppc" "$(ARCH)"
+ MACHINE = $(shell arch)
+ ifeq "i386" "$(MACHINE)"
+ CHECK = check-xfail
+ endif
+endif
+
+all-check: all check
+
+check: ${CHECK}
+
+check-real:
+ DYLD_LIBRARY_PATH=hide:other:places && ./main | grep '0 leaks for 0 total leaked bytes' > /dev/null && echo "PASS dlopen-search-leak"
+
+check-xfail:
+ echo "XFAIL dlopen-leak";
+
+
+all: main
+
+
+libfoo.dylib : foo.c
+ ${CC} foo.c -dynamiclib -o libfoo.dylib
+
+hide/libfoo.dylib :
+ mkdir hide
+ touch hide/libfoo.dylib
+
+other/libfoo.dylib :
+ mkdir other
+ touch other/libfoo.dylib
+
+main : main.c libfoo.dylib hide/libfoo.dylib other/libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main
+
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main libfoo.dylib hide other
--- /dev/null
+void foo()
+{
+}
--- /dev/null
+/*
+ * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+#include "test.h"
+
+
+int main()
+{
+ for (int i=0; i < 100; ++i) {
+ void* handle = dlopen("libfoo.dylib", RTLD_LAZY);
+ if ( handle != NULL )
+ dlclose(handle);
+ }
+
+ // execute leaks command on myself
+ char cmd[512];
+ sprintf(cmd, "leaks %u\n", getpid());
+ system(cmd);
+
+ return EXIT_SUCCESS;
+}
+++ /dev/null
-##
-# Copyright (c) 2009 Apple Inc. All rights reserved.
-#
-# @APPLE_LICENSE_HEADER_START@
-#
-# This file contains Original Code and/or Modifications of Original Code
-# as defined in and that are subject to the Apple Public Source License
-# Version 2.0 (the 'License'). You may not use this file except in
-# compliance with the License. Please obtain a copy of the License at
-# http://www.opensource.apple.com/apsl/ and read it before using this
-# file.
-#
-# The Original Code and all software distributed under the License are
-# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
-# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
-# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-# Please see the License for the specific language governing rights and
-# limitations under the License.
-#
-# @APPLE_LICENSE_HEADER_END@
-##
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-all-check: all check
-
-check:
- ./main
-
-all: main
-
-main: main.c libbar.dylib
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
-
-libbar.dylib : bar.c
- ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib -lz
-
-
-clean:
- ${RM} ${RMFLAGS} *~ main libbar.dylib
-
+++ /dev/null
-/*
- * Copyright (c) 2009 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-#include <stdbool.h> // fprintf(), NULL
-#include <stdio.h> // fprintf(), NULL
-#include <stdlib.h> // exit(), EXIT_SUCCESS
-#include <stdbool.h>
-#include <string.h>
-#include <mach-o/dyld.h>
-#include <mach-o/dyld_priv.h>
-#include <dlfcn.h>
-
-#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
-
-
-
-int main(int argc, const char* argv[])
-{
- // test that libbar which links with libz.dylib preflights
- if ( ! dlopen_preflight("./libbar.dylib") ) {
- FAIL("dlopen_preflight-basic libbar.dylib should be loadable, but got: %s", dlerror());
- exit(0);
- }
-
- // test that libz.dylib itself preflights
- if ( ! dlopen_preflight("/usr/lib/libz.dylib") ) {
- FAIL("dlopen_preflight-basic /usr/lib/libz.dylib should be loadable, but got: %s", dlerror());
- exit(0);
- }
-
- // verify libbar and libz are no longer loaded
- uint32_t count = _dyld_image_count();
- for (uint32_t i=0; i < count; ++i) {
- const char* path = _dyld_get_image_name(i);
- if ( strstr(path, "libz.") != NULL ) {
- FAIL("dlopen_preflight-shared-cache: %s is loaded", path);
- exit(0);
- }
- }
-
-
- PASS("dlopen_preflight-shared-cache");
-
- return EXIT_SUCCESS;
-}
# Test that dtrace probes fire
#
+ifeq "$(OS_NAME)" "iPhoneOS"
+ SUDO =
+else
+ SUDO = sudo
+endif
+
all-check: all check
check:
- if [ `sudo dtrace -q -n 'Foo$$target:::count { printf("%u\n", arg0); } ' -c ./main | wc -w` == 5 ]; \
+ if [ `${SUDO} /usr/sbin/dtrace -q -n 'Foo$$target:::count { printf("%u\n", arg0); } ' -c ./main | wc -w` == 5 ]; \
then \
echo "PASS dtrace-static-probes"; \
else \
all: main test.bundle test.dylib
main : main.c foo.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c
test.bundle : foo.c
- ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle
test.dylib : foo.c
- ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
*/
#include <stdbool.h>
+#include <Availability.h>
+
+
+// _dyld_func_lookup is only available in 10.5 and earlier
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5)
extern bool _dyld_func_lookup(const char* dyld_func_name, void** address);
// looks good
return true;
}
+
+#endif
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <dlfcn.h>
#include <stdbool.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+// _dyld_func_lookup is only available in 10.5 and earlier
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5)
extern bool check_dyld_func_lookup();
typedef bool (*proc)(void);
dlclose(handle);
}
-
+#endif
int main()
{
+// _dyld_func_lookup is only available in 10.5 and earlier
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5)
if ( ! check_dyld_func_lookup() )
FAIL("check_dyld_func_lookup failed for main executable");
trySO("test.bundle");
trySO("test.dylib");
-
+#endif
+
PASS("dyld-func-lookup");
return EXIT_SUCCESS;
}
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h> // fprintf(), NULL
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <mach-o/dyld.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
int
main(int argc, const char* argv[])
{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
_dyld_launched_prebound();
+#endif
return EXIT_SUCCESS;
}
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
include ${TESTROOT}/include/common.makefile
STACK_BASE = 0x8fe01000
+STACK_SIZE = 0x00100000
ifeq "armv6" "$(ARCH)"
STACK_BASE = 0x2fe01000
+ STACK_SIZE = 0x00100000
+endif
+ifeq "armv7" "$(ARCH)"
+ STACK_BASE = 0x2fe01000
+ STACK_SIZE = 0x00100000
endif
ifeq "ppc64" "$(ARCH)"
- STACK_BASE = 0x7fff5fc00000
+ STACK_BASE = 0x7fff5ff00000
+ STACK_SIZE = 0x00400000
endif
ifeq "x86_64" "$(ARCH)"
- STACK_BASE = 0x7fff5fc00000
+ STACK_BASE = 0x7fff5ff00000
+ STACK_SIZE = 0x00400000
endif
${TESTROOT}/bin/exit-zero-pass.pl "dyld did slide" "dyld did not slide" ./main
all:
- ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c -Wl,-stack_addr,${STACK_BASE} -Wl,-stack_size,0x00100000
+ ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c -Wl,-stack_addr,${STACK_BASE} -Wl,-stack_size,${STACK_SIZE}
clean:
${RM} ${RMFLAGS} main
/*
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <stdlib.h> // EXIT_SUCCESS
-#include <mach-o/dyld.h>
+#include <dlfcn.h>
+#include <mach-o/dyld_priv.h>
#include "test.h"
int
main()
{
- // call a dyld function that will internally throw an exception to test dyld slide properly
- NSAddImage("/foo/bar", NSADDIMAGE_OPTION_RETURN_ON_ERROR);
-
+ // call a dyld function that will execute lots of code and bus error dyld was not slid
+ dlsym(RTLD_DEFAULT, "foobar");
+
return EXIT_SUCCESS;
}
##
-# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
#
# @APPLE_LICENSE_HEADER_END@
##
-TESTROOT = ../..
+PWD = $(shell pwd)
+TESTROOT = $(PWD)/../..
include ${TESTROOT}/include/common.makefile
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
+
all-check: all check
check:
- ${TESTROOT}/bin/exit-non-zero-pass.pl "fallback-with-suid" "fallback-with-suid" ./main-suid
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "fallback-with-suid" "fallback-with-suid" $(PWD)/main-suid
all: main-suid
##
-# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
libfoo.dylib : foo.c libbar.dylib
- ${CC} ${CCFLAGS} -mmacosx-version-min=10.2 -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000
libbar.dylib : bar.c
- ${CC} ${CCFLAGS} -mmacosx-version-min=10.2 -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000
int
main(int argc, const char* argv[])
{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
const struct mach_header *image;
image = NSAddImage("AppKit.framework/AppKit",
NSADDIMAGE_OPTION_RETURN_ON_ERROR | NSADDIMAGE_OPTION_WITH_SEARCHING);
- if ( image != NULL )
- PASS("AppKit loaded");
- else
+ if ( image == NULL )
FAIL("Could not load AppKit");
+ else
+#endif
+ PASS("AppKit loaded");
return 0;
}
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -I../../../include
+
+clean:
+ ${RM} ${RMFLAGS} main libfoo.dylib
--- /dev/null
+void foo() {}
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdlib.h> // EXIT_SUCCESS
+#include <stdio.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld_images.h>
+
+#include "test.h"
+
+extern struct mach_header __dso_handle;
+
+
+struct dyld_all_image_infos* getImageInfosFromKernel()
+{
+ task_dyld_info_data_t task_dyld_info;
+ mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
+
+ if ( task_info(mach_task_self(), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) {
+ FAIL("all_image_infos: task_info() failed");
+ exit(0);
+ }
+ return (struct dyld_all_image_infos*)(uintptr_t)task_dyld_info.all_image_info_addr;
+}
+
+
+int
+main()
+{
+ struct dyld_all_image_infos* info = getImageInfosFromKernel();
+ if ( info->version < 10 ) {
+ FAIL("image-count: dyld_all_image_infos is < 10");
+ exit(0);
+ }
+
+ if ( info->infoArrayCount != info->initialImageCount ) {
+ FAIL("image-count: dyld_all_image_infos.infoArrayCount != dyld_all_image_infos.initialImageCount");
+ exit(0);
+ }
+
+ void* h = dlopen("libfoo.dylib", RTLD_LAZY);
+ if ( h == NULL ) {
+ FAIL("image-count: dyld_all_image_infos is < 10");
+ exit(0);
+ }
+
+ if ( info->infoArrayCount != (info->initialImageCount+1) ) {
+ FAIL("image-count: infoArrayCount did not increment when libfoo.dylib was loaded");
+ exit(0);
+ }
+
+ dlclose(h);
+
+ if ( info->infoArrayCount != info->initialImageCount ) {
+ FAIL("image-count: infoArrayCount did not decrement when libfoo.dylib was unloaded");
+ exit(0);
+ }
+
+ PASS("image-count");
+ return EXIT_SUCCESS;
+}
+
+
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
+
static bool doneRegistering = false;
//
}
}
}
+#endif
int main(int argc, const char* argv[])
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// tell dyld we want to know when images successfully loaded
dyld_register_image_state_change_handler(dyld_image_state_initialized, false, singleInitializedHandler);
doneRegistering = true;
}
// dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY);
+#endif
PASS("image-state-deny");
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
}
uint32_t imageCount = _dyld_image_count();
- const struct mach_header* mh = NSAddImage(argv[1], 0);
+ dlopen(argv[1], 0);
if ( imageCount != _dyld_image_count() ) {
FAIL("image count changed");
return EXIT_SUCCESS;
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include bar.c -dynamiclib -o libbar.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include foo1.c foo2.c libbar.dylib -dynamiclib -o libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib libbar.dylib -o main
+
+clean:
+ ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib
+
--- /dev/null
+
+int bar = 0;
+
+void altSecondInit()
+{
+ bar = 1;
+}
--- /dev/null
+#include <stdio.h>
+
+extern void* initializers[] __asm__("section$start$__DATA$__mod_init_func");
+
+extern void altSecondInit();
+
+
+__attribute__((constructor))
+void firstInit()
+{
+ // slam second initializer to be pointer into another dylib
+ initializers[1] = &altSecondInit;
+}
+
--- /dev/null
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+__attribute__((constructor))
+void secondInit()
+{
+ FAIL("initializer-bounds-check, second init called");
+ exit(0);
+}
+
--- /dev/null
+/*
+ * 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 <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+extern int bar;
+
+int
+main()
+{
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ if ( bar == 1 )
+ FAIL("initializer-bounds-check, out of bounds initializer called");
+ else
+ PASS("initializer-bounds-check");
+#else
+ PASS("initializer-bounds-check");
+#endif
+ return EXIT_SUCCESS;
+}
##
-# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
+PWD = `pwd`
+
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
+
all-check: all check
check:
- export DYLD_INSERT_LIBRARIES="/usr/lib/libldap.dylib:/usr/lib/libpcap.dylib" && ./main
+ ${RUN_AS_USER} $(PWD)/main-with-env
all: main
${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
sudo chown root main
sudo chmod 4755 main
+ echo "#!/bin/sh" > main-with-env
+ echo "export DYLD_INSERT_LIBRARIES=/usr/lib/libz.dylib" >> main-with-env
+ echo "$(PWD)/main" >> main-with-env
+ chmod +x main-with-env
clean:
- ${RM} ${RMFLAGS} *~ main
+ ${RM} ${RMFLAGS} *~ main main-with-env
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <stdlib.h>
#include <mach-o/dyld.h>
+#include <Availability.h>
+#include <dlfcn.h>
#include "test.h"
int main()
{
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_5)
NSAddImage("@loader_path/hide/libfoo3.dylib", 0);
-
- PASS("loader_path");
+#else
+ if ( dlopen("@loader_path/hide/libfoo3.dylib", 0) == NULL )
+ FAIL("loader_path");
+ else
+#endif
+ PASS("loader_path");
return EXIT_SUCCESS;
}
int main()
{
+// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// load bundle which indirectly loads libfoo and libbar
NSObjectFileImage ofi;
if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) {
if ( mh != NULL ) {
return 1;
}
-
+#endif
#if 0
// find foo
NSSymbol sym = NSLookupSymbolInImage(mh, "_foo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND);
+++ /dev/null
-
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-
-#
-# <rdar://problem/5807857> there should be some way to temporarily turn off -pie
-#
-# run a PIE four times and verify its load address was the same every time
-#
-
-all-check: all check
-
-check:
- export DYLD_NO_PIE=1 && ./main > main.out
- export DYLD_NO_PIE=1 && ./main >> main.out
- export DYLD_NO_PIE=1 && ./main >> main.out
- export DYLD_NO_PIE=1 && ./main >> main.out
- if [ `sort main.out -u | wc -l` == 1 ]; \
- then \
- echo "PASS pie-DYLD_NO_PIE"; \
- else \
- echo "FAIL pie-DYLD_NO_PIE"; \
- fi; \
-
-all: main
-
-main : main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c
-
-
-clean:
- ${RM} ${RMFLAGS} *~ main main.out
-
+++ /dev/null
-/*
- * Copyright (c) 2007 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-#include <stdio.h>
-#include <stdbool.h>
-
-int main()
-{
- //int local;
-
- printf("&main=%p\n", &main);
- //printf("&local=%p\n",&local);
- return 0;
-}
all: main
main : main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c
clean:
all: main
main : main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c
clean:
*/
#include <stdio.h>
#include <stdbool.h>
+#include <Availability.h>
-char bigarray[1500000000];
-
+#if __IPHONE_OS_VERSION_MIN_REQUIRED
+char bigarray[0x10000000]; // 0.25GB
+#else
+ #if __LP64__
+ char bigarray[0xF0000000]; // 4GB
+ #else
+ char bigarray[0x30000000]; // 0.75GB
+ #endif
+#endif
int main()
{
all: main
main : main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -mmacosx-version-min=10.5 -Wl,-stack_size,0x10000000
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -Wl,-stack_size,0x10000000
clean:
##
-# Copyright (c) 2007-2008 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
./main >> main.out
if [ `sort main.out -u | wc -l` == 4 ]; \
then \
- echo "PASS pie-basic"; \
+ echo "PASS pie-text-reloc"; \
else \
- echo "FAIL pie-basic"; \
+ echo "FAIL pie-text-reloc"; \
fi; \
all: main
main : main.c
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie $(EXTRA_OPTIONS) -o main main.c -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie $(EXTRA_OPTIONS) -o main main.c
clean:
all: main
main: main.c libfoo.dylib
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib -Wl,-no_pie
libfoo.dylib: foo.c
- ${CC} ${CCFLAGS} -dynamiclib -o libfoo.dylib foo.c -seg1addr ${DYLIB_BASES_ADDRESS} -mmacosx-version-min=10.5
+ ${CC} ${CCFLAGS} -dynamiclib -o libfoo.dylib foo.c -seg1addr ${DYLIB_BASES_ADDRESS}
clean:
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
#
-# Test that the 10.4 and 10.5 ways to re-export a dylib work
+# Test that dylib re-exports work
#
all-check: all check
check:
- ./main4
- ./main5
+ ./main
-all: main4 main5
+all: main
-libbar4.dylib : bar.c
- ${CC} bar.c -dynamiclib -o $(PWD)/libbar4.dylib -mmacosx-version-min=10.4
-libfoo4.dylib : foo.c libbar4.dylib
- ${CC} foo.c -dynamiclib libbar4.dylib -sub_library libbar4 -install_name $(PWD)/libfoo4.dylib -o libfoo4.dylib -mmacosx-version-min=10.4
-main4 : main.c libfoo4.dylib
- ${CC} main.c -I${TESTROOT}/include -o main4 libfoo4.dylib -mmacosx-version-min=10.4
+libbar.dylib : bar.c
+ ${CC} bar.c -dynamiclib -o $(PWD)/libbar.dylib
+libfoo.dylib : foo.c libbar.dylib
+ ${CC} foo.c -dynamiclib libbar.dylib -sub_library libbar -install_name $(PWD)/libfoo.dylib -o libfoo.dylib
-
-libbar5.dylib : bar.c
- ${CC} bar.c -dynamiclib -o $(PWD)/libbar5.dylib -mmacosx-version-min=10.5
-
-libfoo5.dylib : foo.c libbar5.dylib
- ${CC} foo.c -dynamiclib libbar5.dylib -sub_library libbar5 -install_name $(PWD)/libfoo5.dylib -o libfoo5.dylib -mmacosx-version-min=10.5
-
-main5 : main.c libfoo5.dylib
- ${CC} main.c -I${TESTROOT}/include -o main5 libfoo5.dylib -mmacosx-version-min=10.5
+main : main.c libfoo.dylib
+ ${CC} main.c -I${TESTROOT}/include -o main libfoo.dylib
clean:
- rm -rf main4 main5 libfoo4.dylib libfoo5.dylib libbar4.dylib libbar5.dylib
+ rm -rf main libfoo.dylib libbar.dylib
\ No newline at end of file
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
#
-# Test that the 10.4 and 10.5 ways to re-export a framework work
+# Test re-exported frameworks work
#
all-check: all check
check:
- ./main4
- ./main5
+ ./main
-all: main4 main5
+all: main
-Bar4.framework/Bar4 : bar.c
- mkdir -p Bar4.framework
- ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar4.framework/Bar4 -o Bar4.framework/Bar4 -mmacosx-version-min=10.4
-Foo4.framework/Foo4 : foo.c Bar4.framework/Bar4
- mkdir -p Foo4.framework
- ${CC} foo.c -dynamiclib Bar4.framework/Bar4 -sub_umbrella Bar4 -install_name $(PWD)/Foo4.framework/Foo4 -o Foo4.framework/Foo4 -mmacosx-version-min=10.4
-main4 : main.c Foo4.framework/Foo4
- ${CC} main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4
+Bar.framework/Bar : bar.c
+ mkdir -p Bar.framework
+ ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar.framework/Bar -o Bar.framework/Bar
+Foo.framework/Foo : foo.c Bar.framework/Bar
+ mkdir -p Foo.framework
+ ${CC} foo.c -dynamiclib Bar.framework/Bar -sub_umbrella Bar -install_name $(PWD)/Foo.framework/Foo -o Foo.framework/Foo
-
-Bar5.framework/Bar5 : bar.c
- mkdir -p Bar5.framework
- ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar5.framework/Bar5 -o Bar5.framework/Bar5 -mmacosx-version-min=10.5
-
-Foo5.framework/Foo5 : foo.c Bar5.framework/Bar5
- mkdir -p Foo5.framework
- ${CC} foo.c -dynamiclib Bar5.framework/Bar5 -sub_umbrella Bar5 -install_name $(PWD)/Foo5.framework/Foo5 -o Foo5.framework/Foo5 -mmacosx-version-min=10.5
-
-main5 : main.c Foo5.framework/Foo5
- ${CC} main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5
+main : main.c Foo.framework/Foo
+ ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F.
clean:
- rm -rf main4 Foo4.framework Bar4.framework main5 Foo5.framework Bar5.framework
+ rm -rf main Foo.framework Bar.framework
\ No newline at end of file
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
#
-# Test that the 10.4 and 10.5 ways to re-export a sub framework work
+# Test that the ways to re-export a sub framework work
#
all-check: all check
check:
- ./main4
- ./main5
+ ./main
-all: main4 main5
+all: main
-Bar4.framework/Bar4 : bar.c
- mkdir -p Bar4.framework
- ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar4.framework/Bar4 -o Bar4.framework/Bar4 -umbrella Foo4 -mmacosx-version-min=10.4
-Foo4.framework/Foo4 : foo.c Bar4.framework/Bar4
- mkdir -p Foo4.framework
- ${CC} foo.c -dynamiclib Bar4.framework/Bar4 -install_name $(PWD)/Foo4.framework/Foo4 -o Foo4.framework/Foo4 -mmacosx-version-min=10.4
+Bar.framework/Bar : bar.c
+ mkdir -p Bar.framework
+ ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar.framework/Bar -o Bar.framework/Bar -umbrella Foo
-main4 : main.c Foo4.framework/Foo4
- ${CC} main.c -I${TESTROOT}/include -o main4 -framework Foo4 -F. -mmacosx-version-min=10.4
+Foo.framework/Foo : foo.c Bar.framework/Bar
+ mkdir -p Foo.framework
+ ${CC} foo.c -dynamiclib Bar.framework/Bar -install_name $(PWD)/Foo.framework/Foo -o Foo.framework/Foo
-
-Bar5.framework/Bar5 : bar.c
- mkdir -p Bar5.framework
- ${CC} bar.c -dynamiclib -install_name $(PWD)/Bar5.framework/Bar5 -o Bar5.framework/Bar5 -umbrella Foo5 -mmacosx-version-min=10.5
-
-Foo5.framework/Foo5 : foo.c Bar5.framework/Bar5
- mkdir -p Foo5.framework
- ${CC} foo.c -dynamiclib Bar5.framework/Bar5 -install_name $(PWD)/Foo5.framework/Foo5 -o Foo5.framework/Foo5 -mmacosx-version-min=10.5
-
-main5 : main.c Foo5.framework/Foo5
- ${CC} main.c -I${TESTROOT}/include -o main5 -framework Foo5 -F. -mmacosx-version-min=10.5
+main : main.c Foo.framework/Foo
+ ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F.
clean:
- rm -rf main4 Foo4.framework Bar4.framework main5 Foo5.framework Bar5.framework
+ rm -rf main Foo.framework Bar.framework
\ No newline at end of file
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+
+#
+# Test that fine grain re-exports works
+#
+
+all-check: all_$(OS_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES)
+
+all: all_$(OS_BAROLO_FEATURES)
+
+check: check_$(OS_BAROLO_FEATURES)
+
+check_1:
+ ./main1
+ ./main2
+
+all_1:
+ # build base library
+ ${CC} ${CCFLAGS} -dynamiclib bar.c -o `pwd`/libbar.dylib
+
+ # build library the re-exports _bar from base library
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -exported_symbols_list foo.exp
+ # link against dylib and verify _bar is marked as coming from libfoo
+ ${CC} ${CCFLAGS} main1.c -I${TESTROOT}/include libfoo.dylib -o main1
+
+ # build library the re-exports _bar from base library as _mybar
+ ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo2.dylib libbar.dylib -Wl,-alias,_bar,_mybar -exported_symbols_list foo2.exp
+ # link against dylib and verify _mybar is marked as coming from libfoo
+ ${CC} ${CCFLAGS} main2.c -I${TESTROOT}/include libfoo2.dylib -o main2
+
+check_:
+ ${PASS_IFF} true
+
+all_:
+
+clean:
+ rm -rf libbar.dylib libfoo.dylib libfoo2.dylib main1 main2
--- /dev/null
+
+int bar(void)
+{
+ return 10;
+}
--- /dev/null
+int foo(void)
+{
+ return 10;
+}
--- /dev/null
+_foo
+_bar
--- /dev/null
+_foo
+_mybar
--- /dev/null
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+extern int foo();
+extern int bar();
+
+int (*pbar)() = &bar;
+
+
+int main()
+{
+ if ( foo() != 10 )
+ FAIL("re-export-symbol: foo() returned wrong value");
+ if ( bar() != 10 )
+ FAIL("re-export-symbol: bar() returned wrong value");
+ if ( (*pbar)() != 10 )
+ FAIL("re-export-symbol: (*pbar)() returned wrong value");
+ PASS("re-export-symbol");
+ return 0;
+}
--- /dev/null
+
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+
+extern int foo();
+extern int mybar();
+
+int (*pmybar)() = &mybar;
+
+int main()
+{
+ if ( foo() != 10 )
+ FAIL("re-export-symbol: foo() returned wrong value");
+ if ( mybar() != 10 )
+ FAIL("re-export-symbol: mybar() returned wrong value");
+ if ( (*pmybar)() != 10 )
+ FAIL("re-export-symbol: (*pmybar)() returned wrong value");
+ PASS("re-export-symbol");
+ return 0;
+}
+
+++ /dev/null
-##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
-#
-# @APPLE_LICENSE_HEADER_START@
-#
-# This file contains Original Code and/or Modifications of Original Code
-# as defined in and that are subject to the Apple Public Source License
-# Version 2.0 (the 'License'). You may not use this file except in
-# compliance with the License. Please obtain a copy of the License at
-# http://www.opensource.apple.com/apsl/ and read it before using this
-# file.
-#
-# The Original Code and all software distributed under the License are
-# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
-# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
-# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
-# Please see the License for the specific language governing rights and
-# limitations under the License.
-#
-# @APPLE_LICENSE_HEADER_END@
-##
-TESTROOT = ../..
-include ${TESTROOT}/include/common.makefile
-
-
-all-check: all check
-
-check:
- cp /usr/lib/libSystem.B.dylib ./libSystem.B.dylib
- export DYLD_LIBRARY_PATH=`pwd` && ./main
-
-all: main
-
-main: main.c libfoo.dylib
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
-
-libfoo.dylib : foo.c
- ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c -framework CoreFoundation
-
-
-
-clean:
- ${RM} ${RMFLAGS} *~ main libSystem.B.dylib libfoo.dylib
-
-
+++ /dev/null
-
-#include <strings.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-#include "test.h"
-
-void __attribute__((constructor)) init()
-{
- uintptr_t libSysFuncAddr = (uintptr_t)&strcmp;
- uintptr_t cfFuncAddr = (uintptr_t)&CFStringGetTypeID;
- //fprintf(stderr, "libSysFuncAddr=0x%0lX, cfFuncAddr=0x%0lX\n", libSysFuncAddr, cfFuncAddr);
- if ( cfFuncAddr - libSysFuncAddr < 256*1024*1024 )
- FAIL("read-only-import-shared-cache-override");
- else
- PASS("read-only-import-shared-cache-override");
-}
-
-
-
+++ /dev/null
-#include <dlfcn.h>
-#include "test.h"
-
-int main()
-{
-#if __arm__
- // no shared cache on iPhone, so skip test
- PASS("read-only-import-shared-cache-override");
-#else
- // dynamically load libfoo.dylib which depends on libstdc++.dylib
- // being re-bound to libfoo's operator new.
- dlopen("libfoo.dylib", RTLD_LAZY);
-#endif
- return 0;
-}
-
#elif __x86_64__
return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide;
#elif __arm__
- return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide;
+ void* p = getsectdata("__TEXT", "__picsymbolstub4", (unsigned long*)&size);
+ if ( p != NULL )
+ return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide;
+ return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__symbolstub1", &size) + slide;
#else
#error unknown arch
#endif
#elif __x86_64__
return getsectdata("__TEXT", "__symbol_stub1", &size);
#elif __arm__
- return getsectdata("__TEXT", "__symbol_stub4", &size);
+ void* p = getsectdata("__TEXT", "__symbol_stub4", &size);
+ if ( p != NULL )
+ return p;
+ return getsectdata("__TEXT", "__symbolstub1", &size);
#else
#error unknown arch
#endif
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
+
+PWD = `pwd`
+
all-check: all check
check:
- export DYLD_LIBRARY_PATH=/ && export DYLD_PRINT_LIBRARIES=/ && export DYLD_PREBIND_DEBUG=/ && ./main
+ ${RUN_AS_USER} $(PWD)/main-with-env
all: main
main: main.c
${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c -sectcreate __RESTRICT __restrict /dev/null
+ echo "#!/bin/sh" > main-with-env
+ echo "export DYLD_INSERT_LIBRARIES=/" >> main-with-env
+ echo "export DYLD_PRINT_LIBRARIES=/" >> main-with-env
+ echo "$(PWD)/main" >> main-with-env
+ chmod +x main-with-env
clean:
- ${RM} ${RMFLAGS} *~ main
+ ${RM} ${RMFLAGS} *~ main main-with-env
#
# @APPLE_LICENSE_HEADER_END@
##
-TESTROOT = ../..
+PWD = $(shell pwd)
+TESTROOT = $(PWD)/../..
include ${TESTROOT}/include/common.makefile
SHELL = bash # use bash shell so we can redirect just stderr
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
#
# Use of @exectuable_path in restricted binaries is not allowed
all-check: all check
check:
- ${TESTROOT}/bin/exit-non-zero-pass.pl "restrict-executable_path @executable_path" "restrict-executable_path @executable_path" ./main_exe "restrict-executable_path"
- ${TESTROOT}/bin/exit-non-zero-pass.pl "restrict-executable_path @loader_path" "restrict-executable_path @loader_path" ./main_loader "restrict-executable_path"
- ${TESTROOT}/bin/exit-non-zero-pass.pl "restrict-executable_path relative path" "restrict-executable_path relative path" ./main_rel "restrict-executable_path"
-
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @executable_path" "setuid-executable_path @executable_path" $(PWD)/main_exe
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @loader_path" "setuid-executable_path @loader_path" $(PWD)/main_loader
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path relative path" "setuid-executable_path relative path" $(PWD)/main_rel
all: main_exe main_loader main_rel
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+SHELL = bash # use bash shell so we can redirect just stderr
+
+
+PWD = $(shell pwd)
+
+#
+# a main executable linked with -rpath. At runtime the exectuable
+# deletes itself than calls dlopen(). Test that @executable_path
+# does not cause malloc to abort.
+#
+
+all-check: all check
+
+check:
+ cp main main.rm
+ ${TESTROOT}/bin/exit-zero-pass.pl "rpath-dlopen-rm-executable" "rpath-dlopen-rm-executable" ./main.rm 2> /dev/null
+
+all: main
+
+
+main : main.c
+ ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@executable_path/hide/hole
+
+clean:
+ ${RM} ${RMFLAGS} *~ main main.rm
--- /dev/null
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+void foo()
+{
+}
--- /dev/null
+/*
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <mach-o/dyld.h>
+
+#include "test.h"
+
+
+int main()
+{
+ char buf[2048];
+ uint32_t bufSize = sizeof(buf);
+ if ( _NSGetExecutablePath(buf, &bufSize) ) {
+ FAIL("rpath-dlopen-rm-exectuable: _NSGetExecutablePath()");
+ return EXIT_SUCCESS;
+ }
+
+ unlink(buf);
+
+ void* handle = dlopen("libz.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("rpath-dlopen-rm-exectuable: %s", dlerror());
+ return EXIT_SUCCESS;
+ }
+
+ PASS("rpath-dlopen-rm-exectuable");
+ return EXIT_SUCCESS;
+}
##
-# Copyright (c) 2007 Apple Inc. All rights reserved.
+# Copyright (c) 2007-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
#
# @APPLE_LICENSE_HEADER_END@
##
-TESTROOT = ../..
+PWD = $(shell pwd)
+TESTROOT = $(PWD)/../..
include ${TESTROOT}/include/common.makefile
-PWD = $(shell pwd)
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
#
# a setuid main executable linked with -rpath links against a dylib
check:
./main || echo "FAIL rpath-indirect-suid absolute path"
- ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid @loader_path path" "rpath-indirect-suid @loader_path path" ./main_bad1
- ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid relative path" "rpath-indirect-suid relative path" ./main_bad2
- ${TESTROOT}/bin/exit-non-zero-pass.pl "rpath-indirect-suid @rpath spoof" "rpath-indirect-suid @rpath spoof" ./main_bad3
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "rpath-indirect-suid @loader_path path" "rpath-indirect-suid @loader_path path" $(PWD)/main_bad1
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "rpath-indirect-suid relative path" "rpath-indirect-suid relative path" $(PWD)/main_bad2
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "rpath-indirect-suid @rpath spoof" "rpath-indirect-suid @rpath spoof" $(PWD)/main_bad3
all: main main_bad1 main_bad2 main_bad3
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+PWD = $(shell pwd)
+
+#
+# check that a loaded dylib with an @rpath install name will
+# be found by a client.
+#
+
+all-check: all check
+
+check:
+ ./main
+
+all: main
+
+libstuff.dylib : stuff.c
+ ${CC} stuff.c -I${TESTROOT}/include -dynamiclib -o libstuff.dylib -install_name @rpath/libstuff.dylib
+
+libstuff_better.dylib : stuff.c
+ ${CC} stuff.c -DBETTER=1 -I${TESTROOT}/include -dynamiclib -o libstuff_better.dylib -install_name @rpath/libstuff.dylib
+
+libbar.dylib : bar.c libstuff.dylib
+ ${CC} bar.c -dynamiclib libstuff.dylib -o libbar.dylib
+
+
+main : main.c libbar.dylib libstuff_better.dylib
+ ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath,${PWD}
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main libbar.dylib libstuff.dylib libstuff_better.dylib
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+extern void stuff();
+
+void bar()
+{
+ stuff();
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include "test.h"
+
+
+int main()
+{
+ void* h1 = dlopen("./libstuff_better.dylib", RTLD_LAZY);
+ if ( h1 == NULL ) {
+ FAIL("rpath-install-name: %s", dlerror());
+ return EXIT_SUCCESS;
+ }
+
+
+ void* handle = dlopen("libbar.dylib", RTLD_LAZY);
+ if ( handle == NULL ) {
+ FAIL("rpath-install-name: %s", dlerror());
+ return EXIT_SUCCESS;
+ }
+
+ typedef void (*BarProc)(void);
+ BarProc pBar = (BarProc)dlsym(handle, "bar");
+ if ( pBar == NULL ) {
+ FAIL("rpath-install-name: %s", dlerror());
+ return EXIT_SUCCESS;
+ }
+
+ (*pBar)();
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*
+ * Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+#include "test.h"
+
+
+void stuff()
+{
+#if BETTER
+ PASS("rpath-install-name");
+#else
+ FAIL("rpath-install-name");
+#endif
+}
return EXIT_SUCCESS;
}
- // walk images to get slide
- uint32_t count = _dyld_image_count();
- for(uint32_t i=0; i < count; ++i) {
- if ( _dyld_get_image_header(i) == info.dli_fbase ) {
- if ( _dyld_get_image_vmaddr_slide(i) == 0 ) {
- // images in shared cache have a slide of zero
- PASS("shared-cache-symlink");
- return EXIT_SUCCESS;
- }
- else {
- FAIL("shared-cache-symlink: libz.dylib not loaded from shared cache");
- return EXIT_SUCCESS;
- }
- }
- }
+ const struct mach_header* mh = (struct mach_header*)info.dli_fbase;
+ if ( mh->flags & 0x80000000 )
+ PASS("shared-cache-symlink");
+ else
+ FAIL("shared-cache-symlink: libz.dylib not loaded from shared cache");
- FAIL("shared-cache-symlink libz.dylib not found");
return EXIT_SUCCESS;
}
--- /dev/null
+##
+# Copyright (c) 2009 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+# rosetta does not support very large stack sizes
+BASE_ADDRESS = 0x90030000
+ifeq "x86_64" "$(ARCH)"
+ BASE_ADDRESS = 0x7FFF80100000
+endif
+
+
+ifeq "iPhoneOS" "$(OS_NAME)"
+ BASE_ADDRESS = 0x2FF80000
+endif
+
+
+all-check: all check
+
+check:
+ ${TESTROOT}/bin/exit-zero-pass.pl "shared-region-overlap" "shared-region-overlap" ./main
+
+all:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wl,-image_base,${BASE_ADDRESS}
+
+clean:
+ ${RM} ${RMFLAGS} main
--- /dev/null
+#include <stdlib.h> // EXIT_SUCCESS
+
+#include "test.h"
+
+
+int
+main()
+{
+ return EXIT_SUCCESS;
+}
+
+
##
-# Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
TESTROOT = ../..
include ${TESTROOT}/include/common.makefile
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
+
+PWD = `pwd`
+
all-check: all check
check:
- export DYLD_INSERT_LIBRARIES=/ && export DYLD_PRINT_LIBRARIES=/ && export DYLD_PREBIND_DEBUG=/ && ./main
+ ${RUN_AS_USER} $(PWD)/main-with-env
all: main
${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c
sudo chown root main
sudo chmod 4755 main
-
+ echo "#!/bin/sh" > main-with-env
+ echo "export DYLD_INSERT_LIBRARIES=/" >> main-with-env
+ echo "export DYLD_PRINT_LIBRARIES=/" >> main-with-env
+ echo "$(PWD)/main" >> main-with-env
+ chmod +x main-with-env
+
clean:
- ${RM} ${RMFLAGS} *~ main
+ ${RM} ${RMFLAGS} *~ main main-with-env
#
# @APPLE_LICENSE_HEADER_END@
##
-TESTROOT = ../..
+PWD = $(shell pwd)
+TESTROOT = $(PWD)/../..
include ${TESTROOT}/include/common.makefile
SHELL = bash # use bash shell so we can redirect just stderr
+ifeq "$(OS_NAME)" "iPhoneOS"
+ RUN_AS_USER = login -f -l mobile
+else
+ RUN_AS_USER =
+endif
#
# Use of @exectuable_path in setuid binaries is not allowed
check:
./main_exe "setuid-executable_path" || echo "FAIL setuid-executable_path @executable_path not setuid"
- ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path @executable_path" "setuid-executable_path @executable_path" ./main_exe-suid "setuid-executable_path"
./main_loader "setuid-executable_path" || echo "FAIL setuid-executable_path @loader_path not setuid"
- ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path @loader_path" "setuid-executable_path @loader_path" ./main_loader-suid "setuid-executable_path"
./main_rel "setuid-executable_path" || echo "FAIL setuid-executable_path relative path not setuid"
- ${TESTROOT}/bin/exit-non-zero-pass.pl "setuid-executable_path relative path" "setuid-executable_path relative path" ./main_rel-suid "setuid-executable_path"
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @executable_path" "setuid-executable_path @executable_path" $(PWD)/main_exe-suid
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path @loader_path" "setuid-executable_path @loader_path" $(PWD)/main_loader-suid
+ ${RUN_AS_USER} ${PASS_IFF_FAILURE} "setuid-executable_path relative path" "setuid-executable_path relative path" $(PWD)/main_rel-suid
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+##
+## Basic test of symbol-resolver functions
+##
+
+all-check: all_$(OS_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES)
+
+all: all_$(OS_BAROLO_FEATURES)
+
+check: check_$(OS_BAROLO_FEATURES)
+
+check_:
+ ${PASS_IFF} true
+
+all_:
+
+
+check_1:
+ ./main
+ export TEN=1 && ./main
+
+all_1:
+ ${CC} ${CCFLAGS} -dynamiclib foo.c foo2.c -o libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main
+
+
+
+clean:
+ ${RM} ${RMFLAGS} main libfoo.dylib
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+
+
+static int foo_ten()
+{
+ return 10;
+}
+
+static int foo_zero()
+{
+ return 0;
+}
+
+
+// This foo is a "resolver" function that return the actual address of "foo"
+void* foo()
+{
+ __asm__(".symbol_resolver _foo"); // magic until we have compiler support
+ if ( getenv("TEN") != NULL )
+ return &foo_ten;
+ else
+ return &foo_zero;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+
+extern int foo();
+
+// test that calls to resolver based function in same dylib work
+int fooPlusOne()
+{
+ return foo() + 1;
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <dlfcn.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+extern int foo();
+extern int fooPlusOne();
+
+
+int main()
+{
+ if ( getenv("TEN") != NULL ) {
+ if ( foo() != 10 )
+ FAIL("symbol-resolver-basic: foo() != 10");
+ else if ( fooPlusOne() != 11 )
+ FAIL("symbol-resolver-basic: fooPlusOne() != 11");
+ else
+ PASS("symbol-resolver-basic");
+ }
+ else {
+ if ( foo() != 0 )
+ FAIL("symbol-resolver-basic: foo() != 0");
+ else if ( fooPlusOne() != 1 )
+ FAIL("symbol-resolver-basic: fooPlusOne() != 1");
+ else
+ PASS("symbol-resolver-basic");
+ }
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+##
+## Basic test of symbol-resolver functions
+##
+
+all-check: all_$(OS_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES)
+
+all: all_$(OS_BAROLO_FEATURES)
+
+check: check_$(OS_BAROLO_FEATURES)
+
+check_:
+ ${PASS_IFF} true
+
+all_:
+
+
+check_1:
+ ./main
+
+all_1:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main
+
+
+
+clean:
+ ${RM} ${RMFLAGS} main libfoo.dylib
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <stdlib.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+int resolverCallCount = 0;
+int realTestCallCount = 0;
+
+
+void test$FOO();
+void test$FOO() {
+ //printf("test\n");
+ ++realTestCallCount;
+}
+
+void* test_chooser() __asm__("_test");
+void* test_chooser() {
+ __asm__(".symbol_resolver _test");
+ //printf("resolver\n");
+ ++resolverCallCount;
+ return test$FOO;
+}
+
+void test();
+static void (*t)(void) = test;
+
+void check() {
+ t(); // call through initialized pointer
+ t = test; // re-assign pointer via non-lazy-poitner
+ t(); // call agin through pointer
+ test(); // call through stub
+ if ( resolverCallCount != 1 )
+ FAIL("symbol-resolver-pointer: resolved called %d times", resolverCallCount);
+ else if ( realTestCallCount != 3 )
+ FAIL("symbol-resolver-pointer: real test function called %d times", realTestCallCount);
+ else
+ PASS("symbol-resolver-pointer");
+}
+
+
+
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+extern void check();
+
+
+int main()
+{
+ check();
+ return EXIT_SUCCESS;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main
+
+all: main
+
+
+main: main.c libfoo.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main
+
+libfoo.dylib: foo.c
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib -seg1addr 0x20000000
+
+
+
+clean:
+ ${RM} ${RMFLAGS} main libfoo.dylib
+
--- /dev/null
+/*
+ * Copyright (c) 2007-2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <unistd.h>
+#include <mach-o/getsect.h>
+#include <mach-o/ldsyms.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+static vm_prot_t getPermission(void* addr)
+{
+ mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr;
+ kern_return_t result;
+ mach_port_t object_name;
+ vm_region_basic_info_data_64_t info;
+ mach_msg_type_number_t count;
+ mach_vm_size_t size = 4096;
+
+ count = VM_REGION_BASIC_INFO_COUNT_64;
+ result = mach_vm_region(mach_task_self(),
+ &address,
+ &size,
+ VM_REGION_BASIC_INFO_64,
+ (vm_region_info_t)&info,
+ &count,
+ &object_name);
+ if ( result == KERN_SUCCESS )
+ return info.protection;
+ return 0;
+}
+
+
+static void* getStubAddr()
+{
+#if __LP64__
+ uint64_t size;
+#else
+ uint32_t size;
+#endif
+ uintptr_t slide = (uintptr_t)&_mh_dylib_header; // assume dylib is zero-base so slide == load address
+#if __i386__
+ return getsectdatafromheader(&_mh_dylib_header, "__IMPORT", "__symbol_stub", &size) + slide;
+#elif __ppc__
+ return getsectdatafromheader(&_mh_dylib_header, "TEXT", "__picsymbolstub1", &size) + slide;
+#elif __ppc64__
+ return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__picsymbolstub1", &size) + slide;
+#elif __x86_64__
+ return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide;
+#elif __arm__
+ void* p = getsectdata("__TEXT", "__picsymbolstub4", (unsigned long*)&size);
+ if ( p != NULL )
+ return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__picsymbolstub4", &size) + slide;
+ return getsectdatafromheader(&_mh_dylib_header, "__TEXT", "__symbolstub1", &size) + slide;
+#else
+ #error unknown arch
+#endif
+}
+
+
+static void checkStubs(void* addr)
+{
+ vm_prot_t perm = getPermission(addr);
+ //fprintf(stderr, "perm=0x%02X\n", perm);
+ if ( (perm == 0) || ((perm & VM_PROT_EXECUTE) == 0) ) {
+ FAIL("text-reloc-perms: missing exec permission 0x%0X at address %p", perm, addr);
+ exit(0);
+ }
+}
+
+
+void foo()
+{
+ void* stubAddr = getStubAddr();
+ checkStubs(stubAddr);
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2007 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <unistd.h>
+#include <mach-o/getsect.h>
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+extern void foo();
+
+static vm_prot_t getPermission(void* addr)
+{
+ mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr;
+ kern_return_t result;
+ mach_port_t object_name;
+ vm_region_basic_info_data_64_t info;
+ mach_msg_type_number_t count;
+ mach_vm_size_t size = 4096;
+
+ count = VM_REGION_BASIC_INFO_COUNT_64;
+ result = mach_vm_region(mach_task_self(),
+ &address,
+ &size,
+ VM_REGION_BASIC_INFO_64,
+ (vm_region_info_t)&info,
+ &count,
+ &object_name);
+ //fprintf(stderr, "result=%X, info.protection=%X\n", result, info.protection);
+ if ( result == KERN_SUCCESS )
+ return info.protection;
+ return 0;
+}
+
+
+static void* getStubAddr()
+{
+ unsigned long size;
+#if __i386__
+ return getsectdata("__IMPORT", "__jump_table", &size);
+#elif __ppc__
+ void* p = getsectdata("__TEXT", "__picsymbolstub1", &size);
+ if ( p != NULL )
+ return p;
+ return getsectdata("__TEXT", "__symbol_stub1", &size);
+#elif __ppc64__
+ return getsectdata("__TEXT", "__picsymbolstub1", &size);
+#elif __x86_64__
+ return getsectdata("__TEXT", "__symbol_stub1", &size);
+#elif __arm__
+ void* p = getsectdata("__TEXT", "__symbol_stub4", &size);
+ if ( p != NULL )
+ return p;
+ return getsectdata("__TEXT", "__symbolstub1", &size);
+#else
+ #error unknown arch
+#endif
+}
+
+
+static void checkStubs(void* addr)
+{
+ vm_prot_t perm = getPermission(addr);
+ if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) {
+ FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr);
+ exit(0);
+ }
+}
+
+
+int main()
+{
+ foo();
+
+ void* stubAddr = getStubAddr();
+#if __i386__
+ if ( stubAddr != NULL )
+#endif
+ {
+ checkStubs(stubAddr);
+ checkStubs(stubAddr);
+ }
+ PASS("text-relocs-perms");
+ return EXIT_SUCCESS;
+}
+
+
include ${TESTROOT}/include/common.makefile
###
-### This test case is to verify __TEXT reliocations work in dylibs
+### This test case is to verify __TEXT relocations work in dylibs
###
###
# ppc64 does not support text relocs
TEXT_RELOC_FLAGS =
endif
-ifeq "armv6" "$(ARCH)"
- # arm does not support text relocs
- TEXT_RELOC_FLAGS =
-endif
all-check: all check
check:
./main
-all: main
-
-main: main.c libbar.dylib
- ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib
-
-libbar.dylib: bar.c
- ${CC} ${CCFLAGS} -dynamiclib -o libbar.dylib bar.c -Os ${TEXT_RELOC_FLAGS}
+all:
+ ${CC} ${CCFLAGS} -dynamiclib bar.c space.s -Os -o libbar.dylib ${TEXT_RELOC_FLAGS}
+ ${CC} ${CCFLAGS} bind.c -static -Os -c -o bind.o
+ ${CC} ${CCFLAGS} -dynamiclib bind.o libbar.dylib -o libbind.dylib ${TEXT_RELOC_FLAGS}
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib libbind.dylib
clean:
- ${RM} ${RMFLAGS} *~ main libbar.dylib
+ ${RM} ${RMFLAGS} *~ main libbar.dylib libbind.dylib bind.o
#include <stdlib.h> // exit(), EXIT_SUCCESS
#include <stdbool.h>
+int y = 0;
+
static int x = 0;
int getx() { return x; }
void setx(int a) { x = a; }
+void bar()
+{
+ printf("hello\n");
+}
--- /dev/null
+
+extern int y;
+
+int test()
+{
+ return y;
+}
--- /dev/null
+ .text
+_junk: .space 1024*1024
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all check
+
+check:
+ ./main
+
+all: main
+
+main : main.c foo.c
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo1.dylib
+ ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo2.dylib
+ ${CC} ${CCFLAGS} client.c -dynamiclib -o libclient.dylib -flat_namespace -undefined dynamic_lookup
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c
+
+clean:
+ ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libclient.dylib
+
--- /dev/null
+
+extern void foo();
+extern void bar();
+extern void baz();
+
+void doit()
+{
+ foo();
+ bar();
+ baz();
+}
--- /dev/null
+
+void foo() {}
+void bar() {}
+void baz() {}
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <string.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+void* h1 = NULL;
+void* h2 = NULL;
+
+static void* work(void* ignore)
+{
+ for (int i=0; i < 10000; ++i) {
+ h1 = dlopen("libfoo1.dylib", 0);
+ if ( h1 == NULL ) {
+ FAIL("dlopen failed: %s", dlerror());
+ exit(0);
+ }
+ dlclose(h2);
+ h2 = dlopen("libfoo2.dylib", 0);
+ if ( h2 == NULL ) {
+ FAIL("dlopen failed: %s", dlerror());
+ exit(0);
+ }
+ dlclose(h1);
+ }
+
+ //fprintf(stderr, "done with foos\n");
+ return NULL;
+}
+
+
+int main()
+{
+ h2 = dlopen("libfoo2.dylib", 0);
+ if ( h2 == NULL ) {
+ FAIL("dlopen failed: %s", dlerror());
+ exit(0);
+ }
+
+ // other thread dlopens and closes libfoo.dylib 500 times
+ pthread_t other;
+ if ( pthread_create(&other, NULL, work, NULL) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ // this thread looks up a symbol 10,000 times
+ for (int i=0; i < 15000; ++i) {
+ void* handle = dlopen("libclient.dylib", 0);
+ if ( handle == NULL ) {
+ FAIL("dlopen failed: %s", dlerror());
+ exit(0);
+ }
+ typedef void (*proc_t)();
+ proc_t proc = dlsym(handle, "doit");
+ if ( proc == NULL ) {
+ FAIL("dlsym failed: %s", dlerror());
+ exit(0);
+ }
+ (*proc)();
+ dlclose(handle);
+ }
+ //fprintf(stderr, "done with libclient\n");
+
+ void* result;
+ pthread_join(other, &result);
+
+ PASS("threaded-flat-lookup");
+ return 0;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+##
+## Basic test of thread-local-variables in a main executable
+##
+
+all-check: all check
+
+check:
+ ./main
+
+all: main
+
+main : main.c
+ clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c -o main
+
+
+clean:
+ ${RM} ${RMFLAGS} main
+
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <pthread.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+__thread int a;
+__thread int b = 5;
+__thread static int c;
+__thread static int d = 5;
+
+
+static void* work(void* arg)
+{
+ //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a());
+ if ( a != 0 ) {
+ FAIL("tlv-basic: get_a() non-zero");
+ exit(0);
+ }
+ if ( b != 5 ) {
+ FAIL("tlv-basic: get_b() not five");
+ exit(0);
+ }
+ if ( c != 0 ) {
+ FAIL("tlv-basic: get_c() non-zero");
+ exit(0);
+ }
+ if ( d != 5 ) {
+ FAIL("tlv-basic: get_d() not five");
+ exit(0);
+ }
+ return NULL;
+}
+
+int main()
+{
+ pthread_t worker1;
+ if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ pthread_t worker2;
+ if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ void* result;
+ //fprintf(stderr, "waiting for worker 1\n");
+ pthread_join(worker1, &result);
+ //fprintf(stderr, "waiting for worker 2\n");
+ pthread_join(worker2, &result);
+
+ work(NULL);
+
+ PASS("tlv-basic");
+ return EXIT_SUCCESS;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+##
+## Basic test of thread-local-variables in a main executable
+##
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include foo.c -dynamiclib -o libfoo.dylib
+ clang -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main
+
+
+clean:
+ ${RM} ${RMFLAGS} libfoo.dylib main
+
--- /dev/null
+
+
+
+__thread int a;
+__thread int b = 5;
+
+
+int getB() { return b; }
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <pthread.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+extern __thread int a;
+extern __thread int b;
+__thread static int c;
+__thread static int d = 5;
+
+
+static void* work(void* arg)
+{
+ //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a());
+ if ( a != 0 ) {
+ FAIL("tlv-basic: get_a() non-zero");
+ exit(0);
+ }
+ if ( b != 5 ) {
+ FAIL("tlv-basic: get_b() not five");
+ exit(0);
+ }
+ if ( c != 0 ) {
+ FAIL("tlv-basic: get_c() non-zero");
+ exit(0);
+ }
+ if ( d != 5 ) {
+ FAIL("tlv-basic: get_d() not five");
+ exit(0);
+ }
+ return NULL;
+}
+
+int main()
+{
+ pthread_t worker1;
+ if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ pthread_t worker2;
+ if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ void* result;
+ //fprintf(stderr, "waiting for worker 1\n");
+ pthread_join(worker1, &result);
+ //fprintf(stderr, "waiting for worker 2\n");
+ pthread_join(worker2, &result);
+
+ work(NULL);
+
+ PASS("tlv-basic");
+ return EXIT_SUCCESS;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+##
+## Basic test of thread-local-variables in a main executable
+##
+
+all-check: all_$(OS_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES)
+
+check: check_$(OS_BAROLO_FEATURES)
+
+check_:
+ ${PASS_IFF} true
+
+all_:
+
+
+check_1:
+ ./main
+
+all_1:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c get.s -o main
+
+
+clean:
+ ${RM} ${RMFLAGS} main
+
--- /dev/null
+
+ # _a is zerofill global TLV
+ .tbss _a$tlv$init,4,2
+
+ # _b is an initialized global TLV
+ .tdata
+_b$tlv$init:
+ .long 5
+
+
+#if __x86_64__
+
+ # _a is global TLV
+ .tlv
+ .globl _a
+_a: .quad __tlv_bootstrap
+ .quad 0
+ .quad _a$tlv$init
+
+ # _b is a global TLV
+ .tlv
+ .globl _b
+_b: .quad __tlv_bootstrap
+ .quad 0
+ .quad _b$tlv$init
+
+ # _myinit sets up TLV content
+ .thread_init_func
+ .quad _myinit
+
+
+ .text
+ .globl _get_a
+_get_a:
+ pushq %rbp
+ movq %rsp, %rbp
+ movq _a@TLVP(%rip), %rdi
+ call *(%rdi)
+ popq %rbp
+ ret
+
+ .globl _get_b
+_get_b:
+ pushq %rbp
+ movq %rsp, %rbp
+ movq _b@TLVP(%rip), %rdi
+ call *(%rdi)
+ popq %rbp
+ ret
+
+#endif
+
+#if __i386__
+
+ # _a is global TLV
+ .tlv
+ .globl _a
+_a: .long __tlv_bootstrap
+ .long 0
+ .long _a$tlv$init
+
+ # _b is a global TLV
+ .tlv
+ .globl _b
+_b: .long __tlv_bootstrap
+ .long 0
+ .long _b$tlv$init
+
+ # _myinit sets up TLV content
+ .thread_init_func
+ .long _myinit
+
+
+ .text
+ .globl _get_a
+_get_a:
+ pushl %ebp
+ movl %esp, %ebp
+ subl $8, %esp
+ movl _a@TLVP, %eax
+ call *(%eax)
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+ .globl _get_b
+_get_b:
+ pushl %ebp
+ movl %esp, %ebp
+ subl $8, %esp
+ movl _b@TLVP, %eax
+ call *(%eax)
+ movl %ebp, %esp
+ popl %ebp
+ ret
+
+#endif
+
+.subsections_via_symbols
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <pthread.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+extern int* get_a(); // initially 0
+extern int* get_b(); // initially 5
+
+void myinit()
+{
+ *get_a() = 11;
+ *get_b() = 42;
+}
+
+
+static void* work(void* arg)
+{
+ //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a());
+ if ( *get_a() != 11 ) {
+ FAIL("tlv-initializer: get_a() not initialized to 11");
+ exit(0);
+ }
+ if ( *get_b() != 42 ) {
+ FAIL("tlv-initializer: get_b() not initialized to 42");
+ exit(0);
+ }
+ return NULL;
+}
+
+int main()
+{
+ pthread_t worker1;
+ if ( pthread_create(&worker1, NULL, work, NULL) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ pthread_t worker2;
+ if ( pthread_create(&worker2, NULL, work, NULL) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ void* result;
+ //fprintf(stderr, "waiting for worker 1\n");
+ pthread_join(worker1, &result);
+ //fprintf(stderr, "waiting for worker 2\n");
+ pthread_join(worker2, &result);
+
+ work(NULL);
+
+ PASS("tlv-initializer");
+ return EXIT_SUCCESS;
+}
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+##
+## Basic test of thread-local-variables in a main executable
+##
+
+all-check: all_$(OS_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES)
+
+check: check_$(OS_BAROLO_FEATURES)
+
+check_:
+ ${PASS_IFF} true
+
+all_:
+
+
+check_1:
+ ./main
+
+all_1:
+ clang ${CCFLAGS} -I${TESTROOT}/include main.c init.s -o main
+
+
+clean:
+ ${RM} ${RMFLAGS} main
+
--- /dev/null
+
+ # _myinit sets up TLV content
+ .thread_init_func
+ #if __LP64__
+ .quad _myinit
+ #else
+ .long _myinit
+ #endif
+
+.subsections_via_symbols
--- /dev/null
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+struct thread_var_info
+{
+ pthread_t th;
+ int* a_addr;
+ int* b_addr;
+ bool a_terminated;
+ bool b_terminated;
+};
+
+struct thread_var_info threadmain;
+struct thread_var_info thread1;
+struct thread_var_info thread2;
+
+
+__thread int a; // statically, initially 0
+__thread int b = 5; // statically, initially 5
+
+extern void _tlv_atexit(void (*termfunc)(void* objAddr), void* objAddr);
+
+void myinit()
+{
+ a = 11; // dynamically initialized to 11
+ b = 42; // dynamically initialized to 42
+}
+
+void myterm(void* objAddr)
+{
+ pthread_t self = pthread_self();
+ //fprintf(stderr, "myterm(%p), self=%p\n", objAddr, self);
+ if ( thread1.th == self && thread1.a_addr == objAddr )
+ thread1.a_terminated = true;
+ else if ( thread1.th == self && thread1.b_addr == objAddr )
+ thread1.b_terminated = true;
+ else if ( thread2.th == self && thread2.a_addr == objAddr )
+ thread2.a_terminated = true;
+ else if ( thread2.th == self && thread2.b_addr == objAddr )
+ thread2.b_terminated = true;
+ else if ( threadmain.th == self && threadmain.a_addr == objAddr )
+ threadmain.a_terminated = true;
+ else if ( threadmain.th == self && threadmain.b_addr == objAddr )
+ threadmain.b_terminated = true;
+}
+
+static void* work(void* arg)
+{
+ if ( a != 11 ) {
+ FAIL("tlv-terminators: a not initialized to 11");
+ exit(0);
+ }
+ if ( b != 42 ) {
+ FAIL("tlv-terminators: b not initialized to 42");
+ exit(0);
+ }
+ struct thread_var_info* s = (struct thread_var_info*)arg;
+ s->th = pthread_self();
+ s->a_addr = &a;
+ s->b_addr = &b;
+ s->a_terminated = false;
+ s->b_terminated = false;
+ //fprintf(stderr, "self=%p, arg=%p, &a=%p, &b=%p\n", s->th, arg, s->a_addr , s->b_addr);
+
+ _tlv_atexit(myterm, &a);
+ _tlv_atexit(myterm, &b);
+
+ return NULL;
+}
+
+int main()
+{
+ pthread_t worker1;
+ if ( pthread_create(&worker1, NULL, work, &thread1) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ pthread_t worker2;
+ if ( pthread_create(&worker2, NULL, work, &thread2) != 0 ) {
+ FAIL("pthread_create failed");
+ exit(0);
+ }
+
+ void* result;
+ //fprintf(stderr, "waiting for worker 1\n");
+ pthread_join(worker1, &result);
+ //fprintf(stderr, "waiting for worker 2\n");
+ pthread_join(worker2, &result);
+
+ work(&threadmain);
+
+ //fprintf(stderr, "thread1: &a=%p, &b=%p\n", thread1.a_addr, thread1.b_addr);
+ //fprintf(stderr, "thread2: &a=%p, &b=%p\n", thread2.a_addr, thread2.b_addr);
+ //fprintf(stderr, "threadm: &a=%p, &b=%p\n", threadmain.a_addr, threadmain.b_addr);
+
+ if ( ! thread1.a_terminated ) {
+ FAIL("tlv-terminators: terminator for a on thread 1 not run");
+ exit(0);
+ }
+ if ( ! thread1.b_terminated ) {
+ FAIL("tlv-terminators: terminator for b on thread 1 not run");
+ exit(0);
+ }
+
+ if ( ! thread2.a_terminated ) {
+ FAIL("tlv-terminators: terminator for a on thread 2 not run");
+ exit(0);
+ }
+ if ( ! thread2.b_terminated ) {
+ FAIL("tlv-terminators: terminator for b on thread 2 not run");
+ exit(0);
+ }
+
+ if ( threadmain.a_terminated ) {
+ FAIL("tlv-terminators: terminator for a on main thread run early");
+ exit(0);
+ }
+ if ( threadmain.b_terminated ) {
+ FAIL("tlv-terminators: terminator for b on main thread run early");
+ exit(0);
+ }
+
+
+ PASS("tlv-terminators");
+ return EXIT_SUCCESS;
+}
#include <mach-o/dyld.h>
#include <mach/mach.h>
#include <sys/mman.h>
+#include <dlfcn.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
// call a dyld API that uses the string
// if dyld reads past the end of the string, it will crash
// <rdar://problem/6493245> trie parser can read past end of input symbol name
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && (__MAC_OS_X_VERSION_MIN_REQUIRED <= __MAC_10_5)
_dyld_lookup_and_bind(sym, NULL, NULL);
-
+#else
+ dlsym(RTLD_DEFAULT, sym);
+#endif
PASS("trie-symbol-overrun");
return EXIT_SUCCESS;
}
/*
- * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <string.h>
#include <mach-o/dyld.h>
+#include <dlfcn.h>
+#include <Availability.h>
#include "test.h" // PASS(), FAIL()
int main()
{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED
// load libfoo which depends on libbar
const struct mach_header* mh = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR);
if ( mh != NULL ) {
FAIL("library-cant-be-bound: NSAddImage should have failed");
return 1;
}
-
+#else
+ if ( dlopen("libfoo.dylib", RTLD_LAZY) != NULL ){
+ FAIL("library-cant-be-bound: dlopen should have failed");
+ return 1;
+ }
+#endif
+
uint32_t count = _dyld_image_count();
for(uint32_t i=0; i < count; ++i) {
const char* name = _dyld_get_image_name(i);
--- /dev/null
+##
+# Copyright (c) 2010 Apple Inc. All rights reserved.
+#
+# @APPLE_LICENSE_HEADER_START@
+#
+# This file contains Original Code and/or Modifications of Original Code
+# as defined in and that are subject to the Apple Public Source License
+# Version 2.0 (the 'License'). You may not use this file except in
+# compliance with the License. Please obtain a copy of the License at
+# http://www.opensource.apple.com/apsl/ and read it before using this
+# file.
+#
+# The Original Code and all software distributed under the License are
+# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+# Please see the License for the specific language governing rights and
+# limitations under the License.
+#
+# @APPLE_LICENSE_HEADER_END@
+##
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+all-check: all_$(OS_BAROLO_FEATURES) check_$(OS_BAROLO_FEATURES)
+
+all: all_$(OS_BAROLO_FEATURES)
+
+check: check_$(OS_BAROLO_FEATURES)
+
+check_:
+ ${PASS_IFF} true
+
+all_:
+
+
+check_1:
+ ./main1
+ ./main2
+
+all_1:
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib up.c -DSTUB -o libup.stub -install_name libup.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib down.c -o libdown.dylib -Wl,-upward_library,libup.stub
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib up.c libdown.dylib -o libup.dylib
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libdown.dylib libup.dylib -o main2
+ ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libup.dylib libdown.dylib -o main1
+
+
+clean:
+ ${RM} ${RMFLAGS} *~ main1 main2 libup.dylib libdown.dylib libup.stub
+
--- /dev/null
+#include <stdio.h>
+#include "up.h"
+
+static int state = 0;
+
+
+// should run second because down.dylib is lower than up.dylib
+static __attribute__((constructor)) void myInit3()
+{
+ //fprintf(stderr, "myInit3()\n");
+ state = 1;
+}
+
+int getdown()
+{
+ return state;
+}
+
+void other()
+{
+ whatsup();
+}
\ No newline at end of file
--- /dev/null
+extern int getdown();
+
--- /dev/null
+#include <stdio.h> // fprintf(), NULL
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+
+#include "up.h"
+#include "down.h"
+
+int main()
+{
+ if ( whatsup() )
+ PASS("upward-dylib");
+ else
+ FAIL("upward-dylib");
+ return EXIT_SUCCESS;
+}
+
+
--- /dev/null
+#include <stdio.h>
+#include "down.h"
+
+static int state = 0;
+
+#ifndef STUB
+// should run second because up.dylib is higher than down.dylib
+static __attribute__((constructor)) void myInit1()
+{
+ //fprintf(stderr, "myInit1()\n");
+ if ( getdown() ) {
+ state = 1;
+ }
+}
+#endif
+
+int whatsup()
+{
+ return state;
+}
+
--- /dev/null
+
+extern int whatsup();
--- /dev/null
+
+TESTROOT = ../..
+include ${TESTROOT}/include/common.makefile
+
+
+all-check: all check
+
+check:
+ ./main
+
+all:
+ $(CC) $(CCFLAGS) -I${TESTROOT}/include main.c -o main
+ $(CC) $(CCFLAGS) bar.c -dynamiclib -o libbar.dylib
+ $(CC) $(CCFLAGS) foo.c -dynamiclib -o libfoo.dylib
+ strip -c -x libfoo.dylib -o libstub.dylib
+
+clean:
+ ${RM} ${RMFLAGS} libbar.dylib libfoo.dylib libstub.dylib main
--- /dev/null
+void bar() {}
+
+void foo() __attribute__((weak));
+void foo()
+{
+}
\ No newline at end of file
--- /dev/null
+
+
+void foo() __attribute__((weak));
+
+void foo()
+{
+}
+
+void abcdefghijklmnopqrstuvwxzy() {}
+void abcde3fghijklmnopqrstuvwxzy() {}
+void abcdef4ghijklmnopqrstuvwxzy() {}
+void abcdefgh5ijklmnopqrstuvwxzy() {}
+void abcdefghij6klmnopqrstuvwxzy() {}
+void abcdefghijk7lmnopqrstuvwxzy() {}
+void abcdefghijklm8nopqrstuvwxzy() {}
+void abcdefghijklmn9opqrstuvwxzy() {}
+void a1bcdefghijklmnopqrstuvwxzy() {}
+void a2bcdefghijklmnopqrstuvwxzy() {}
+void a3bcdefghijklmnopqrstuvwxzy() {}
+void a4bcdefghijklmnopqrstuvwxzy() {}
+void a5bcdefghijklmnopqrstuvwxzy() {}
+void a6bcdefghijklmnopqrstuvwxzy() {}
+void a7bcdefghijklmnopqrstuvwxzy() {}
+void a8bcdefghijklmnopqrstuvwxzy() {}
+void a9bcdefghijklmnopqrstuvwxzy() {}
+void ab1cdefghijklmnopqrstuvwxzy() {}
+void ab2cdefghijklmnopqrstuvwxzy() {}
+void ab3cdefghijklmnopqrstuvwxzy() {}
+void ab4cdefghijklmnopqrstuvwxzy() {}
+void ab5cdefghijklmnopqrstuvwxzy() {}
+void ab6cdefghijklmnopqrstuvwxzy() {}
+void ab7cdefghijklmnopqrstuvwxzy() {}
+void ab8cdefghijklmnopqrstuvwxzy() {}
+void ab9cdefghijklmnopqrstuvwxzy() {}
+void abc1defghijklmnopqrstuvwxzy() {}
+void abc2defghijklmnopqrstuvwxzy() {}
+void abc3defghijklmnopqrstuvwxzy() {}
+void abc4defghijklmnopqrstuvwxzy() {}
+void abc5defghijklmnopqrstuvwxzy() {}
+void abc6defghijklmnopqrstuvwxzy() {}
+void abc7defghijklmnopqrstuvwxzy() {}
+void abc8defghijklmnopqrstuvwxzy() {}
+void abc9defghijklmnopqrstuvwxzy() {}
+void abcd1efghijklmnopqrstuvwxzy() {}
+void abcd2efghijklmnopqrstuvwxzy() {}
+void abcd3efghijklmnopqrstuvwxzy() {}
+void abcd4efghijklmnopqrstuvwxzy() {}
+void abcd5efghijklmnopqrstuvwxzy() {}
+void abcd6efghijklmnopqrstuvwxzy() {}
+void abcd7efghijklmnopqrstuvwxzy() {}
+void abcd8efghijklmnopqrstuvwxzy() {}
+void abcd9efghijklmnopqrstuvwxzy() {}
+void abcde1fghijklmn9opqrstuvwxzy() {}
+void abcde2fghijklmn9opqrstuvwxzy() {}
+void abcde3fghijklmn9opqrstuvwxzy() {}
+void abcde4fghijklmn9opqrstuvwxzy() {}
+void abcde5fghijklmn9opqrstuvwxzy() {}
+void abcde6fghijklmn9opqrstuvwxzy() {}
+void abcde7fghijklmn9opqrstuvwxzy() {}
+void abcde8fghijklmn9opqrstuvwxzy() {}
+void abcde9fghijklmn9opqrstuvwxzy() {}
--- /dev/null
+#include <stdio.h>
+#include <dlfcn.h>
+#include <stdlib.h> // exit(), EXIT_SUCCESS
+
+#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL()
+
+//<rdar://problem/7886402> Loading MH_DYLIB_STUB causing coalescable miscount
+
+int main()
+{
+ // try to load stub many times
+ for (int i=0; i < 10; ++i) {
+ void* handle = dlopen("libstub.dylib", RTLD_LAZY);
+ if ( handle != NULL ) {
+ FAIL("weak-coalesce-stubs: load of libstub.dylib unexpectedly succeeded");
+ }
+ }
+ // try to load real dylib
+ dlopen("libbar.dylib", RTLD_LAZY);
+ PASS("weak-coalesce-stubs");
+ return 0;
+}