diff --git a/dyld/.clang-format b/dyld/.clang-format new file mode 100644 index 0000000..1d7b0fd --- /dev/null +++ b/dyld/.clang-format @@ -0,0 +1,13 @@ +Language: Cpp +BasedOnStyle: WebKit + +AlignConsecutiveDeclarations: true +AlignOperands: false +AlignTrailingComments: true +IndentWidth: 4 + +Standard: Cpp11 + +UseTab: Never + +SortIncludes: false diff --git a/dyld/.gitignore b/dyld/.gitignore new file mode 100644 index 0000000..95b4227 --- /dev/null +++ b/dyld/.gitignore @@ -0,0 +1,8 @@ +build +dyld.xcodeproj/kledzik.mode1v3 +dyld.xcodeproj/kledzik.pbxuser +dyld.xcodeproj/project.xcworkspace/ +dyld.xcodeproj/xcuserdata/ +.DS_Store +.pyc + diff --git a/dyld/APPLE_LICENSE b/dyld/APPLE_LICENSE new file mode 100644 index 0000000..fe81a60 --- /dev/null +++ b/dyld/APPLE_LICENSE @@ -0,0 +1,367 @@ +APPLE PUBLIC SOURCE LICENSE +Version 2.0 - August 6, 2003 + +Please read this License carefully before downloading this software. +By downloading or using this software, you are agreeing to be bound by +the terms of this License. If you do not or cannot agree to the terms +of this License, please do not download or use the software. + +1. General; Definitions. This License applies to any program or other +work which Apple Computer, Inc. ("Apple") makes publicly available and +which contains a notice placed by Apple identifying such program or +work as "Original Code" and stating that it is subject to the terms of +this Apple Public Source License version 2.0 ("License"). As used in +this License: + +1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is +the grantor of rights, (i) claims of patents that are now or hereafter +acquired, owned by or assigned to Apple and (ii) that cover subject +matter contained in the Original Code, but only to the extent +necessary to use, reproduce and/or distribute the Original Code +without infringement; and (b) in the case where You are the grantor of +rights, (i) claims of patents that are now or hereafter acquired, +owned by or assigned to You and (ii) that cover subject matter in Your +Modifications, taken alone or in combination with Original Code. + +1.2 "Contributor" means any person or entity that creates or +contributes to the creation of Modifications. + +1.3 "Covered Code" means the Original Code, Modifications, the +combination of Original Code and any Modifications, and/or any +respective portions thereof. + +1.4 "Externally Deploy" means: (a) to sublicense, distribute or +otherwise make Covered Code available, directly or indirectly, to +anyone other than You; and/or (b) to use Covered Code, alone or as +part of a Larger Work, in any way to provide a service, including but +not limited to delivery of content, through electronic communication +with a client other than You. + +1.5 "Larger Work" means a work which combines Covered Code or portions +thereof with code not governed by the terms of this License. + +1.6 "Modifications" mean any addition to, deletion from, and/or change +to, the substance and/or structure of the Original Code, any previous +Modifications, the combination of Original Code and any previous +Modifications, and/or any respective portions thereof. When code is +released as a series of files, a Modification is: (a) any addition to +or deletion from the contents of a file containing Covered Code; +and/or (b) any new file or other representation of computer program +statements that contains any part of Covered Code. + +1.7 "Original Code" means (a) the Source Code of a program or other +work as originally made available by Apple under this License, +including the Source Code of any updates or upgrades to such programs +or works made available by Apple under this License, and that has been +expressly identified by Apple as such in the header file(s) of such +work; and (b) the object code compiled from such Source Code and +originally made available by Apple under this License. + +1.8 "Source Code" means the human readable form of a program or other +work that is suitable for making modifications to it, including all +modules it contains, plus any associated interface definition files, +scripts used to control compilation and installation of an executable +(object code). + +1.9 "You" or "Your" means an individual or a legal entity exercising +rights under this License. For legal entities, "You" or "Your" +includes any entity which controls, is controlled by, or is under +common control with, You, where "control" means (a) the power, direct +or indirect, to cause the direction or management of such entity, +whether by contract or otherwise, or (b) ownership of fifty percent +(50%) or more of the outstanding shares or beneficial ownership of +such entity. + +2. Permitted Uses; Conditions & Restrictions. Subject to the terms +and conditions of this License, Apple hereby grants You, effective on +the date You accept this License and download the Original Code, a +world-wide, royalty-free, non-exclusive license, to the extent of +Apple's Applicable Patent Rights and copyrights covering the Original +Code, to do the following: + +2.1 Unmodified Code. You may use, reproduce, display, perform, +internally distribute within Your organization, and Externally Deploy +verbatim, unmodified copies of the Original Code, for commercial or +non-commercial purposes, provided that in each instance: + +(a) You must retain and reproduce in all copies of Original Code the +copyright and other proprietary notices and disclaimers of Apple as +they appear in the Original Code, and keep intact all notices in the +Original Code that refer to this License; and + +(b) You must include a copy of this License with every copy of Source +Code of Covered Code and documentation You distribute or Externally +Deploy, and You may not offer or impose any terms on such Source Code +that alter or restrict this License or the recipients' rights +hereunder, except as permitted under Section 6. + +2.2 Modified Code. You may modify Covered Code and use, reproduce, +display, perform, internally distribute within Your organization, and +Externally Deploy Your Modifications and Covered Code, for commercial +or non-commercial purposes, provided that in each instance You also +meet all of these conditions: + +(a) You must satisfy all the conditions of Section 2.1 with respect to +the Source Code of the Covered Code; + +(b) You must duplicate, to the extent it does not already exist, the +notice in Exhibit A in each file of the Source Code of all Your +Modifications, and cause the modified files to carry prominent notices +stating that You changed the files and the date of any change; and + +(c) If You Externally Deploy Your Modifications, You must make +Source Code of all Your Externally Deployed Modifications either +available to those to whom You have Externally Deployed Your +Modifications, or publicly available. Source Code of Your Externally +Deployed Modifications must be released under the terms set forth in +this License, including the license grants set forth in Section 3 +below, for as long as you Externally Deploy the Covered Code or twelve +(12) months from the date of initial External Deployment, whichever is +longer. You should preferably distribute the Source Code of Your +Externally Deployed Modifications electronically (e.g. download from a +web site). + +2.3 Distribution of Executable Versions. In addition, if You +Externally Deploy Covered Code (Original Code and/or Modifications) in +object code, executable form only, You must include a prominent +notice, in the code itself as well as in related documentation, +stating that Source Code of the Covered Code is available under the +terms of this License with information on how and where to obtain such +Source Code. + +2.4 Third Party Rights. You expressly acknowledge and agree that +although Apple and each Contributor grants the licenses to their +respective portions of the Covered Code set forth herein, no +assurances are provided by Apple or any Contributor that the Covered +Code does not infringe the patent or other intellectual property +rights of any other entity. Apple and each Contributor disclaim any +liability to You for claims brought by any other entity based on +infringement of intellectual property rights or otherwise. As a +condition to exercising the rights and licenses granted hereunder, You +hereby assume sole responsibility to secure any other intellectual +property rights needed, if any. For example, if a third party patent +license is required to allow You to distribute the Covered Code, it is +Your responsibility to acquire that license before distributing the +Covered Code. + +3. Your Grants. In consideration of, and as a condition to, the +licenses granted to You under this License, You hereby grant to any +person or entity receiving or distributing Covered Code under this +License a non-exclusive, royalty-free, perpetual, irrevocable license, +under Your Applicable Patent Rights and other intellectual property +rights (other than patent) owned or controlled by You, to use, +reproduce, display, perform, modify, sublicense, distribute and +Externally Deploy Your Modifications of the same scope and extent as +Apple's licenses under Sections 2.1 and 2.2 above. + +4. Larger Works. You may create a Larger Work by combining Covered +Code with other code not governed by the terms of this License and +distribute the Larger Work as a single product. In each such instance, +You must make sure the requirements of this License are fulfilled for +the Covered Code or any portion thereof. + +5. Limitations on Patent License. Except as expressly stated in +Section 2, no other patent rights, express or implied, are granted by +Apple herein. Modifications and/or Larger Works may require additional +patent licenses from Apple which Apple may grant in its sole +discretion. + +6. Additional Terms. You may choose to offer, and to charge a fee for, +warranty, support, indemnity or liability obligations and/or other +rights consistent with the scope of the license granted herein +("Additional Terms") to one or more recipients of Covered Code. +However, You may do so only on Your own behalf and as Your sole +responsibility, and not on behalf of Apple or any Contributor. You +must obtain the recipient's agreement that any such Additional Terms +are offered by You alone, and You hereby agree to indemnify, defend +and hold Apple and every Contributor harmless for any liability +incurred by or claims asserted against Apple or such Contributor by +reason of any such Additional Terms. + +7. Versions of the License. Apple may publish revised and/or new +versions of this License from time to time. Each version will be given +a distinguishing version number. Once Original Code has been published +under a particular version of this License, You may continue to use it +under the terms of that version. You may also choose to use such +Original Code under the terms of any subsequent version of this +License published by Apple. No one other than Apple has the right to +modify the terms applicable to Covered Code created under this +License. + +8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in +part pre-release, untested, or not fully tested works. The Covered +Code may contain errors that could cause failures or loss of data, and +may be incomplete or contain inaccuracies. You expressly acknowledge +and agree that use of the Covered Code, or any portion thereof, is at +Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND +WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND +APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE +PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM +ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF +MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR +PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD +PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST +INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE +FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS, +THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO +ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE +AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY. +You acknowledge that the Covered Code is not intended for use in the +operation of nuclear facilities, aircraft navigation, communication +systems, or air traffic control machines in which case the failure of +the Covered Code could lead to death, personal injury, or severe +physical or environmental damage. + +9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO +EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL, +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING +TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR +ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY, +TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF +APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY +REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF +INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY +TO YOU. In no event shall Apple's total liability to You for all +damages (other than as may be required by applicable law) under this +License exceed the amount of fifty dollars ($50.00). + +10. Trademarks. This License does not grant any rights to use the +trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS", +"QuickTime", "QuickTime Streaming Server" or any other trademarks, +service marks, logos or trade names belonging to Apple (collectively +"Apple Marks") or to any trademark, service mark, logo or trade name +belonging to any Contributor. You agree not to use any Apple Marks in +or as part of the name of products derived from the Original Code or +to endorse or promote products derived from the Original Code other +than as expressly permitted by and in strict compliance at all times +with Apple's third party trademark usage guidelines which are posted +at http://www.apple.com/legal/guidelinesfor3rdparties.html. + +11. Ownership. Subject to the licenses granted under this License, +each Contributor retains all rights, title and interest in and to any +Modifications made by such Contributor. Apple retains all rights, +title and interest in and to the Original Code and any Modifications +made by or on behalf of Apple ("Apple Modifications"), and such Apple +Modifications will not be automatically subject to this License. Apple +may, at its sole discretion, choose to license such Apple +Modifications under this License, or on different terms from those +contained in this License or may choose not to license them at all. + +12. Termination. + +12.1 Termination. This License and the rights granted hereunder will +terminate: + +(a) automatically without notice from Apple if You fail to comply with +any term(s) of this License and fail to cure such breach within 30 +days of becoming aware of such breach; + +(b) immediately in the event of the circumstances described in Section +13.5(b); or + +(c) automatically without notice from Apple if You, at any time during +the term of this License, commence an action for patent infringement +against Apple; provided that Apple did not first commence +an action for patent infringement against You in that instance. + +12.2 Effect of Termination. Upon termination, You agree to immediately +stop any further use, reproduction, modification, sublicensing and +distribution of the Covered Code. All sublicenses to the Covered Code +which have been properly granted prior to termination shall survive +any termination of this License. Provisions which, by their nature, +should remain in effect beyond the termination of this License shall +survive, including but not limited to Sections 3, 5, 8, 9, 10, 11, +12.2 and 13. No party will be liable to any other for compensation, +indemnity or damages of any sort solely as a result of terminating +this License in accordance with its terms, and termination of this +License will be without prejudice to any other right or remedy of +any party. + +13. Miscellaneous. + +13.1 Government End Users. The Covered Code is a "commercial item" as +defined in FAR 2.101. Government software and technical data rights in +the Covered Code include only those rights customarily provided to the +public as defined in this License. This customary commercial license +in technical data and software is provided in accordance with FAR +12.211 (Technical Data) and 12.212 (Computer Software) and, for +Department of Defense purchases, DFAR 252.227-7015 (Technical Data -- +Commercial Items) and 227.7202-3 (Rights in Commercial Computer +Software or Computer Software Documentation). Accordingly, all U.S. +Government End Users acquire Covered Code with only those rights set +forth herein. + +13.2 Relationship of Parties. This License will not be construed as +creating an agency, partnership, joint venture or any other form of +legal association between or among You, Apple or any Contributor, and +You will not represent to the contrary, whether expressly, by +implication, appearance or otherwise. + +13.3 Independent Development. Nothing in this License will impair +Apple's right to acquire, license, develop, have others develop for +it, market and/or distribute technology or products that perform the +same or similar functions as, or otherwise compete with, +Modifications, Larger Works, technology or products that You may +develop, produce, market or distribute. + +13.4 Waiver; Construction. Failure by Apple or any Contributor to +enforce any provision of this License will not be deemed a waiver of +future enforcement of that or any other provision. Any law or +regulation which provides that the language of a contract shall be +construed against the drafter will not apply to this License. + +13.5 Severability. (a) If for any reason a court of competent +jurisdiction finds any provision of this License, or portion thereof, +to be unenforceable, that provision of the License will be enforced to +the maximum extent permissible so as to effect the economic benefits +and intent of the parties, and the remainder of this License will +continue in full force and effect. (b) Notwithstanding the foregoing, +if applicable law prohibits or restricts You from fully and/or +specifically complying with Sections 2 and/or 3 or prevents the +enforceability of either of those Sections, this License will +immediately terminate and You must immediately discontinue any use of +the Covered Code and destroy all copies of it that are in your +possession or control. + +13.6 Dispute Resolution. Any litigation or other dispute resolution +between You and Apple relating to this License shall take place in the +Northern District of California, and You and Apple hereby consent to +the personal jurisdiction of, and venue in, the state and federal +courts within that District with respect to this License. The +application of the United Nations Convention on Contracts for the +International Sale of Goods is expressly excluded. + +13.7 Entire Agreement; Governing Law. This License constitutes the +entire agreement between the parties with respect to the subject +matter hereof. This License shall be governed by the laws of the +United States and the State of California, except that body of +California law concerning conflicts of law. + +Where You are located in the province of Quebec, Canada, the following +clause applies: The parties hereby confirm that they have requested +that this License and all related documents be drafted in English. Les +parties ont exige que le present contrat et tous les documents +connexes soient rediges en anglais. + +EXHIBIT A. + +"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +Reserved. + +This file contains Original Code and/or Modifications of Original Code +as defined in and that are subject to the Apple Public Source License +Version 2.0 (the 'License'). You may not use this file except in +compliance with the License. Please obtain a copy of the License at +http://www.opensource.apple.com/apsl/ and read it before using this +file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +Please see the License for the specific language governing rights and +limitations under the License." diff --git a/dyld/Makefile b/dyld/Makefile new file mode 100644 index 0000000..4a4742e --- /dev/null +++ b/dyld/Makefile @@ -0,0 +1,13 @@ +CXXFLAGS = -Wall +CXX = clang++ + +.PHONY: all clean + +all: dsc_extractor + +dsc_extractor: dsc_extractor.o dsc_iterator.o + $(CXX) -o $@ $^ + +clean: + -rm -f dsc_extractor dsc_extractor.o dsc_iterator.o + -rm -f *~ diff --git a/dyld/README.md b/dyld/README.md new file mode 100644 index 0000000..04a6f1e --- /dev/null +++ b/dyld/README.md @@ -0,0 +1,27 @@ +# Dynamic Shared Cache Extractor + +Apple dyld (*dynamic link editor*) loads and links the shared/dynamic libraries (`.dylib` files in Apple OSes: macOS, iOS). +It is provided as open source software by Apple; you can [browse it](https://opensource.apple.com/source/dyld/) or you can download [dyld tarballs](https://opensource.apple.com/tarballs/dyld/). +There is an [unofficial repository](https://github.com/opensource-apple/dyld) that's currently (December 2017) not updated to the latest dyld provided by Apple (`519.2.1`). + +As told by [the iPhoneDevWiki](http://iphonedevwiki.net/index.php/Dyld_shared_cache) "all system (private and public) libraries have been combined into a big cache file to improve performance". +This file is named `dyld_shared_cache_...` with a suffix denoting the architecture. +Extraction of libraries from the dyld shared cache is not trivial since certain symbols are updated ("redacted"). + +Based on [ant4g0nist's blog post](http://ant4g0nist.blogspot.ro/2015/04/ios-shared-cache-extraction-to-solve.html), we downloaded the latest [dyld tarball](https://opensource.apple.com/tarballs/dyld/) (`dyld-519.2.1.tar.gz`), copied the contents of the `launch-cache/` subfolder in the repository and added a `Makefile` to build `dsc_extractor` (*dyld shared cache extractor*). +`dsc_extractor` is used to extract the library files from the dyld shared cache. +In the `dsc_extractor.cpp` source code file we enabled the `main` function to allow the building of the `dsc_exctractor` executable. + +Building and running `dsc_extractor` requires macOS. +To build the `dsc_extractor` executable run `make`: + +``` +make +``` + +To run `dsc_extractor` pass it two arguments: the path to the dyld shared cache file and the output folder that will store the extracted library files. +The command below extracts the shared library files for an iOS 9 dyld shared cache in the current directory: + +``` +./dsc_extractor /mnt/ios/iPhone5,1_9.3_13E237/System/Library/Caches/com.apple.dyld/dyld_shared_cache_armv7s . +``` diff --git a/dyld/bin/expand.pl b/dyld/bin/expand.pl new file mode 100755 index 0000000..b21ef3f --- /dev/null +++ b/dyld/bin/expand.pl @@ -0,0 +1,66 @@ +#!/usr/bin/perl + +use strict; + + +my $sdk = $ENV{"SDKROOT"}; +my $availCmd = $sdk . "/usr/local/libexec/availability.pl"; + +sub expandVersions +{ + my $macroPrefix = shift; + my $availArg = shift; + + my $cmd = $availCmd . " " . $availArg; + my $versionList = `$cmd`; + my $tmp = $versionList; + while ($tmp =~ m/^\s*([\S]+)(.*)$/) { + my $vers = $1; + $tmp = $2; + + my $major = 0; + my $minor = 0; + my $revision = 0; + my $uvers; + + if ($vers =~ m/^(\d+)$/) { + $major = $1; + $uvers = sprintf("%d_0", $major); + } elsif ($vers =~ m/^(\d+).(\d+)$/) { + $major = $1; + $minor = $2; + $uvers = sprintf("%d_%d", $major, $minor); + } elsif ($vers =~ m/^(\d+).(\d+).(\d+)$/) { + $major = $1; + $minor = $2; + $revision = $3; + if ($revision == 0) { + $uvers = sprintf("%d_%d", $major, $minor); + } + else { + $uvers = sprintf("%d_%d_%d", $major, $minor, $revision); + } + } + printf "#define %s%-18s 0x00%02X%02X%02X\n", $macroPrefix, $uvers, $major, $minor, $revision; + } +} + + + + +while() +{ + if(m/^\/\/\@MAC_VERSION_DEFS\@$/) { + expandVersions("DYLD_MACOSX_VERSION_", "--macosx"); + } + elsif(m/^\/\/\@IOS_VERSION_DEFS\@$/) { + expandVersions("DYLD_IOS_VERSION_", "--ios"); + } + elsif(m/^\/\/\@WATCHOS_VERSION_DEFS\@$/) { + expandVersions("DYLD_WATCHOS_VERSION_", "--watchos"); + } + else { + print $_; + } +} + diff --git a/dyld/bin/set-alt-dyld b/dyld/bin/set-alt-dyld new file mode 100755 index 0000000..19ae128 --- /dev/null +++ b/dyld/bin/set-alt-dyld @@ -0,0 +1,30 @@ +#!/usr/bin/perl -w + +use strict; +undef $/; + +if(@ARGV == 0) +{ + print "Usage: $0 [ ...]\n"; + exit 1; +} + +my $arg; +foreach $arg (@ARGV) +{ + open IN, "<$arg" or die $!; + my $in = ; + close IN or die $!; + + if($in =~ s{/usr/lib/dyld}{/usr/local/dy}) + { + open OUT, ">$arg" or die $!; + print OUT $in; + close OUT or die $!; + } + else + { + print STDERR "ERROR: $arg\n"; + exit 1; + } +} diff --git a/dyld/configs/base.xcconfig b/dyld/configs/base.xcconfig new file mode 100644 index 0000000..e69de29 diff --git a/dyld/configs/closured.xcconfig b/dyld/configs/closured.xcconfig new file mode 100644 index 0000000..215e217 --- /dev/null +++ b/dyld/configs/closured.xcconfig @@ -0,0 +1,2 @@ + +CODE_SIGN_ENTITLEMENTS[sdk=embedded*] = dyld3/closured/closured_entitlements.plist diff --git a/dyld/configs/dyld.xcconfig b/dyld/configs/dyld.xcconfig new file mode 100644 index 0000000..d1ec099 --- /dev/null +++ b/dyld/configs/dyld.xcconfig @@ -0,0 +1,21 @@ +ALIGNMENT[arch=armv7s] = -Wl,-segalign,0x4000 + +ENTRY[sdk=*simulator*] = -Wl,-e,_start_sim +ENTRY[sdk=iphoneos*] = -Wl,-e,__dyld_start +ENTRY[sdk=macosx*] = -Wl,-e,__dyld_start + +EXPORTED_SYMBOLS_FILE[sdk=*simulator*] = $(SRCROOT)/src/dyld_sim.exp +EXPORTED_SYMBOLS_FILE[sdk=iphoneos*] = $(SRCROOT)/src/dyld.exp +EXPORTED_SYMBOLS_FILE[sdk=macosx*] = $(SRCROOT)/src/dyld.exp + +PRODUCT_NAME[sdk=*simulator*] = dyld_sim +PRODUCT_NAME[sdk=iphoneos*] = dyld +PRODUCT_NAME[sdk=macosx*] = dyld + +INSTALL_PATH = /usr/lib + +//:configuration = Debug +GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1 DEBUG=1 + +//:configuration = Release +GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1 diff --git a/dyld/configs/libdyld.xcconfig b/dyld/configs/libdyld.xcconfig new file mode 100644 index 0000000..8f06e5a --- /dev/null +++ b/dyld/configs/libdyld.xcconfig @@ -0,0 +1,14 @@ + +LIBSYSTEM_LIBS[sdk=*simulator*] = -Wl,-upward-lsystem_sim_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_sim_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_sim_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch -Wl,-upward-lcommonCrypto -Wl,-upward-lclosured +LIBSYSTEM_LIBS[sdk=embedded*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch -Wl,-upward-lcommonCrypto -Wl,-upward-lclosured -Wl,-upward-lcompiler_rt +LIBSYSTEM_LIBS[sdk=macosx*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lxpc -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lsystem_sandbox -Wl,-upward-ldispatch -Wl,-upward-lcommonCrypto -Wl,-upward-lclosured + +INSTALL_PATH = /usr/lib/system + + +//:configuration = Debug +GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 BUILDING_LIBDYLD=1 DEBUG=1 + +//:configuration = Release +GCC_PREPROCESSOR_DEFINITIONS = DYLD_IN_PROCESS=1 BUILDING_LIBDYLD=1 + diff --git a/dyld/configs/update_dyld_shared_cache.xcconfig b/dyld/configs/update_dyld_shared_cache.xcconfig new file mode 100644 index 0000000..e45c714 --- /dev/null +++ b/dyld/configs/update_dyld_shared_cache.xcconfig @@ -0,0 +1,4 @@ + + +CODE_SIGN_ENTITLEMENTS = dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist + diff --git a/dyld/configs/update_dyld_sim_shared_cache.xcconfig b/dyld/configs/update_dyld_sim_shared_cache.xcconfig new file mode 100644 index 0000000..e4f7bfa --- /dev/null +++ b/dyld/configs/update_dyld_sim_shared_cache.xcconfig @@ -0,0 +1,3 @@ + +#include "/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig" + diff --git a/dyld/doc/ReleaseNotes.txt b/dyld/doc/ReleaseNotes.txt new file mode 100644 index 0000000..18d5f81 --- /dev/null +++ b/dyld/doc/ReleaseNotes.txt @@ -0,0 +1,1771 @@ + +dyld-208 + Correct @path to still work with symlinks + +dyld-207 + dyld should give a better error message if you try to use a newer binary on older OS + backtraces of LC_MAIN binaries end in tlv_get_addr+136, instead libdyld should export "_start" symbol + Fix up @rpath based paths during introspection + Interposition tuples are not respected when binding with resolvers + +dyld-206 + Give some warning that DYLD_ env vars are disabled + +dyld-205 + Add dyld to uuidArray to enable symbolication of stackshots + +dyld-204.1 + fix initializer ordering of upwardly linked dylibs + +dyld-204 + dyld misreads very large addends + Perform method list sort even if __DATA,__objc_opt_rw is absent + Remove category attaching from dyld shared cache + Shared cache method list sorting breaks protocol extended type encodings + +dyld-203.1 + initializer not run in only upwardly linked dylib + +dyld-203 + update_dyld_shared_cache does not build with libc++ + dyld does not build with libc++ + Add functions to get min OS and sdk versions program was linked with + DYLD_FRAMEWORK_PATH not mentioned in dlopen() man page + dsc_extractor not updating LC_FUNCTION_STARTS + +dyld-202 + Get rid of crt1.o and jump straight into main() from dyld + image denied loading by gc_enforcer is left in dyld_all_images_info array + +dyld-201 + Use spin lock to guard sAllImageInfos + +dyld-200.3 + genCaches fails: "header size miscalculation 0x00006000" + +dyld-200.2 + ARCH_NAME for armv7k is defined as "armv7s" + dsc_iterator.cpp needs cases for v7 variants + +dyld-200 + dyld fails to build for armv7s + update_dyld_shared_cache should accept an 'overlay' along with a 'root' directory option +Remove PowerPC support + +-------------- +dyld-199.5 (iOS 5) + Update initial image list size + +dyld-199.4 + genCaches fails: header size miscalculation + +dyld-199.3 + Repair ivar offsets in dyld shared cache + +dyld-199.2 + improve Xcode upload of iOS dylibs + +dyld-199.1 + correctly adjust ARM movw when in dyld shared cache + +dyld-199 + update_dyld_shared_cache requires weak-linked frameworks to be present at shared cache generation time + Remove armv7 variants from dyld + +dyld-198.1 + back out previous change DYLD_XXX restrict check + +dyld-198 + corrupt load commands can pass sniff test if 32-bit wraps around + dyld should restrict DYLD_XXX debugging facilities + dyld should not allow loading of dylibs without valid signatures +enable DYLD_PRINT_REBASINGS + +dyld-197 + dyld should range check the fixup location of all bindings + range check initializers and terminators to be within image + +dyld-196.2 + dyld should support new armv7 variants + +dyld-196.1 + support re-exported symbols from re-exported libraries + +dyld-196 + movw/movt don't work in dyld shared cache + + +--------------------- +dyld-195.3 (Mac OS X 10.7.0) + update_dyld_shared_cache missed libstdc++? + i386 dyld shared cache overflows after adding libclh.dylib + +dyld-195.2 + spurious warning about embedded framework not being put in dyld shared cache + +dyld-195 + C++ 0x thread_local destructor support + update_dyld_shared_cache -verify finds differences + more verbose messages when vm_protect() fails + +dyld-194.1 +Fix uses of libc++abi-static.a + +dyld-194 + make interposing available to open source + __thread implementation doesn't preserve enough registers (ABI issue) + clang built update_dyld_shared_cache fails at runtime + +dyld-193 + dyld should honor CS_RESTRICT in _main like hasRestrictedSegment + update_dyld_shared_cache -overlay optimizations + +dyld-192.1 + dyld has NULL paths in image info array + +dyld-192 + dyld's map+slide should be an all or nothing operation + +dyld-191.3 + overriding shared cache dylibs with resolvers fails + +dyld-191.2 + dyld_stub_binder called repeatedly for $VARIANT$ functions + update_dyld_shared_cache crash when fat dylib truncated + +dyld-191.1 + update_dyld_shared_cache is failing + +dyld-191 + ASLR/PIE: dyld's "random padding" algorithm is deterministic + race condition with flat-namespace lazy binding + libdyld fails to build with clang-127 + +dyld-190.1 +remove libdyld.a target + +dyld-190 + _NSGetExecutablePath documentation is ambiguous about bufsize + 11A315: LC_DYLD_ENVIRONMENT does not expand @executable_path + dyld should avoid arc4random for dylib randomization + tune trie traversal code for 10.7 compiler + breaking libSystem into dylibs slows down dyld binding, shared cache should compensate + update_dyld_shared_cache should not be built no-pie + +dyld-189 + dyld(1) man page feedback + LC_DYLD_ENVIRONMENT should still be honored with __RESTRICT,__restrict section present + 11A307: DYLD_SHARED_REGION=private crashes + don't slide shared cache if ASLR disabled (main executable didn't slide) + +dyld-188 + Dynamic linking memory usage seems unusually high for trivial programs + +dyld-187 + madvise MADV_FREE calls for LINKEDIT are failing in dyld during launch in iOS + dyld allImageInfos messed up with duplicate IOKit + +dyld-186 + dyld: Pick up changes from libsystem_kernel reorganisation in dyld's build. + Shared Region Base Should Slide at Boot Time + Multiple /dev/urandom accesses on every process exec + +dyld-185 + text relocs don't work if only S_ATTR_EXT_RELOC is used + DYLD_FORCE_FLAT_NAMESPACE=YES causes process to spin in DYLD-STUB$$bzero + +dyld-184 + Add cache slide value to dyld_all_image_infos + +dyld-183.1 + dyld proper does not need dyld_stub_binder.o + +dyld-183 + dyld needs to call new kernel interface to register metadata + ASLR side build causes 15-30% regression in most launch times + Support version-checked framework/library override paths + Support LC_DYLD_ENVIRONMENT load command + function starts info should be copied into dyld shared cache + +dyld-182 + Merge Jasper dyld changes to trunk + +dyld-181.1 + 11A270: FNPLicensingService crashes + +dyld-181 + 11A238a: AutoDesk AutoCAD beta 4 crashes on launch (dyld) + update_dyld_shared_cache-173 project fails to build with LLVM compiler 2.0. + +dyld-180 + 8F63: dyld-179.4 fails on missing /usr/local/lib/system/libc.a + 11A238a: AutoDesk AutoCAD beta 4 crashes on launch + 11A244: overlapping files in libdyld & update_dyld_shared_cache + + +dyld-179.7 (iOS 4.2) + range check initializers + +dyld-179.6 + dyld_shared_cache_extract_dylibs shouldn't return '-1' when arch already exists + +dyld-179.5 + Need a version of dyld_shared_cache_extract_dylbs that lipo's results + Need a version of dyld_shared_cache_extract_dylbs that reports progress + back out work around for 8151909 + +dyld-179.4 + images in shared cache are bound against different IOKit than found at runtime + +dyld-179.3.1 + 11A245: Photoshop CS4 fails in SmokeTest because of crash in FNPLicensingService (dyld) + +dyld-179.3 + 11A238a: All Adobe CS4 applications crash on launch + +dyld-179.2 + 11A239: leaks throws an exception, doesn't work + +dyld-179.1 + libdsc.a in Jasper SDK needs to be arm version (is x86 version) + +dyld-179 + update_dyld_shared_cache does not work with individual symbol re-exports + +dyld-178 + Support __thread variables exported from dylibs + update dyld_all_image_infos more often, for better crash logs + +dyld-177 + We need an extractor able to pull the shared symbols cache apart + Don't put slide info in dyld shared cache in Jasper + dyld does not prevent loading of unsigned dylib + +dyld-176 + split seg info wrong for x86_64 stub helpers + +dyld-175 + ObjC optimized shared cache does not slide + +dyld-174 + Need to merge Apex ObjC method list optimizations in dyld shared cache to TOT dyld + implement ASLR for dyld shared cache for iOS + +dyld-173 + fix permissions on rebased binary not slid + +dyld-172.2 + dyld should not include SystemIntegrity headers on embedded platform +Have DYLD_PRINT_BINDINGS print out when resolver functions are called + +dyld-172.1 + fix visibility of dyld_func_lookup depending if libdyld is .a or .dylib + +dyld-172 +Add hack to work around 8151909 +Alter project to build for both kinds of libSystems + +dyld-171 +Add support for individual symbol re-exports +Add support for building for iOS + +dyld-170 + ER: more metadata for dyld errors + +dyld-169 + update_dyld_shared_cache needs to support resolver functions +Implement some cxa stuff to work with next c++abi.a + +dyld-168 + dyld should move shared cache file into /var/run instead of open-unlinking + private shared cache calls mmap twice for each range + Loading MH_DYLIB_STUB causing coalescable miscount + 11A168: update_dyld_shared_cache problems during install + use of C++ thread local initializer causes infinite recursion + (from iPhoneOS) dyld should stub out mach_error_string for 8KB RAM savings per process + (from iPhoneOS) verify that replacement is in this image + +dyld-167.2 + dyld: Use libsystem_mach.a from /usr/local/lib/dyld to build dyld + +dyld-167.1 + dyld should move shared cache file into /tmp instead of open-unlinking + +dyld-167 + dyld support for function specialization + dyld should directly call __cxa_finalize(), part 2 + dyld support for thread local variables + + +dyld-166 + ER: facility to detect which images were loaded at runtime + update_dyld_shared_cache -verify fails + + +dyld-165 + update_dyld_shared_cache should suppress warnings for embedded frameworks + Sort method lists in dyld shared cache + + +dyld-164 + Move libdyldapis over to a dylib target for Libsystem + pruneEnvironmentVariables does not update gLinkContext.apple + + +dyld-163 + Warning message is misleading + update_dyld_shared_cache should not run past end of splitseginfo if zero terminator is missing + dyld should directly call __cxa_finalize(), part 1 + + +dyld-162 + crash in update_dyld_shared_cache when checking if current cache is out of date + ER: Support upward dylib dependencies + + +dyld-161 + __builtin_return_address broke for PPC + SWBDC error: 'ld: duplicate symbol' while building 'dyld-160~48' in '11A108a-llvmgcc-2324.3' + + +dyld-160 + Environment variable to cause dyld to dump rpaths used during loading + update_dyld_shared_cache failed: internal error, new trie allocated to far from fLinkeditBase + dyld still slides executables when PIE is disabled by the kernel + + +dyld-159 + update_dyld_shared_cache fails to find roots when targetting NFS + dyld does not call initializers from all S_MOD_INIT_FUNC_POINTERS sections + move _dyld_func_lookup prototype to dyld_priv.h + + +dyld-158 + dyld attempts to load libraries via rpath when already loaded + dyld shared cache can be more random + + +dyld-157 + switch to use libc++abi-static.a instead of libstdc++-static.a + + +dyld-156 + remove DYLD_NO_PIE from man page + support multiple dylibs with non-intersected weak symbols in shared cache + dyld sends bogus library unload notice to CoreSymbolication during dlopen_preflight() + spelling: "was build against" -> "was built against" + + +dyld-155 + sjlj based exception support needs to be conditionalized for arm - not iPhoneOS + + +dyld-154 + Need to enable CoreSymbolication load/unload notices on iPhoneOS + + +dyld-153 + add field to dyld_all_image_infos pointing to dyld_all_image_infos + + +dyld-152 + Minimize system calls and mach messages during dyld startup + Why is checkSharedRegionDisable() !__LP64__ + + +dyld-151 + dyld support for sliding PIE binaries in the kernel + libdyld does not install multiple headers at installhdrs time + dlopen() crash when executable contains LC_RPATH and executable file is deleted while running + leak in dyld during dlopen when using DYLD_ variables + + +---------------------------------- + +dyld-150 ( iPhoneOS 3.1 ) + flat_namespace linkage error in update_dyld_shared_cache should be a warning not an error + Have dyld save load addr + UUID for executable images + Libsystem fails to link with latest gcc: dyld missing _dyld_func_lookup + + +dyld-149 + dlopen() not working with non-canonical paths + update_dyld_shared_cache fails creating shared cache + + +dyld-148 + shared cache file offsets are inconsistent + + +dyld-147 + move install location for update_dyld_shared_cache and man page to local + imageFileModDate in dyld_all_image_infos is sometimes bogus for the first image therein + dyld_shared_cache_util should optionally print VM load addresses for each dylib + uuid_t not defined in dyld_priv.h + + +dyld-146 + Save load information (load addr + UUID) to dyld_all_image_infos for images from outside the shared cache + update_dyld_shared_cache should improve warning above deployment target + + +dyld-145 + optimize stubs in dyld shared cache to be no-PIC + dyld_shared_cache_util built by dyld target should go in platform directory + dyld_shared_cache_util should list LC_REEXPORT_DYLIB libraries as dependents + + +dyld-144 + dyld_shared_cache_util built by dyld target should go in platform directory + API: Detect shared cache overrides + + +dyld-143 + ER: Tool to list the contents of a dyld shared cache image + ARM support for dsc_iterator + + +dyld-142 + text relocs fail in large segments + Variable whose data is overwritten needs to be marked `volatile' + dyld option to print time for each initializer, Apple's or the app's + + +dyld-141 + update_dyld_shared_cache assumes -root path contains no symlinks +sync with SnowLeopard dyld-132.13 + + +dyld-140 + load code signature from file, and before mapping the file + + +dyld-139.1 + Northstar7C62: dyld-139 fails to build + + +dyld-139 + dyld on iPhoneOS uses libgcc_eh.a which uses pthread_key_create which does not work + dyld can leak when an internal exception in thrown + support compressed LINKEDIT on iPhone +sync with SnowLeopard dyld-132.11 + + +dyld-138.1 + New mechanism to instruct dyld whether to check for libs outside the shared cache + + +dyld-138 + need to handle symlinks when dylib is in cache but file is intentionally missing + dyld should avoid stat()ing libraries present in the shared cache unless magic override file is present + + +dyld-137 + dyld reports image not found if it is in the shared cache but no binary is present on disk +sync with SnowLeopard dyld-132.10 + + +dyld-136 + dyld reports bogus fat offset when registering shared cache signature + + +dyld-135 + iPhone: Need objc optimizations in iPhone OS shared cache + + +dyld-134 + build armv6 dyld with Thumb +sync with SnowLeopard + + + +dyld-133.1 +fix for all arm architectures + + +dyld-133 + make dyld that uses shared cache on iPhone OS + + +---------------------------------- + +dyld-132.13 ( Mac OS X 10.6 ) + classic images not unmapped when unloaded + + +dyld-132.12 + dyld's dtrace notification should be done after rebasing + + +dyld-132.11 + Remove dyld workaround for McAfee VirusScan + Add gating mechanism to dyld support system order file generation process + + +dyld-132.10 + dyld's abort_report_np() doesn't abort + 10A331: CUPS crashes when calling sandbox_init + + +dyld-132.9 + dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized + Exception Backtrace being obscured by _mh_execute_header + Silverlight Preferences.app crashes + + +dyld-132.8 + dlopen() leaks send right obtained from mach_thread_self() + dyld's calloc() allocates too few bytes + dyld lazy binding is not thread safe + + +dyld-132.7 + need a way other than setgid to have dyld launch a process securely, ignoring DYLD_ env vars etc + Need mapping of files to their offsets into the dyld_shared_cache files on disk + + +dyld-132.6 + update_dyld_shared_cache -overlay should check root dyld caches + NSCreateObjectFileImageFromMemory() call vm_deallocate even if application used malloc() + + +dyld-132.5 + dyld is missing some binding optimizations + symbol lookups in the new trie structure should be faster + weak binding done too early with inserted libraries + + +dyld-132.4 + improve statistics output + better error message if LC_RPATH is used in dylib destined for shared cache + dladdr() broke when image has debug symbols + 10A264: Google Earth 5.0 crashes on quit + man page should better explain update_dyld_shared_cache -root option + + +dyld-132.3 + remove setjmp/longjmp from _simple_printf in dyld + Typo in dyld(3) man page + race condition in pre-10.6 style lazy pointer binding + make cheaper dladdr() that just returns image path + 10A266 - Adobe InDesign CS4 crashes on launch + dyld is not setting imageFileModDate in dyld_image_info + Logic Pro crashes after creating a new document on SnowLeopard + + +dyld-132.2 + adopt new CoresSymbolication notification mechanism + Alter libdyld.a to not need libsystem to link with dylib1.o + 10A256a: update_dyld_shared_cache -verify crashes (probably due to lack of a shared cache) + + +dyld-132.1 + update_dyld_shared_cache failed: no writable segment in Cocoa framework + + +dyld-132 + CrashTracer: [USER] 1 crash in Neverwinter Nights 2 + + +dyld-131 + libgmalloc broken on 10A10246 and 10A251 (libgmalloc not inserted early enough) + + +dyld-130 + Rosetta circular dependency spew + @rpath and @loader_path Should be Documented in man page +Prune unneeded load commands from shared cache images + + +dyld-129 + dyld-128 no longer builds for armv6 + 10A244: iTunes crashes trying to load MobileDevice.framework + + +dyld-128 + ImageLoader objects can be made smaller + dyld spin in SecurityAgent + ImageLoaderMachO::makeImportSegmentWritable() doesn't do it + + +dyld-127 + Add all_image_infos for CrashReporter +some fixes for compressed LINKEDIT + + +dyld-126 + x86_64 export trie nodes may have large negative address + update_dyld_shared_cache man page should be updated to reflect new SL behavior + + +dyld-125 + @rpath and @loader_path should be documented in man page + Add NOP after trap instruction in _dyld_fatal_error + + +dyld-124 + update_dyld_shared_cache should not automatically run by default +Add support for arm shared caches +Add support for __program_vars section +Add more -verify sanity checks + + +dyld-123 + Add -verify option to update_dyld_shared_cache + [dyld] Errors reported by the upcoming compiler flags verifier + 10A224: update_dyld_shared_cache warns about a condition in B&I-built frameworks + stop shadowing old data structures + dyld implicit-libSystem breaks valgrind + + +dyld-122 + Need a way to determine if a gdb call to dlopen() would block + Drop Rosetta shared cache generation by default / at install + "terminaton function" misspelled + Make it easier to use shared caches from foreign systems + SnowLeopard10A210: MATLAB 7.6 crashes on launch + + +dyld-121.1 + CrashTracer: crash in iTunes at com.apple.QuickTimeComponents.component + + +dyld-121 + libdyld.a is missing dyld_stub_binder + + +dyld-120 + 10A197 - After Effects 8.0.2 fails to launch after installation + + +dyld-119 + 10A212: update_dyld_shared_cache failed: string buffer exhausted + Oracle client crashes + + +dyld-118.1 + Cope with duplicate protocol references in shared cache construction + + +dyld-118 + 10A197 vs 10A190: Applications warm launch time slowdown due to dyld + + +dyld-117 + 10A198 - Final Cut Pro 6.0.4 crashes on launch [INTRODUCED BY dyld-115 IN 10A197] + + +dyld-116 + Pandora Desktop (Adobe AIR Framework) crashes on launch [INTRODUCED BY dyld-101 IN 10A14] + dyld should use libunwind + Possible leak originating in speech at LoadEngine + update_dyld_shared_cache manpage typos 'parition', 'assignes', 'choosen' + + +dyld-115 + LINKEDIT content could be greatly compressed + + +dyld-114 + update_dyld_shared_cache needs to provide progress that the Installer can display + update_dyld_shared_cache needs to be able to look at an Installation sandbox + dyld isn't calling csdlc_notify on library unload + warning, could not bind Mail.app because realpath() failed on /AppleInternal/.../XILog.framework + + +dyld-113 + NSAddressOfSymbol(NULL) should return NULL and not crash + dlopen() should fail to load (non-pie) executables + + +dyld-112 + _replacement misspelled as _replacement in dyld-interposing.h + make _dyld_find_unwind_sections() faster + + +dyld-111 + improve bad relocation error message + Need load/unload notification sent to com.apple.vmudl service when requested + _replacement misspelled as _replacement in dyld-interposing.h + + +dyld-110 + check libSystem is correct in shared cache file before loading it + function names returned by dladdr do not match reality + update_dyld_shared_cache .map files should be written atomically + dlopen(RTLD_NOLOAD) does not need to throw an internal exception + Explanation for the RTLD_NEXT flag in dlsym(3) needs clarification + DYLD_FALLBACK_LIBRARY_PATH should not apply to dlopen() of a partial path + No shared cache present on first boot + + +dyld-109 + Safe Boot should disable dyld shared cache + CrashTracer: crash in preFetch() reading off end of LINKEDIT + DYLD_NO_PIE env variable should be documented in dyld manpage + put all dylibs in shared cache - not just ones used by more than one app + Leaked fSegmentsArray and image segments during failed dlopen_preflight + + +dyld-108.1 + armv7/armv5 specific settings needed for dyld + dyld shouldn't set VALID_ARCHS + + +dyld-108 + ER: dyld based Objective-C selector uniquing + + +dyld-107 + update_dyld_shared_cache should require all source dylibs be owned by root + Limit what might go in the dyld shared cache + DYLD_ROOT_PATH should apply to LC_RPATH rpaths + Grow initial dyld pool if needed + there should be some way to temporarily turn off -pie + If pie, ignore preferred load address + Put all_image_infos in its own section to it is easy to find + + +dyld-106 +allow update_dyld_shared_cache to be build 64-bit + dyld error handling fails in low memory, _simple_salloc() result not checked + dyld should provide executing program name in incompatible cpu-subtype error message + + +dyld-105 + It should work to set DYLD_FALLBACK_LIBRARY_PATH to empty + Remove the exceptions made in 4804594 for filemaker + Remove Tiger-era hacks in dyld for old app compatibility + Make RTLD_MAIN_ONLY public + die more gracefully when load commands are malformed + SWB: dyld build failure when built with -fstack-protector + dyld man page mis-spelling + update_dyld_shared_cache does not work when run manually + + +dyld-104 + The optimization to reuse install names as fPath was lost + Add JIT field to dyld_all_image_infos +Add _dyld_find_unwind_sections() + + +dyld-103 + NSLinkModule() can crash + dyld: obsoleted deprecated APIs +work around for +add test cases for lazy dylib loading + + +dyld-102 + Man page typo for update_dyld_shared_cache + Add "malloc is initialized" to the dyld_all_image_infos struct + better handling of open() errors + remove + Use instead of + dyld and libdyld should not force building with gcc 4.0 + + +dyld-101 + make it easier to find dyld_all_image_infos + Need _dyld_get_image_slide(const struct mach_header* mh) + dyld: push code signatures for libs to kernel + + +dyld-100 + dyld falls over when asked to map a split seg library not in the shared region + dyld: interposing does not work when replacee is thumb + dyld: all PIE programs crash on launch + BigBear: not loading libraries at their preferred addresses + dyld support for arm subtypes + dyld's prefetching should be turned off for prebound images (and perhaps entirely?) + dyld-95.3 doesn't build with gcc 4.2 +merge in arm support + ADOBE: Premiere Pro crashes on quit + +---------------------------------- + +dyld-96.2 (Mac OS X 10.5.2) + 10.5.2 Regression: 9C18 MeetingMaker crash on launch + +dyld-96.1 + update_dyld_shared_cache can crash if dylibs modified out from under it + crash when dyld interposes on system with partially invalid cache + com.apple.dyld message spew + CFSTRs cause crashes in Leopard + if system shuts down during update_dyld_shared_cache, tmp file is never cleaned up + dlopen() and dlopen_preflight() can leak on failure + + + +dyld-95.3 (Mac OS X 10.5) + Increase initial dyld pool size for 64-bit programs + + +dyld-95.2 + make ppc dyld cache a different file for rosetta + + +dyld-95.1 + McAfee VirusScan fails to launch on Leopard9A513 + + +dyld-95 + 9A516 - Keep getting disk full errors + + +dyld-94 + Leopard (9a499): dyld crash with recursive calls to dlclose() + + +dyld-93 + FileMaker Server 8.0v4 helper tools broken by @executable_path security change + Use msync(MS_SYNC) when building dyld cache + + +dyld-92 + Skype Crashes during launch + + +dyld-91.2 + dlopen() looks too far up stack, can cause crash + + +dyld-91.1 + dyld warning about dtracehelper is too noisy? + Lots of task_self_trap() system calls in ImageLoader::recursiveInitialization() + + +dyld-91 + use of @loader_path based RPATH can cause dyld to leak + Dyld_stubs should not be writable on x86 + + +dyld-90.2 + generating dyld shared cache generation on first boot makes time to MacBuddy very slow + + +dyld-90.1 + truncated dyld cache file after panic causes hang at boot + + +dyld-90 + stop special casing main executables initializers + DYLD_INSERT_LIBRARIES doesn't work correctly with initializer functions + + +dyld-89 + dyld could asynchronously request pages it will need + handle when argv[0] is NULL. + Foundation built on 9A436 doesn't launch 64 bit apps +partial fix for: Dyld_stubs should not be writable on x86 + + +dyld-88 + update_dyld_shared_cache keeps spewing messages to console + optimize LINKEDIT region of dyld shared cache + Support extended __dyld section with NXArgc, etc addresses +remove call to __xlocale_init() +Update __OPEN_SOURCE__ conditionals + + +dyld-87 + CFM games use private _dyld_fork_* routines - add back + better handling of NOLOAD with symlinked dylibs + + +dyld-86.1 +Fix DYLD_SHARED_REGION=private +update man page + + +dyld-86 + update_dyld_shared_cache fails on @executable_path framework + [Leopard]: 9A441/442: unable to log in after switching to arabic + dlopen via CFBundleLoad keeps searching after finding a loadable object + + +dyld-85.2 + MatLab 2007a (7.4) doesn't launch on Leopard9A441 +Never optimize ppc shared cache on intel +Fix LINKEDIT size in shared cache .map files +Fix how PIEs are moved to work with NX +Call pthread_init_keys to work with latest Libc + + +dyld-85.1 + Leopard9A447: Meeting Maker and Microsoft apps will not launch on Intel. + + +dyld-85 + 9A436: Adobe: Photoshop CS3 crashed on pressing Command-C after Command-A + Use _dyld_initializer + + +dyld-84 + 9A438 dlopen_preflight() corrupts all_image_info list causing Symbolication crashes + B&I needs an ENV variable to turn off "dyld: ioctl to register dtrace DOF section failed" warnings + remove support for __image_notify sections +remove all update_prebinding code + + +dyld-83 + use _simple_dprintf() instead of fprintf() +remove -progress option from update_dyld_shared_cache +update_dyld_shared_cache no longer tells rosetta to flush its caches + update_dyld_shared_cache error message gives an errno value rather than an error string + dyld interposing doesn't work with dlsym() lookup + dlopen_preflight() of MH_BUNDLE leaks + integrate ImageLoader changes into leopard dyld + translated (ppc) dyld should not attempt to register DOF sections + Some dyld library paths are not canonicalized, causing tools using those paths to defenestrate themselves + + +dyld-82.5 + REGR: Leopard9A419: Firefox hangs on launch + + +dyld-82.4 + Leopard9A420: interposing of libMallocDebug or libgmalloc broken +Fix so problems like are warnings instead of errors + + +dyld-82.3 + 9A420: dyld: ioctl to register dtrace DOF section failed + + +dyld-82.2 + dyld frees string returned by dyld_image_state_change_handler + better handling than "corrupt binary, library ordinal too big" + + +dyld-82.1 + dyld changes needed to support read-only DOF + + + +dyld-82 + don't need to hold dyld global lock while running initializers + dyld leaks when dlopen fails + dyld leaks two blocks after bundle unload + + +dyld-81 + auto update dyld shared caches if missing or out of date + + +dyld-80.1 + Erronious "unsafe use of @rpath" dyld error + 9A384: update_dyld_shared_cache fails after ditto'ing matador root with debug info + Uninitialized ImageLoader->fRegisteredDOF field + + +dyld-80 (Leopard9A400) + Use new shared region syscalls + @rpath does not work with -rpath @executable_path/... + Firefox causes segfault during update_dyld_shared_cache + + +dyld-79.3 + Use new shared region syscalls + +dyld-79.2 + @rpath does not work with -rpath @executable_path/... + +dyld-79.1 (Leopard9A396) +fix use of LC_REEXPORTED_DYLIB + + +dyld-79 (Leopard9A392) + Support Apple PIE (address space randomization) + update_dyld_shared_cache should warning and not quit if a specified root is missing + DOF registration needs to switch from /dev/helper to /dev/dtracehelper + don't error out when a cache line crossing i386 stub cannot be bound + + +dyld-78.2 (Leopard9A387) + 9A385: Mail (anything using Message.framework) takes a ridiculous amount of time to launch + + +dyld-78.1 (Leopard9A385) +Fix override of _malloc_lock to be data instead of code + + +dyld-78 + when loading a bundle, dyld is not making a copy of name + 9A343 KidPix 3 : SpellChecker bundle is not loaded + dyld cache does not recognize dynamic load of library via symbolic link + + +dyld-77.1 +Back out 4892382 until B&I build fleet has fixed kernel + + +dyld-77 + Use _dyld_initializer + Look at reduction/elimination of per-framework cost (don't touch __dyld section) + libdyldapis.a: make initialization as lean as possible + dyld should malloc never-to-be-freed blocks from its own pool + Libraries feeding into Libsystem should contain version numbers (libdyldapis) +Install update_prebinding symlink +Addend warnings to end of shared cache .map files +Conditionalize away update_prebinding support +dladdr() should not remove 's' from "start" + + +dyld-76.2 + hang at boot, Libc changes + +dyld-76.1 + x86_64: dyld share cache does not work for AppKit + + +dyld-76 + Rosetta apps crash after update_dyld_shared_cache + Long-standing typo for "file to short" error from dlopen() / dlerror() + + +dyld-75.1 +Enable ppc shared cache generation on intel machines + + +dyld-75 + 64-byte crossing fast stubs should be bound early to avoid threading issues + support new intel stub segment that is marked not-writable + + +dyld-74 + register dtrace DOF sections with kernel + 10.4.9 Regression: Math Kernel crash with TiNovi 8P114 + + +dyld-73.2 + Leopard 9A921: Dyld error "lazy pointer not found" loading/running java + + +dyld-73.1 (Leopard9A328) + 9A326: update_prebinding crashes at end of install + + +dyld-73 (Leopard9A326) + REGR: 9A322 All Java apps crashing at dyld's misaligned_stack_error + + +dyld-72 (Leopard9A322) + Maya 8 crashes on launch on Leopard9A309 + ProTools 7.1.1cs1 for Intel hangs on launch on Leopard9A309 + x86 crashes in the binding helper do not have proper backtrace + + +dyld-71 (Leopard9A320) + inform rosetta of each library in the dyld shared cache + 9A316: Dreamweaver MX 2004 crashes on launch in dyld's addImage + + +dyld-70 (Leopard9A315) +support split-seg dylibs built with LC_SEGMENT_SPLIT_INFO +support --progress option in update_dyld_shared_cache so that installer can run it + + +dyld-69.1 (Leopard9A309) + 9A305: Firefox 2.0 crashes on launch + httpd is dying in dyld after libobjc.dylib is unloaded + + +dyld-69 (Leopard9A305) + ER: dlclose() should be able to unload dylibs + runtime support for RPATH + + +dyld-68 (Leopard9A296) + rosetta doesn't work when shared cache is present + shared cache for 64-bit archs does not work when some dylibs are invalid + + +dyld-67.1 (Leopard9A292) + support 64-bit programs built with new 10.5 subtype + + +dyld-67 + CrashReporter needs a new way to distinguish fatal dyld errors + support dlopen(NULL, RTLD_FIRST) +Move base address of ppc64 dyld to match new memory layout +Move base address of 64-bit shared caches to match new memory layout +Move location of shared cache file to /var/db/dyld +Add support for LC_REEXPORT_DYLIB +Use shared cache if it exists +Requires ld64-63.1 or later built dylibs for shared cache generation + + +dyld-66.3 (Leopard9A276) + dyld fails to build with Libc changes in 4632326 + support 64-bit programs built with new 10.5 subtype + + +dyld-66.2 (Leopard9A260) + Leopard9A259: Backtraces in crash reports are not getting symbolicated + dyld should get rosetta process name from standard location + + +dyld-66.1 (Leopard9A259) +Fix for build breakage with Libc-436 + + +dyld-66 +Preliminary shared cache support + is in Darwin but not Dev Tools package + export shared range regions for gdb + __pthread_tsd_first appears to be initialized too late in dyld + don't use @executable_path or fallback searching in setuid programs + + +dyld-65.1 (Leopard9A252) +fix B&I build failure with Libc-435 + + +dyld-65 (Leopard9A247) + jump table entry at end of segment can crash + Mathematica 5.2 (64-bit MathKernel) always crashes on 9A229 + dlsym man page needs to be more specific about RTLD_DEFAULT + Change wording in SEARCHING section in dlopen man page + Man page for dyld has misspelling: cheep + dyld(3) man page should point to Mach-O Programming Topics URL + + +dyld-64 (Leopard9A224) + No man page for dlopen_preflight(3) + dyld lazy binding of fast stubs is not completely thread safe + remove use of load_shared_file() + + +dyld-63 (Leopard9A215) + Would like way to quiet dynamic loader + deprecated old APIs for Leopard + NSCreateObjectFileImageFromMemory crashes when image is a MH_EXECUTABLE + + +dyld-62.1 (Leopard9A206) + prebound old stubs failure prevents uTest from running on i386 + + +dyld-62 (Leopard9A202) + an image with an open non-zero base address does not load more efficiently + Leopard9A190: NSAddImage() crashes when called from x86_64 process + /usr/lib/libAPSimple.dylib: mmap error + need to force interposing for rosetta processes + + +dyld-61 (Leopard9A179) + dyld calls close(-1) + _stub_binding_helper_interface isn't using movdqa + dyld tries to mmap 0 size segments and fails with conforming mmap + Load dyld above 4GB for x86-64 +Move apple parameters on stack when DYLD_ variables are removed + + +dyld-60 (Leopard9A160) + Suresec #203: dyld environment with suid binaries + SureSec si#187 linker: environment variables + print warning message if DYLD_INSERT_LIBRARIES is set (then ignored) for a setuid/setgid program + dyld's disableIfBadUser() routine can fail for constant strings + + +dyld-59 (Leopard9A156) + ER: dlopen_preflight() + + +dyld-58 (Leopard9A154) + implement RTLD_SELF + better handling of open() errors + would like dlopen(RTLD_FIRST) so that dlsym variant that does not search dependent libraries + dyld needs to adopt to Unix conformance changes + Crash on Leopard9A146 when GC rejects loading a library on Intel + + +dyld-57 (Leopard9A144) + pthread tsd entries doubly owned by DYLD and Libsystem... (Leopard) + dyld should automatically stop using the shared region if unavailable + If instantiateFromLoadedImage fails, dyld crashes + isCompatibleMachO needs to know about x86-64 + + +dyld-56 (Leopard9A140) + 64-bit dyld should load at around the last 4G - not in the first 4G + 64 bit: app crashes immediately if -pagezero_size >= 0x0ffffffff + dyld needs to build for x86-64 + dyld_debug API shim has leak + dyld does not slide properly on Intel + + +dyld-55 (Leopard9A138) + dlopen() should fail if bundle has objc code incompatible with runtime environment + libdyld: make non-POSIX header definitions visible when _POSIX_C_SOURCE is defined + A flat_namespace image with a reference to an internal private_extern should resolve immediately + dlopen() man page is missing RTLD_NOLOAD and RTLD_NODELETE + _CFExecutableLinkedOnOrAfter() fails on 64 bit + dyld needs to support x86-64 + + +dyld-54 (Leopard9A80) + remove ppc64 workarounds for fixed bugs + Memory error in removePathWithPrefix() + dyld does not properly swap CPU subtype from fat file header + dyld does not compile for open source + ADOBE XCODE 2.2: ZeroLink can cause wrong typeinfos to be used +Sync with Chardonnay (except for 4313830 and 4215516) + + +dyld-53 (Leopard9A42) + Add -fno-exceptions to Release target of libdyld + Wrong number of seconds reported by DYLD_PRINT_STATISTICS on Intel + + +dyld-52 (Leopard9Axxx) + dyld changes for new libstdc++ project + + +dyld-51 (Clueless) + STD:VSX: dlclose() should return non-zero value on failure. + STD:BUILD: dyld references non-conforming member name for PPC64 + The gdb list of images should be updated before dyld bases and binds each image + interposing does not handle stacking/nesting of interposing functions + use of DYLD_INTERPOSE() causes compiler warnings with -std=c99 -pedantic + SWB: dyld-32 fails to link using 4.0 compiler (gcc-4042) on Tiger8A371 + +dyld-50 (Leopard9Axxx) +Convert to build with gcc-4.0 + SWB: dyld-32 fails to link using 4.0 compiler (gcc-4042) on Tiger8A371 + +---------------------------------- + + + +dyld-46.16 (Mac OS X 10.4.11) + raise bail out limit in update_prebinding to 100 errors + +dyld-46.15 + [SUIncaSoho] update_prebinding can only handle 2MB of ppc unprebound dylibs on intel + update_prebinding fails if a prebound dylib depends on a non-prebound dylib + +dyld-46.14 + prebinding zeroes out files if an error occurs (such as no vm space left) + Rare & unknown root cause: Corruption of Kerberos framework during SU + +dyld-46.13 + dyld crashes starting threaded program + 10.4.9 Regression: SuTiNovi8P132: update_prebinding never reaches steady state + 10.4.9 Regression: SuTiNovi8P132: update_prebinding never reaches steady state + [SUTiSoHo] update_prebinding crashes when a weak linked dylib is missing + [SUIncaSoHo] update_prebinding crashes when a weak linked dylib is missing + +dyld-46.12 + 10.4.9 Regression: Math Kernel crash with TiNovi 8P114 + +dyld-46.11 + dyld's x86_64 support should be open source + 10.4.x: an image with an open non-zero base address does not load more efficiently + [SUTiNovi] A flat_namespace image with a reference to an internal private_extern should resolve immediately + [SUTiNovi] 10.4.8 regression: ppc X program segmentation fault in 10.4.8, worked in 10.4.7 + [SUIncaNovi] 10.4.8 regression: ppc X program segmentation fault in 10.4.8, worked in 10.4.7 + [SUIncaNovi] A flat_namespace image with a reference to an internal private_extern should resolve immediately + +dyld-46.10 + dyld-46.9 fails to build in Nicoya + +dyld-46.9 (Inca8K...) + jump table entry at end of segment can crash + +dyld-46.8 (Inca8K1073) + Mathematica 5.2 (64-bit MathKernel) always crashes on Inca + +dyld-46.7 (Inca8K1072) + dyld lazy binding of fast stubs is not completely thread safe + +dyld-46.6 (Inca8K1061) + Inca: don't write warning to stderr for setuid binaries + +dyld-46.5 (Inca8K1059) + prebound old stubs failure prevents uTest from running on i386 + +dyld-46.4 (Inca8K1057) + NSAddImage() crashes when called from x86_64 process + an image with an open non-zero base address does not load more efficiently + +dyld-46.3 (Inca8K1054) + need to force interposing for rosetta processes +re-enable setuid security fixes for 4525062 and 4525053 + +dyld-46.2 (Inca8K1046) + Adobe CS2 no longer launches + Load dyld above 4GB for x86-64 + +dyld-46.1 (Inca8K1040) +rdar://problem/4538177 dyld does not slide on x86_64 + +dyld-46 (Inca...) +re-enable x86_64 + + +dyld-45.3 (SUSecurity ) + *SecUpd: Chardonnay* don't write warning to stderr for setuid binaries + *SecUpd: Tiger* don't write warning to stderr for setuid binaries + +dyld-45.2 (SUSecurity ) + *SecUpd: Chardonnay* SureSec si#187 remove all DYLD_ env vars for setuid binaries + *SecUpd: Tiger* SureSec si#187 remove all DYLD_ env vars for setuid binaries + *SecUpd: Chardonnay* Suresec #203: don't use $HOME with suid binaries + *SecUpd: Tiger* Suresec #203: don't use $HOME with suid binaries + + +dyld-45.1 (SUTiLondon...) +back out Suresec #203: dyld environment with suid binaries [SUTi] + + +dyld-45 (SUTiLondon...) + sync all 10.4.x dyld trains + dyld fix for gcc-3.3 C++ needs to get in SU + 64-bit dlopen crashes when opening fat bundle + SureSec si#187 linker: environment variables [SUTi] + Suresec #203: dyld environment with suid binaries [SUTi] + SureSec si#187 linker: environment variables [SUChard] + Suresec #203: dyld environment with suid binaries [SUChard] + +dyld-44.23 (Inca8...) + Crash using Core Image under Rosetta running InDesign CS2 w/ Magma Effects + +dyld-44.22 (Inca8K1030) + Stub binding helper changes for FP args for x86-64 + +dyld-44.21 (Inca8K1030) + printf doesn't work for x86-64 + +dyld-44.20 (Inca8K1029) + isCompatibleMachO needs to know about x86-64 + +dyld-44.19 (Inca8J1028) +two small x86_64 fixes + +dyld-44.18 (Inca8J1027) + dyld needs to support x86-64 + +dyld-44.17 (Chardonnay8G1152) + prebound fast stubs not ignored for flat_namespace dylibs + +dyld-44.16 (Chardonnay8G1141) + Sherlock often crashes in dyld::bindLazySymbol on launch + +dyld-44.15 (Chardonnay8G1137) + no apps can launch with /usr/lib/libMallocDebug.A.dylib on 8G1133 + +dyld-44.14 (Chardonnay8F1110) + System Integrity: changes needed for dyld + +dyld-44.13 (Chardonnay8F1108) + pthread tsd entries doubly owned by DYLD and Libsystem... (Chardonnay) + +dyld-44.12 (Chardonnay8F1108) + never pass shared_region_map_file_np() a zero-length region + +dyld-44.11 (Chardonnay8F1104) + dyld launch code should special case if there is only one prebound image with weak exports + +dyld-44.10 (Chardonnay8F1100) + dyld fails to link with Libc-391.1.13 + +dyld-44.9 (Chardonnay8F1093) + XCode2.1 + gcc 3.3 + C++ exception + Bundle Bug + low disk space code path executed with 44GB free + +dyld-44.8 (Chardonnay8F1079) + dyld lazy binding code should use movdqa + +dyld-44.7 (Chardonnay8B1072) + fix for rosetta crashes + +dyld-44.6 (Chardonnay8B1072) + Optimizing system should only progress once + +dyld-44.5 (Chardonnay8B1052) + New intel stub support + +dyld-44.4 (Chardonnay8B1051) + Leopard 9A14: Finder crashes burning a CD + dyld lazy binding code needs to save/restore all possible register parameters + use new libstdc++-static + +dyld-44.3 (Chardonnay8B1051) + dyld should recognize byte-swapped Mach-O files + dyld should only update prebound external relocations if they change + +dyld-44.2 (SUTiDenver8F10) [Mac OS X 10.4.3] + Tiger breaks NSCreateObjectFileImageFromMemory with fat Mach-O + dyld does not load libraries correctly if matching install_name in /usr/lib + CrashTracer: ..269 crashes at com.adobe.Acrobat.framework: RunAcrobat + 424 + +dyld-44.1 (Chardonnay) + __attribute__ ((regparm (3), stdcall)) doesn't work for inter-image calls + dyld should automatically set DYLD_SHARED_REGION to avoid + +dyld-44 (Chardonnay) + merge SUTiCambridge dyld and Karma dyld into Chardonnay + + +dyld-43.1 (SUTiCambridge8C20) [Mac OS X 10.4.2] +Update open source APSL headers + update_prebinding should gracefully handle low disk space + prebinding should not change n_value of .obj_class_name symbols + dyld gets into infinite loop with dlsym if dylib dependencies contain loops + FilesBuster crashed in dyld + DYLD_ROOT_PATH crashes program if any component does not start with / + dyld writes past end of buffer when checking environment variables + dyld_image_removing notification never sent + ANN: DR020 + + +dyld-43 (Tiger8A428) [Mac OS X 10.4.0] +rdar://problem/4067311 PACE protected Mach-O apps crash under Tiger 8a420 + + +dyld-42 (Tiger8A420) +rdar://problem/4058724 FileMaker JDBC extension does not work in Tiger + + +dyld-41 (Tiger8A417) +rdar://problem/4047633 Adobe Photoshop CS2: Scripting Save for Web runs out of memory unexpectedly when compared to 10.3.8. + + +dyld-40 (Tiger8A413) +rdar://problem/4047391 dyld no longer follow dependent libraries using dlopen + + +dyld-39 (Tiger8A406) +rdar://problem/4034570 DT P1: MATLAB R14sp2 does not run on Tiger +rdar://problem/4028274 DT P1: GLSLShowpieces crashes when built and run + + +dyld-38 (Tiger8A402) +rdar://problem/3820219 Tiger + MOTU Digital Performer + hit play = Digital Performer crashes +rdar://problem/3978682 If an excutable & a framework with the same base name are in the same folder, you get a dyld error if you use full path +rdar://problem/3987135 MATLAB unresolved symbol __XEditResPutWidgetInfo on Tiger +rdar://problem/4027813 dlsym man page documents unsupported RTDL_SELF + + +dyld-37 (Tiger8A399) +rdar://problem/4001668 Safari hang inside CFBundleDYLDGetSymbolByNameWithSearch in Flash plug-in upon opening www.espn.com +rdar://problem/3853454 dyld needs to call sys_icache_invalidate() after instruction fix-ups +rdar://problem/3984074 Soldier of Fortune II crashes at start of gameplay on Tiger8A36 +rdar://problem/4008399 Malicious user can set Tiger dyld fallback paths in setuid process +rdar://problem/4021002 Aliens vs Predator II with latest update does not run + + +dyld-36 (Tiger8A393) +rdar://problem/3970385 [Tiger] 8A323: Aliens VS Predator 2 Freezes On Launch +rdar://problem/3839120 _dyld_get_image_header_containing_address returning NULL when it shouldn't +rdar://problem/3925105 update_prebinding crashes on /usr/lib/libnetsnmptrapd.5.dylib + + +dyld-35 (Tiger8A384) +rdar://problem/3984074 Soldier of Fortune II crashes at start of gameplay on Tiger8A367 +rdar://problem/4003637 Typo blocks support of -init for 64-bits +rdar://problem/4003891 improve mmap() failure error msg + + +dyld-34 (Tiger8A381) +rdar://problem/3976215 dlopen() should look for a library with matching name and architecture +rdar://problem/3978682 executable and dylibs can be confused +rdar://problem/3819111 dlopen() ignores LD_LIBRARY_PATH +rdar://problem/3956709 Insufficient documentation for dlopen + + +dyld-33 (Tiger8A379) +rdar://problem/3941826 8A340: Tron 2.0 does not launch +rdar://problem/3848965 James Bond 007 Nightfire fails to launch +rdar://problem/3947513 No One Lives Forever 2 crashes on Quit on Tiger8A347 +rdar://problem/3779999 Spyhunter crashes upon launch on Tiger8A244 + + +dyld-32 (Tiger8A367) +rdar://problem/3974486 PowerMail launch fails: weak linking of library but not symbols +rdar://problem/3974797 can update_prebinding only complain about stuff I actually have installed? +rdar://problem/3979715 synchronizing attribute name with section name + + +dyld-31 (Tiger8A362) +rdar://problem/3958479 Third party "crash reporter" app backtraces have no symbols for our libraries/APIs +rdar://problem/3966025 add DYLD_INTERPOSE macro to an apple internal header +rdar://problem/3968283 For interposing support section-by-type and section-by-name + + +dyld-30 (Tiger8A357) +rdar://problem/3947090 objc runtime / two-level namespace crash when using new interposing libMallocDebug or libgmalloc + + +dyld-29 (Tiger8A356) +rdar://problem/3960729 update_prebinding needs to fflush() every output line +rdar://problem/3960657 Why are my crashreporter logs all useless? + + +dyld-28 (Tiger8A354) +rdar://problem/3510780 _dyld_get_image_header_containing_address man page is wrong +rdar://problem/3798074 STD: dlclose() does not unload bundles +rdar://problem/3882857 dyld no longer finds framework file if not in Framework hierarchy +rdar://problem/3898206 dlopen() does not support library searching as linkage at launch does +rdar://problem/3921479 dyld(1) should not document unimplemented env vars +rdar://problem/3935714 Support RTLD_NODELETE +rdar://problem/3942919 can't use _debug libSystem + + +dyld-27 (Tiger8A350) +rdar://problem/3902088 update_prebinding needs to provide progress information + + +dyld-26 (Tiger8A348) +rdar://problem/3943033 hang upon boot, permissions and startup thrash + + +dyld-25 (Tiger8A347) +rdar://problem/3916220 Main executable unmapped +rdar://problem/3812732 Microsoft Error Reporting crashes on Tiger +rdar://problem/3943349 dyld project should install mach-o/dyld_debug.h +rdar://problem/3943546 mach-o/dyld_debug.h should be deprecated +rdar://problem/3933738 use getsectdatafromheader_64 + + +dyld-24 (Tiger8A345) +rdar://problem/3941688 update_prebinding needs a --dry-run option + + +dyld-23 +rdar://problem/3799069 Need coalescing across dylibs +rdar://problem/3859973 problem with weaklink using darwine +rdar://problem/3938167 dyld does not work with XLF generated binaries and provided libraries +rdar://problem/3935377 STD: dyld needs to include sys/time.h and sys/types.h +rdar://problem/3930361 STD: dyld must create a stub for ___fegetfltrounds +rdar://problem/3934712 STD:VSX: more dlfcn.h namespace issues + + +dyld-22.5 (Tiger8A340) +rdar://problem/3788633 MatLab 7.0 fails to launch on Tiger8A246 +rdar://problem/3919674 prebound flat-namespace libraries are bound wrong + + +dyld-22.4 +rdar://problem/3915914 make update_prebinding more robust + + +dyld-22.3 (Tiger8A333) +rdar://problem/3920720 dyld missing symbols because Xcode not add -fvisibility=hidden + + +dyld-22.2 (Tiger8A330) +rdar://problem/3909873 Transmit.app crashes with symbol not found + + +dyld-22.1 +rdar://problem/3908248 Crash in apps when launched from MallocDebug (BlockMove symbol not found if DYLD_FORCE_FLAT_NAMESPACE is set) + + +dyld-22 (Tiger8A325) +rdar://problem/3903866 reduce LINKEDIT page usage by better use of two-level hints +rdar://problem/3884004 Libraries can be half-baked if an error occurs during their first use +rdar://problem/3899047 interposing doesn't work on 8A317 with dyld-21 +rdar://problem/3514720 Adobe bundle kernel panics system + + +dyld-21 (Tiger8A322) +rdar://problem/3745562 Support two-level interposing with insert libraries (malloc-debug) +rdar://problem/3892061 8A316: Selecting any application in Finder in the column view crashes finder +rdar://problem/3894540 DYLD_PRINT_SEGMENTS no longer works on 8A317 + + +dyld-20 (Tiger8A317) +rdar://problem/32957877 Support @loader_path in dylib commands +rdar://problem/33837173 need SPI to set fallback symbol lookup +rdar://problem/33891778 dyld (__DATA,__dyld) function pointers should drift as little as possible +Fix issue with new prebinding overwriting symlinks + + +dyld-19 (Tiger8A315) +rdar://problem/3823664 dyld reports corrupt executable +rdar://problem/3847571 dyld submission number should be embedded in dyld +rdar://problem/3865141 dyld_all_image_infos should contain bit that says if process is using the shared region +rdar://problem/3884103 dyld needs to use -msoft-float for ppc64 +rdar://problem/3886337 PPC_RELOC_PB_LA_PTR not recognized by ppc64 +Clean up/document dyld-gdb interface + + +dyld-18.3 (prebinding sideworld build) +prebinding enhancements + +dyld-18.2 (Tiger8A306) +rdar://problem/3782982 Fix for Macromedia apps (Flash 7.2) + +dyld-18.1 +rdar://problem/3864644 DVD player crash (loading memory based bundle) + +dyld-18 (Tiger8A303) +rdar://problem/3866877 STD:VSX dlsym() faults when passed a bad handle. +rdar://problem/3862043 typo in dyld function name +rdar://problem/3857000 dyld should limit its exported symbols +rdar://problem/3835208 want better error message for incompatible libraries +dyld now built with dead-code-stripping +better launch performance when hundreds of libraries are used +more bug fixes in update_prebinding in dyld + + +dyld-17.1 (Tiger8A301) +rdar://problem/3672757 initial implementation of update_prebinding in dyld +rdar://problem/3847376 dyld does not slide itself properly (partial fix) +rdar://problem/3843028 STD: make dlfcn.h posix compliant +rdar://problem/3856182 STD: add includes +rdar://problem/3782982 infrastructure work on compatibility for Macromedia apps +Other 64-bit tweaks + + +dyld-16.1 (Tiger8A296) +rdar://problem/3768530 dyld should adopt shared_region_map_file_np() + + +dyld-15 (Gordian 64-bit side world build only) +rdar://problem/3834744 64-bit dyld needs to handle mh->cputype != host_info cputype +Other 64-bit clean ups + + +dyld-14 (Tiger8A280) +rdar://problem/3811777 Deadlock in dyld code, seen in Merlin and Mail +rdar://problem/3793075 Update to fix: DVD Player crashes at launch with disc in drive +rdar://problem/3830560 Don't let gcc-3.5 optimize away send_event +rdar://problem/3826169 Update dlfcn.h header to Apple header and acknowledge Peter and Jorge +rdar://problem/3793861 Update dlopen(3) man page to reflect new implementation +rdar://problem/3559013 libdyld.a needs to build for ppc64 +Added DYLD_PRINT_OPTS +Added DYLD_PRINT_ENV +Deprecated 10 APIs + + +dyld-13 (Gordian 64-bit side world build only) +rdar://problem/3560664 Add ppc64 support +rdar://problem/3819144 Opening PDF in Safari crashes +rdar://problem/3793861 Update dlopen man pages +rdar://problem/3765271 Build with gcc-3.5 +rdar://problem/3762685 Make own prototype for task_self_trap() +rdar://problem/3799467 Mozilla tries to open a dylib using a bundle API +rdar://problem/3812263 Add dyld SPI so Shark can interpose lazy pointer lookup +Added emacs major-mode and tab-width directives + + +dyld-12.4 (Tiger8A271) +tweak so dyld can use libc.a built with long double support + + +dyld-12.3 (no-build) +rdar://problem/3765271 switch dyld to build with gcc-3.5 + + +dyld-12.2 (Tiger8A265) +rdar://problem/3793075 DVD Player crashes at launch with disc in drive + + +dyld-12.1 (Tiger8A255) +rdar://problem/3749360 a flat lookup needs to search everywhere and not short-circuit to own image + + +dyld-12 (Tiger8A231) +rdar://problem/3751226 make dyld thread safe (libdyld.a should come from new dyld project) +rdar://problem/3654650 integrate dlopen() and friends into dyld + + +dyld-11.3 (Tiger8A225) +rdar://problem/3751226 NSAddImage() with leaf name doesn't use fallback path + + +dyld-11.1 (Tiger8A223) +rdar://problem/3749251 NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED implies NSADDIMAGE_OPTION_RETURN_ON_ERROR" + + +dyld-11 (Tiger8A220) +rdar://problem/3684168 prevent DYLD_LIBRARY_PATH from causing circular reference when two dylib have same leaf name +rdar://problem/3698212 implement _dyld_launched_prebound() +rdar://problem/3696924 suppport frameworks without symlink to current version +rdar://problem/3692136 Support DYLD_PRINT_LIBRARIES_POST_LAUNCH +rdar://problem/3685517 make find-image-for-address faster via caching +rdar://problem/3661976 -force_flat_namespace should disable prebinding +rdar://problem/3702311 make dyld's __TEXT read-only +rdar://problem/3669059 do cpu sub-type checking on thin files +rdar://problem/3696002 Support C++ vague linkage that resolves internally +rdar://problem/3725847 run initializers in inserted libraries +rdar://problem/3731063 pass arc/argv to initializers +rdar://problem/3731100 don't write on __dyld section unless necessary +rdar://problem/3731633 use mmap() instead of map_fd() +rdar://problem/3676934 don't double read first page of images, use pread() +rdar://problem/3725372 ignore environment variables for setuid binaries + + +dyld-10.2 (Tiger8A157) +rdar://problem/3668765 Macromedia apps don't launch + + +dyld-10.1 (Tiger8A156) +rdar://problem/3691952 Fix-n-continue broke in Objective-C + + +dyld-10 (Tiger8A148) +rdar://problem/3686995 implement dyld_call_module_initializers_for_dylib and do parameter sanity checking of module parameters +rdar://problem/3674139 Make @executable_path work when main executable is a symlink +rdar://problem/3688719 Support CW built binaries built with -data_before + + +dyld-9 +rdar://problem/3682744 GoLive crash (NSIsSymbolDefinedWithHint should try harder) +rdar://problem/3660691 iTunes crash with partial plugin +rdar://problem/3684167 DYLD_IMAGE_SUFFIX broke +rdar://problem/3672670 Fix uninitialized variable in objc hook + + +dyld-8 (Tiger8A144) +rdar://problem/3680627 handle weaklib when prebound +rdar://problem/3672670 new hook for objc + + +dyld-7 (Tiger8A141) +rdar://problem/3669059 Support cpu-sub-type selection +rdar://problem/3675131 allow text relocations +rdar://problem/3675614 fix lazy pointer usage in termination routines +rdar://problem/3673658 Allow NSUnLinkModule to be called with NULL + + +dyld-6 (Tiger8A139) +rdar://problem/3649313 clean up +rdar://problem/3661976 support MH_FORCE_FLAT +rdar://problem/3659666 DYLD_FORCE_FLAT_NAMESPACE should disable prebinding +Better error reporting +Faster flat namespace lookups +Better implementation of NSLinkEditError + + +dyld-5 (Tiger8A135) +rdar://problem/3665738 fix binding of private_extern within multi-module libs +rdar://problem/3667763 use hints supplied in APIs +Set error_string for CrashReporter +Better error message when library can't be found +Properly use ZeroLink bundle notifier + + +dyld-4 (Tiger8A133) +rdar://problem/3663433 allmemory shows 8000 pages are missing + + +dyld-3 +rdar://problem/3659408 Proper searching in umbrellas +rdar://problem/3657040 Better error messages when symbols not found +rdar://problem/3657197 Add ~/Library/Frameworks to fallback path +Properly use DYLD_IMAG_SUFFIX +Initial CrashReporter support + + +dyld-2 (Tiger8A132) +Support: NSNameOfSymbol, NSModuleForSymbol, NSLibraryNameForModule, and NSMakePrivateModulePublic +Add more functionality to DYLD_IGNORE_PREBINDING environment variable + + +dyld-1 +Initial submission of dyld rewrite. diff --git a/dyld/doc/man/man1/closured.1 b/dyld/doc/man/man1/closured.1 new file mode 100644 index 0000000..a13b005 --- /dev/null +++ b/dyld/doc/man/man1/closured.1 @@ -0,0 +1,10 @@ +.Dd 3/1/17 +.Dt closured 1 +.Os Darwin +.Sh NAME +.Nm closured +.Nd Daemon for building dyld closures. +.Sh SYNOPSIS +.Nm closured is a launchd managed daemon. +.Sh DESCRIPTION +.Nm closured is a launchd managed daemon. diff --git a/dyld/doc/man/man1/dyld.1 b/dyld/doc/man/man1/dyld.1 new file mode 100644 index 0000000..3c58b80 --- /dev/null +++ b/dyld/doc/man/man1/dyld.1 @@ -0,0 +1,300 @@ +.TH DYLD 1 "June 1, 2017" "Apple Inc." +.SH NAME +dyld \- the dynamic linker +.SH SYNOPSIS +DYLD_FRAMEWORK_PATH +.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_PRINT_TO_FILE +.br +DYLD_SHARED_REGION +.br +DYLD_INSERT_LIBRARIES +.br +DYLD_FORCE_FLAT_NAMESPACE +.br +DYLD_IMAGE_SUFFIX +.br +DYLD_PRINT_OPTS +.br +DYLD_PRINT_ENV +.br +DYLD_PRINT_LIBRARIES +.br +DYLD_BIND_AT_LAUNCH +.br +DYLD_DISABLE_DOFS +.br +DYLD_PRINT_APIS +.br +DYLD_PRINT_BINDINGS +.br +DYLD_PRINT_INITIALIZERS +.br +DYLD_PRINT_REBASINGS +.br +DYLD_PRINT_SEGMENTS +.br +DYLD_PRINT_STATISTICS +.br +DYLD_PRINT_DOFS +.br +DYLD_PRINT_RPATHS +.br +DYLD_SHARED_CACHE_DIR +.br +DYLD_SHARED_CACHE_DONT_VALIDATE +.SH DESCRIPTION +The dynamic linker checks the following environment variables during the launch +of each process. +.br +.br +Note: If System Integrity Protection is enabled, these environment variables are ignored +when executing binaries protected by System Integrity Protection. +.TP +.B DYLD_FRAMEWORK_PATH +This is a colon separated list of directories that contain frameworks. +The dynamic linker searches these directories before it searches for the +framework by its install name. +It allows you to test new versions of existing +frameworks. (A framework is a library install name that ends in the form +XXX.framework/Versions/YYY/XXX or XXX.framework/XXX, where XXX and YYY are any +name.) +.IP +For each framework that a program uses, the dynamic linker looks for the +framework in each directory in +.SM DYLD_FRAMEWORK_PATH +in turn. If it looks in all the directories and can't find the framework, it +searches the directories in +.SM DYLD_LIBRARY_PATH +in turn. If it still can't find the framework, it then searches +.SM DYLD_FALLBACK_FRAMEWORK_PATH +and +.SM DYLD_FALLBACK_LIBRARY_PATH +in turn. +.IP +Use the +.B \-L +option to +.IR otool (1). +to discover the frameworks and shared libraries that the executable +is linked against. +.TP +.B DYLD_FALLBACK_FRAMEWORK_PATH +This is a colon separated list of directories that contain frameworks. +It is used as the default location for frameworks not found in their install +path. + +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 +locations for libraries. It allows you to test new versions of existing +libraries. +.IP +For each library that a program uses, the dynamic linker looks for it in each +directory in +.SM DYLD_LIBRARY_PATH +in turn. If it still can't find the library, it then searches +.SM DYLD_FALLBACK_FRAMEWORK_PATH +and +.SM DYLD_FALLBACK_LIBRARY_PATH +in turn. +.IP +Use the +.B \-L +option to +.IR otool (1). +to discover the frameworks and shared libraries that the executable +is linked against. +.TP +.B DYLD_FALLBACK_LIBRARY_PATH +This is a colon separated list of directories that contain libraries. +It is used as the default location for libraries not found in their install +path. +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_PRINT_TO_FILE +This is a path to a (writable) file. Normally, the dynamic linker writes all +logging output (triggered by DYLD_PRINT_* settings) to file descriptor 2 +(which is usually stderr). But this setting causes the dynamic linker to +write logging output to the specified file. +.TP +.B DYLD_SHARED_REGION +This can be "use" (the default), "avoid", or "private". Setting it to +"avoid" tells dyld to not use the shared cache. All OS dylibs are loaded +dynamically just like every other dylib. Setting it to "private" tells +dyld to remove the shared region from the process address space and mmap() +back in a private copy of the dyld shared cache in the shared region address +range. This is only useful if the shared cache on disk has been updated +and is different than the shared cache in use. +.TP +.B DYLD_INSERT_LIBRARIES +This is a colon separated list of dynamic libraries to load before the ones +specified in the program. This lets you test new modules of existing dynamic +shared libraries that are used in flat-namespace images by loading a temporary +dynamic shared library with just the new modules. Note that this has no +effect on images built a two-level namespace images using a dynamic shared +library unless +.SM DYLD_FORCE_FLAT_NAMESPACE +is also used. +.TP +.B DYLD_FORCE_FLAT_NAMESPACE +Force all images in the program to be linked as flat-namespace images and ignore +any two-level namespace bindings. This may cause programs to fail to execute +with a multiply defined symbol error if two-level namespace images are used to +allow the images to have multiply defined symbols. +.TP +.B DYLD_IMAGE_SUFFIX +This is set to a string of a suffix to try to be used for all shared libraries +used by the program. For libraries ending in ".dylib" the suffix is applied +just before the ".dylib". For all other libraries the suffix is appended to the +library name. This is useful for using conventional "_profile" and "_debug" +libraries and frameworks. +.TP +.B DYLD_PRINT_OPTS +When this is set, the dynamic linker writes to file descriptor 2 (normally +standard error) the command line options. +.TP +.B DYLD_PRINT_ENV +When this is set, the dynamic linker writes to file descriptor 2 (normally +standard error) the environment variables. +.TP +.B DYLD_PRINT_LIBRARIES +When this is set, the dynamic linker writes to file descriptor 2 (normally +standard error) the filenames of the libraries the program is using. +This is useful to make sure that the use of +.SM DYLD_LIBRARY_PATH +is getting what you want. +.TP +.B DYLD_BIND_AT_LAUNCH +When this is set, the dynamic linker binds all undefined symbols +the program needs at launch time. This includes function symbols that can are normally +lazily bound at the time of their first call. +.TP +.B DYLD_PRINT_STATISTICS +Right before the process's main() is called, dyld prints out information about how +dyld spent its time. Useful for analyzing launch performance. +.TP +.B DYLD_PRINT_STATISTICS_DETAILS +Right before the process's main() is called, dyld prints out detailed information about how +dyld spent its time. Useful for analyzing launch performance. +.TP +.B DYLD_DISABLE_DOFS +Causes dyld not register dtrace static probes with the kernel. +.TP +.B DYLD_PRINT_INITIALIZERS +Causes dyld to print out a line when running each initializers in every image. Initializers +run by dyld included constructors for C++ statically allocated objects, functions marked with +__attribute__((constructor)), and -init functions. +.TP +.B DYLD_PRINT_APIS +Causes dyld to print a line whenever a dyld API is called (e.g. NSAddImage()). +.TP +.B DYLD_PRINT_SEGMENTS +Causes dyld to print out a line containing the name and address range of each mach-o segment +that dyld maps. In addition it prints information about if the image was from the dyld +shared cache. +.TP +.B DYLD_PRINT_BINDINGS +Causes dyld to print a line each time a symbolic name is bound. +.TP +.B DYLD_PRINT_DOFS +Causes dyld to print out information about dtrace static probes registered with the kernel. +.TP +.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 +conjunction with DYLD_SHARED_REGION=private and DYLD_SHARED_CACHE_DONT_VALIDATE +to run a process with an alternate shared cache. +.TP +.B DYLD_SHARED_CACHE_DONT_VALIDATE +Causes dyld to not check that the inode and mod-time of files in the shared cache match +the requested dylib on disk. Thus a program can be made to run with the dylib in the +shared cache even though the real dylib has been updated on disk. +.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). +But there are times when a full path is not appropriate; for instance, may want your +binaries to be installable in anywhere on the disk. +To support that, there are three @xxx/ variables that can be used as a path prefix. At runtime dyld +substitutes a dynamically generated path for the @xxx/ prefix. +.TP +.B @executable_path/ +This variable is replaced with the path to the directory containing the main executable for +the process. This is useful for 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 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 +it is substituted with each path in the run path list until a loadable dylib if found. +The run path stack is built from the LC_RPATH load commands in the depencency chain +that lead to the current dylib load. +You can add an LC_RPATH load command to an image with the -rpath option to ld(1). You can +even add a LC_RPATH load command path that starts with @loader_path/, and it will push a path +on the run path stack that relative to the image containing the LC_RPATH. +The use of @rpath is most useful when you have a complex directory structure of programs and +dylibs which can be installed anywhere, but keep their relative positions. This scenario +could be implemented using @loader_path, but every client of a dylib could need a different +load path because its relative position in the file system is different. The use of @rpath +introduces a level of indirection that simplies things. You pick a location in your directory +structure as an anchor point. Each dylib then gets an install path that starts with @rpath +and is the path to the dylib relative to the anchor point. Each main executable is linked +with -rpath @loader_path/zzz, where zzz is the path from the executable to the anchor point. +At runtime dyld sets it run path to be the anchor point, then each dylib is found relative +to the anchor point. +.SH "SEE ALSO" +dyldinfo(1), ld(1), otool(1) diff --git a/dyld/doc/man/man1/update_dyld_shared_cache.1 b/dyld/doc/man/man1/update_dyld_shared_cache.1 new file mode 100644 index 0000000..f1c2330 --- /dev/null +++ b/dyld/doc/man/man1/update_dyld_shared_cache.1 @@ -0,0 +1,75 @@ +.Dd June 1, 2017 +.Dt update_dyld_shared_cache 1 +.Os Darwin +.Sh NAME +.Nm update_dyld_shared_cache +.Nd "Updates dyld's shared cache" +.Sh SYNOPSIS +.Nm +.Op Fl root Ar directory +.Op Fl overlay Ar directory +.Op Fl arch Ar arch +.Op Fl force +.Op Fl debug +.Op Fl universal_boot +.Sh DESCRIPTION +.Nm update_dyld_shared_cache +ensures that dyld's shared cache is up-to-date. This tool is normally +only run by Apple's Installer and Software Update, as they are the only +official ways for OS dylibs to be updated. But if for some reason you +used another mechanism to alter an OS dylib, you should manually run +.Nm update_dyld_shared_cache . +.Pp +Note that the new cache does not take effect until the OS is rebooted. +.Pp +The dyld shared cache +is mapped by dyld into a process at launch time. Later, when loading +any mach-o image, dyld will first check if is in the share cache, and if +it is will use that pre-bound version instead of opening, mapping, and binding +the original file. This results in significant performance improvements to +launch time. +.Pp +.Nm update_dyld_shared_cache +scans the directory /System/Library/Receipts/ for .bom files which list all files +installed. From that info it creates the set of OS dylibs to build into the dyld cache. +.Pp +.Nm update_dyld_shared_cache +builds a separate cache file for each architecture. The cache files and a readable text +map of the cached are generated to /var/db/dyld. +.Pp +You must be root to run this tool. +.Pp +The options are as follows: +.Bl -tag +.It Fl root Ar directory +This option specifies the root of an OS installation for which dyld's +shared cache should be updated. This is used by the Installer to update the +dyld shared cache in a partition other than the one you into which you are currently +booted. The cache files are created in the var/db/dyld directory of the specified directory. +Note: if you are manually doing this, be sure to run the update_dyld_shared_cache tool +that is in the partition being updated. This assures the cache format created will +match that expected when booting off that partition. +.It Fl overlay Ar directory +This option specifies the root of a sparse directory tree. When building +the dyld shared cache, any corresponding mach-o files in the sparse directory +will override those in the boot partition. This is used by Software +Update to build a dyld shared cache for the update that is about to be +installed. The cache files +are created in the var/db/dyld directory of the specified directory. +.It Fl arch Ar arch +By default +.Nm update_dyld_shared_cache +generates cache files for all architecture that the current machine +can execute. You can override this behavior by specifying one or more -arch options and list +exactly which architectures should have their shared caches updated. +.It Fl force +This option will cause +.Nm update_dyld_shared_cache +to regenerated the shared cache files even if they appear to be already up-to-date. +.It Fl debug +This option prints out additional information about the work being done. +.It Fl universal_boot +This option builds caches for all machines. +.El +.Sh SEE ALSO +.Xr dyld 1 diff --git a/dyld/doc/man/man3/dladdr.3 b/dyld/doc/man/man3/dladdr.3 new file mode 100644 index 0000000..bca4462 --- /dev/null +++ b/dyld/doc/man/man3/dladdr.3 @@ -0,0 +1,73 @@ +.Dd September 24, 2004 +.Os +.Dt DLADDR 3 +.Sh NAME +.Nm dladdr +.Nd find the image containing a given address +.Sh SYNOPSIS +.In dlfcn.h +.Ft int +.Fn dladdr "const void* addr" "Dl_info* info" +.Sh DESCRIPTION +The +.Fn dladdr +function +queries dyld (the dynamic linker) for information about the image +containing the address +.Fa addr . +The information is returned in the structure specified by +.Fa info . +The structure contains at least the following members: +.Bl -tag -width "XXXconst char *dli_fname" +.It Li "const char* dli_fname" +The pathname of the shared object containing the address. +.It Li "void* dli_fbase" +The base address (mach_header) at which the image is mapped into the +address space of the calling process. +.It Li "const char* dli_sname" +The name of the nearest run-time symbol with a value less than or +equal to +.Fa addr . +.It Li "void* dli_saddr" +The value of the symbol returned in +.Li dli_sname . +.El +.Pp +The +.Fn dladdr +function +is available only in dynamically linked programs. +.Sh ERRORS +If an image containing +.Fa addr +cannot be found, +.Fn dladdr +returns 0. +On success, a non-zero value is returned. +.Pp +If the image containing +.Fa addr +is found, but no nearest symbol was found, +the dli_sname and dli_saddr fields are set to NULL. +.Sh SEE ALSO +.Xr dyld 3 , +.Xr dlopen 3 +.Sh HISTORY +The +.Fn dladdr +function first appeared in the Solaris operating system. +.Sh AUTHORS +Mac OS X 10.3 incorporated the dlcompat package written by Jorge Acereda +and Peter O'Gorman . +.Pp +In Mac OS X 10.4, dlopen was rewritten to be a native part of dyld. +.Pp +This man page was borrowed from FreeBSD and modified. +.Sh BUGS +This implementation is almost bug-compatible with the Solaris +implementation. The following bugs are present: +.Bl -bullet +.It +Returning 0 as an indication of failure goes against long-standing +Unix tradition. +.El diff --git a/dyld/doc/man/man3/dlclose.3 b/dyld/doc/man/man3/dlclose.3 new file mode 100644 index 0000000..1200e80 --- /dev/null +++ b/dyld/doc/man/man3/dlclose.3 @@ -0,0 +1,43 @@ +.Dd Nov 6, 2006 +.Dt DLCLOSE 3 +.Sh NAME +.Nm dlclose +.Nd close a dynamic library or bundle +.Sh SYNOPSIS +.In dlfcn.h +.Ft int +.Fn dlclose "void* handle" +.Sh DESCRIPTION +.Fn dlclose +releases a reference to the dynamic library or bundle referenced by +.Fa handle . +If the reference count drops to 0, the bundle is removed from the +address space, and +.Fa handle +is rendered invalid. +Just before removing a dynamic library or bundle in this way, any +termination routines in it are called. +.Fa handle +is the value returned by a previous call to dlopen. +.Pp +Prior to Mac OS X 10.5, only bundles could be unloaded. Starting in Mac OS X 10.5, +dynamic libraries may also be unloaded. There are a couple of cases in which a +dynamic library will never be unloaded: 1) the main executable links against it, +2) An API that does not supoort unloading (e.g. NSAddImage()) was used to load +it or some other dynnamic library that depends on it, 3) the dynamic library +is in dyld's shared cache. +.Sh RETURN VALUES +If +.Fn dlclose +is successful, it returns a value of 0. +Otherwise it returns -1, and sets an error string that can be +retrived with +.Fn dlerror . +.Pp +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlsym 3 +.Xr dlerror 3 +.Xr dyld 3 +.Xr ld 1 +.Xr cc 1 diff --git a/dyld/doc/man/man3/dlerror.3 b/dyld/doc/man/man3/dlerror.3 new file mode 100644 index 0000000..758cc18 --- /dev/null +++ b/dyld/doc/man/man3/dlerror.3 @@ -0,0 +1,34 @@ +.Dd April 17, 2006 +.Dt DLERROR 3 +.Sh NAME +.Nm dlerror +.Nd get diagnostic information +.Sh SYNOPSIS +.In dlfcn.h +.Ft const char* +.Fn dlerror "void" +.Sh DESCRIPTION +.Fn dlerror +returns a null-terminated character string describing the last error that +occurred on this thread during a call to +.Fn dlopen , +.Fn dlopen_preflight , +.Fn dlsym , +or +.Fn dlclose . +If no such error has occurred, +.Fn dlerror +returns a null pointer. +At each call to +.Fn dlerror , +the error indication is reset. Thus in the case of two calls +to +.Fn dlerror , +where the second call follows the first immediately, the second call +will always return a null pointer. +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlopen_preflight 3 +.Xr dlclose 3 +.Xr dlsym 3 +.Xr dyld 3 diff --git a/dyld/doc/man/man3/dlopen.3 b/dyld/doc/man/man3/dlopen.3 new file mode 100644 index 0000000..1da4927 --- /dev/null +++ b/dyld/doc/man/man3/dlopen.3 @@ -0,0 +1,187 @@ +.Dd Aug 7, 2012 +.Os +.Dt DLOPEN 3 +.Sh NAME +.Nm dlopen +.Nd load and link a dynamic library or bundle +.Sh SYNOPSIS +.In dlfcn.h +.Ft void* +.Fn dlopen "const char* path" "int mode" +.Sh DESCRIPTION +.Fn dlopen +examines the mach-o file specified by +.Fa path . +If the file is compatible with the current process and has not already been +loaded into the current process, it is loaded and linked. After being linked, +if it contains any initializer functions, they are called, before +.Fn dlopen +returns. +.Fn dlopen +can load dynamic libraries and bundles. It returns a handle that can +be used with +.Fn dlsym +and +.Fn dlclose . +A second call to +.Fn dlopen +with the same path will return the same handle, but the internal reference +count for the handle will be incremented. Therefore all +.Fn dlopen +calls should be balanced with a +.Fn dlclose +call. +.Pp +If a null pointer is passed in +.Fa path , +.Fn dlopen +returns a handle equivalent to RTLD_DEFAULT. +.Pp +.Fa mode +contains options to +.Fn dlopen . +It must contain one or more of the following values, possibly ORed together: +.Pp +.Bl -tag -width RTLD_LAZYX +.It Dv RTLD_LAZY +Each external function reference is bound the first time the function is called. +.It Dv RTLD_NOW +All external function references are bound immediately during the call to +.Fn dlopen . +.El +.Pp +.Dv RTLD_LAZY +is normally preferred, for reasons of efficiency. +However, +.Dv RTLD_NOW +is useful to ensure that any undefined symbols are discovered during the +call to +.Fn dlopen . +If neither +RTLD_LAZY nor RTLD_NOW is specified, the default is RTLD_LAZY. +.Pp +One of the following flags may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_LOCALX +.It Dv RTLD_GLOBAL +Symbols exported from this image (dynamic library or bundle) will be available to any +images build with -flat_namespace option to +.Xr ld 1 +or to calls to +.Fn dlsym +when using a special handle. +.It Dv RTLD_LOCAL +Symbols exported from this image (dynamic library or bundle) are generally hidden +and only availble to +.Fn dlsym +when directly using the handle returned by this call to +.Fn dlopen . +.Pp +.El +If neither +RTLD_GLOBAL nor RTLD_LOCAL is specified, the default is RTLD_GLOBAL. +.Pp +One of the following may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_NODELETEX +.It Dv RTLD_NOLOAD +The specified image is not loaded. However, a valid +.Fa handle +is returned if the image already exists in the process. This provides a way +to query if an image is already loaded. The +.Fa handle +returned is ref-counted, so you eventually need a corresponding call to +.Fn dlclose +.It Dv RTLD_NODELETE +The specified image is tagged so that will never be removed from the address space, +even after all clients have released it via +.Fn dlclose +.El +.Pp +Additionally, the following may be ORed into the +.Fa mode +argument: +.Bl -tag -width RTLD_FIRSTX +.It Dv RTLD_FIRST +The retuned +.Fa handle +is tagged so that any +.Fn dlsym +calls on the +.Fa handle +will only search the image specified, and not subsequent images. If +.Fa path +is NULL and the option RTLD_FIRST is used, the +.Fa handle +returned will only search the main executable. +.El +.Sh SEARCHING +.Fn dlopen +searches for a compatible Mach-O file in the directories specified by a set of environment variables and +the process's current working directory. +When set, the environment variables contain a colon-separated list of directory paths, +which can be absolute or relative to the current working directory. +.Pp +When +.Fa path +does not contain a slash character (i.e. it is just a leaf name), +.Fn dlopen +searches the following until it finds a compatible Mach-O file: $LD_LIBRARY_PATH, +$DYLD_LIBRARY_PATH, current working directory, $DYLD_FALLBACK_LIBRARY_PATH. +.Pp +When +.Fa path +looks like a framework path (e.g. /stuff/foo.framework/foo), +.Fn dlopen +searches the following until it finds a compatible Mach-O file: +$DYLD_FRAMEWORK_PATH (with framework partial path from +.Fa path +), then the supplied +.Fa path +(using current working directory for relative paths), then +$DYLD_FALLBACK_FRAMEWORK_PATH (with framework partial path from +.Fa path +). +.Pp +When +.Fa path +contains a slash but is not a framework path (i.e. a full path or a partial path to a dylib), +.Fn dlopen +searches the following until it finds a compatible Mach-O file: +$DYLD_LIBRARY_PATH (with leaf name from +.Fa path +), then the supplied +.Fa path +(using current working directory for relative paths), then +$DYLD_FALLBACK_LIBRARY_PATH (with leaf name from +.Fa path +). +.Pp +Note: If DYLD_FALLBACK_LIBRARY_PATH is not set, dlopen operates as if +DYLD_FALLBACK_LIBRARY_PATH was set to $HOME/lib:/usr/local/lib:/usr/lib. +.Pp +Note: If DYLD_FALLBACK_FRAMEWORK_PATH is not set, dlopen operates as if +DYLD_FALLBACK_FRAMEWORK_PATH was set to $HOME/Library/Frameworks:/Library/Frameworks:/Network/Library/Frameworks:/System/Library/Frameworks. +.Pp +Note: There are no configuration files to control dlopen searching. +.Pp +Note: If the main executable is a set[ug]id binary or codesigned with entitlements, +then all environment variables are ignored, and only a full path can be used. +.Pp +Note: Mac OS X uses "universal" files to combine 32-bit and 64-bit libraries. This means there are no separate 32-bit and 64-bit search paths. +.Pp +.Sh RETURN VALUES +If +.Fn dlopen +fails, it returns a null pointer, and sets an error condition which may be interrogated with +.Fn dlerror . +.Pp +.Sh SEE ALSO +.Xr dlopen_preflight 3 +.Xr dlclose 3 +.Xr dlsym 3 +.Xr dlerror 3 +.Xr dyld 1 +.Xr ld 1 diff --git a/dyld/doc/man/man3/dlopen_preflight.3 b/dyld/doc/man/man3/dlopen_preflight.3 new file mode 100644 index 0000000..aa27dbf --- /dev/null +++ b/dyld/doc/man/man3/dlopen_preflight.3 @@ -0,0 +1,33 @@ +.Dd April 17, 2006 +.Os +.Dt DLOPEN_PREFLIGHT 3 +.Sh NAME +.Nm dlopen_preflight +.Nd preflight the load of a dynamic library or bundle +.Sh SYNOPSIS +.In dlfcn.h +.Ft bool +.Fn dlopen_preflight "const char* path" +.Sh DESCRIPTION +.Fn dlopen_preflight +examines the mach-o file specified by +.Fa path . +It checks if the file and libraries it depends on are all compatible with the current process. +That is, they contain the correct architecture and are not otherwise ABI incompatible. +.Pp +.Fn dlopen_preflight +was first available in Mac OS X 10.5. +.Sh SEARCHING +.Fn dlopen_preflight +uses the same steps as +.Fn dlopen +to find a compatible mach-o file. +.Sh RETURN VALUES +.Fn dlopen_preflight +returns true on if the mach-o file is compatible. If the file is not compatible, it returns false +and sets an error string that can be examined with +.Fn dlerror . +.Pp +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlerror 3 diff --git a/dyld/doc/man/man3/dlsym.3 b/dyld/doc/man/man3/dlsym.3 new file mode 100644 index 0000000..5f7c4ed --- /dev/null +++ b/dyld/doc/man/man3/dlsym.3 @@ -0,0 +1,94 @@ +.Dd August 28, 2008 +.Dt DLSYM 3 +.Sh NAME +.Nm dlsym +.Nd get address of a symbol +.Sh SYNOPSIS +.In dlfcn.h +.Ft void* +.Fn dlsym "void* handle" "const char* symbol" +.Sh DESCRIPTION +.Fn dlsym +returns the address of the code or data location +specified by the null-terminated character string +.Fa symbol . +Which libraries and bundles are searched depends on the +.Fa handle +parameter. +.Pp +If +.Fn dlsym +is called with a +.Fa handle , +returned by +.Fn dlopen +then only that image and any libraries it depends on are searched for +.Fa symbol . +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_DEFAULT , +then all mach-o images in the process (except those loaded with dlopen(xxx, RTLD_LOCAL)) +are searched in the order they were loaded. +This can be a costly search and should be avoided. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_NEXT , +then dyld searches for the symbol in the dylibs the calling image +linked against when built. It is usually used when +you intentionally have multiply defined symbol across images +and want to find the "next" definition. It searches other images +for the definition that the caller would be using if it did not +have a definition. The exact search algorithm depends on whether +the caller's image was linked -flat_namespace or -twolevel_namespace. +For flat linked images, the search starts in the load ordered list +of all images, in the image right after the caller's image. +For two-level images, the search simulates how the static linker +would have searched for the symbol when linking the caller's +image. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_SELF , +then the search for the symbol starts with the image that called +.Fn dlsym . +If it is not found, the search continues as if RTLD_NEXT was used. +.Pp +If +.Fn dlsym +is called with the special +.Fa handle +.Dv RTLD_MAIN_ONLY , +then it only searches for +.Fa symbol +in the main executable. +.Pp +.Sh RETURN VALUES +The +.Fn dlsym +function +returns a null pointer if the symbol cannot be found, and sets an error +condition which may be queried with +.Fn dlerror . +.Pp +.Sh NOTES +The symbol name passed to +.Fn dlsym +is the name used in C source code. For example to find the address +of function foo(), you would pass "foo" as the symbol name. This +is unlike the older dyld APIs which required a leading underscore. +If you looking up a C++ symbol, you need to use the mangled C++ symbol +name. +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dlerror 3 +.Xr dyld 3 +.Xr ld 1 +.Xr cc 1 diff --git a/dyld/doc/man/man3/dyld.3 b/dyld/doc/man/man3/dyld.3 new file mode 100644 index 0000000..cb9f6e4 --- /dev/null +++ b/dyld/doc/man/man3/dyld.3 @@ -0,0 +1,126 @@ +.Dd November 29, 2010 +.Dt dyld 3 +.Sh NAME +.Nm _dyld_image_count, +.Nm _dyld_get_image_header, +.Nm _dyld_get_image_vmaddr_slide, +.Nm _dyld_get_image_name, +.Nm _dyld_register_func_for_add_image, +.Nm _dyld_register_func_for_remove_image, +.Nm NSVersionOfRunTimeLibrary, +.Nm NSVersionOfLinkTimeLibrary +.Nm _NSGetExecutablePath +.Sh SYNOPSIS +.In mach-o/dyld.h +.Ft uint32_t +.Fo _dyld_image_count +.Fa "void" +.Fc +.Ft const struct mach_header* +.Fo _dyld_get_image_header +.Fa "uint32_t image_index" +.Fc +.Ft intptr_t +.Fo _dyld_get_image_vmaddr_slide +.Fa "uint32_t image_index" +.Fc +.Ft const char* +.Fo _dyld_get_image_name +.Fa "uint32_t image_index" +.Fc +.Ft void +.Fo _dyld_register_func_for_add_image +.Fa "void \*[lp]*func\*[rp]\*[lp]const struct mach_header* mh, intptr_t vmaddr_slide\*[rp]" +.Fc +.Ft void +.Fo _dyld_register_func_for_remove_image +.Fa "void \*[lp]*func\*[rp]\*[lp]const struct mach_header* mh, intptr_t vmaddr_slide\*[rp]" +.Fc +.Ft int32_t +.Fo NSVersionOfRunTimeLibrary +.Fa "const char* libraryName" +.Fc +.Ft int32_t +.Fo NSVersionOfLinkTimeLibrary +.Fa "const char* libraryName" +.Fc +.Ft int +.Fo _NSGetExecutablePath +.Fa "char* buf" +.Fa "uint32_t* bufsize" +.Fc +.Sh DESCRIPTION +These routines provide additional introspection of dyld beyond that provided by +.Fn dlopen +and +.Fn dladdr +. +.Pp +.Fn _dyld_image_count +returns the current number of images mapped in by dyld. Note that using this +count to iterate all images is not thread safe, because another thread +may be adding or removing images during the iteration. +.Pp +.Fn _dyld_get_image_header +returns a pointer to the mach header of the image indexed by image_index. If +.Fa image_index +is out of range, NULL is returned. +.Pp +.Fn _dyld_get_image_vmaddr_slide +returns the virtural memory address slide amount of the image indexed by +.Fa image_index. +If +.Fa image_index +is out of range zero is returned. +.Pp +.Fn _dyld_get_image_name +returns the name of the image indexed by +.Fa image_index. +The C-string continues to be owned by dyld and should not deleted. +If +.Fa image_index +is out of range NULL is returned. +.Pp +.Fn _dyld_register_func_for_add_image +registers the specified function to be called when a new image is added +(a bundle or a dynamic shared library) to the program. When this function is +first registered it is called for once for each image that is currently part of +the process. +.Pp +.Fn _dyld_register_func_for_remove_image +registers the specified function to be called when an image is removed +(a bundle or a dynamic shared library) from the process. +.Pp +.Fn NSVersionOfRunTimeLibrary +returns the current_version number of the currently loaded dylib +specifed by the libraryName. The libraryName parameter would be "bar" for /path/libbar.3.dylib and +"Foo" for /path/Foo.framework/Versions/A/Foo. This function returns -1 if no such library is loaded. +.Pp +.Fn NSVersionOfLinkTimeLibrary +returns the current_version number that the main executable was linked +against at build time. The libraryName parameter would be "bar" for /path/libbar.3.dylib and +"Foo" for /path/Foo.framework/Versions/A/Foo. This function returns -1 if the main executable did not link +against the specified library. +.Pp +.Fn _NSGetExecutablePath +copies the path of the main executable into the buffer +.Fa buf . +The +.Fa bufsize +parameter should initially be the size of the buffer. This function returns 0 if the path was successfully copied, +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. +Note that +.Fn _NSGetExecutablePath +will return "a path" to the executable not a "real path" to the executable. +That is, the path may be a symbolic link and not the real file. With deep directories the total bufsize +needed could be more than MAXPATHLEN. +.Sh SEE ALSO +.Xr dlopen 3 +.Xr dladdr 3 +.Xr dyld 1 +http://developer.apple.com/documentation/DeveloperTools/Conceptual/MachOTopics/index.html \ No newline at end of file diff --git a/dyld/dyld.xcodeproj/project.pbxproj b/dyld/dyld.xcodeproj/project.pbxproj new file mode 100644 index 0000000..121b7fa --- /dev/null +++ b/dyld/dyld.xcodeproj/project.pbxproj @@ -0,0 +1,3549 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */; + buildPhases = ( + F94182D61E60E74E00D8EF25 /* pre-platform builds */, + ); + dependencies = ( + D8668AD01ECE335F005E7D31 /* PBXTargetDependency */, + F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */, + F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */, + F94182DC1E60F16900D8EF25 /* PBXTargetDependency */, + ); + name = update_dyld_shared_cache; + productName = update_dyld_shared_cache; + }; + F908134211D3ED0B00626CC1 /* libdyld */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */; + buildPhases = ( + F9C69EFC14EC8AB8009CAE2E /* usr|local|include */, + 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" */; + buildPhases = ( + ); + dependencies = ( + F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */, + F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */, + 37A0AD0F1C16000F00731E50 /* PBXTargetDependency */, + ); + name = all; + productName = all; + }; + F9F6F4271C1FB0A700BD8FED /* dyld_tests */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */; + buildPhases = ( + F9F6F42B1C1FB0AE00BD8FED /* build */, + ); + dependencies = ( + F97FF3661C237F97000ACDD2 /* PBXTargetDependency */, + ); + name = dyld_tests; + productName = dyld_tests; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; }; + 37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + 37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + 37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */; }; + 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + 37554F401E3F167A00407388 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; + 37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; + 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; + 37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; + 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; + 37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; + 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; + 37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; + 37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + 37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; + 37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; + 37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; }; + 37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; }; + 37554F531E3F7B1E00407388 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + 37554F541E3F7B1F00407388 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + 37554F551E3F7B4200407388 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; }; + 37554F561E3F7B4300407388 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; }; + 37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + 37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; }; + 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 376ED1D71C46F2710051DD54 /* Metabom.framework */; }; + 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; + 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; + 37908A2E1E3A8632009613FA /* Manifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A281E3A853E009613FA /* Manifest.mm */; }; + 37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */; }; + 37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + 37908A311E3EB585009613FA /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + 37908A321E3ED667009613FA /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + 37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; }; + 37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */; }; + 37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; + 37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; }; + 37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */; }; + F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + 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 */; }; + F90F7C081E9C6B8B00535722 /* libCrashReporterClient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */; }; + F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */; }; + F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 37F7A5961BB363820039043A /* Bom.framework */; }; + F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + F922AE581EF0D3C300926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F922AE591EF0DBA500926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F922AE5A1EF0DC7200926F9D /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */; }; + F926C0471DDBFB7A00941CB1 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; }; + F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */; }; + F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */; }; + F9460DCD1E09FFFC00FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + F94942B31E6796D70019AE08 /* closured.1 in install man page */ = {isa = PBXBuildFile; fileRef = F94942B21E6796D40019AE08 /* closured.1 */; }; + F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F94C22241E513CA90079E5DD /* CoreFoundation.framework */; }; + 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"; }; }; + F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */; }; + F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */; }; + F960A78A1E40569400840176 /* dyld-interposing.h in Headers */ = {isa = PBXBuildFile; fileRef = F918691408B16D2500E0F9DB /* dyld-interposing.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F960A78B1E405DE300840176 /* dyld_cache_format.h in Headers */ = {isa = PBXBuildFile; fileRef = F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; + F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; + F96354361DCD74A400895049 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F96354381DCD74A400895049 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; + F963543A1DCD74A400895049 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; }; + F963543B1DCD74A400895049 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; + F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */; }; + F963546C1DD8F38300895049 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; }; + F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A51D9363D6007AF3CE /* APIs.cpp */; }; + F96D19BF1D94A6DC007AF3CE /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19A61D9363D6007AF3CE /* AllImages.cpp */; }; + F977DDCB1E53BF5500609230 /* SharedCacheRuntime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */; }; + F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */; }; + F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F97C61A01D9CA6B800A84CD7 /* Logging.cpp */; }; + F97C61B31DBAE14200A84CD7 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + F97FF3611C23640C000ACDD2 /* nocr.c in Sources */ = {isa = PBXBuildFile; fileRef = F97FF35F1C236402000ACDD2 /* nocr.c */; }; + F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */ = {isa = PBXBuildFile; fileRef = F97FF3631C237F5C000ACDD2 /* nocr.1 */; }; + F981C8BD1EEF447500452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F981C8BE1EEF733800452F35 /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; }; + F981C8BF1EEF733C00452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F981C8C01EEF7D4100452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F981C8C11EF06A7800452F35 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F98692171DC3EFD500CBEDE6 /* update_dyld_shared_cache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */; }; + F98692181DC3EFD700CBEDE6 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */; }; + F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F98692211DC401B900CBEDE6 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */; }; + F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */ = {isa = PBXBuildFile; fileRef = F988F0BA1E2FDF5B003AED79 /* execserver.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */ = {isa = PBXBuildFile; fileRef = F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */; }; + F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CE90630A80600DF4E74 /* dyld_priv.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */ = {isa = PBXBuildFile; fileRef = F95090D01C5AB89A0031F81D /* dyld_process_info.h */; settings = {ATTRIBUTES = (Private, ); }; }; + F99006DD1E411BA70013456D /* dyld_images.h in Headers */ = {isa = PBXBuildFile; fileRef = F98D274C0AA79D7400416316 /* dyld_images.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F99006DE1E411BBC0013456D /* dyld.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CEA0630A80600DF4E74 /* dyld.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F99006E01E4130AE0013456D /* dyld_gdb.h in Headers */ = {isa = PBXBuildFile; fileRef = F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 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 */; }; + F9A548B31DDBBC75002B4422 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; }; + F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */; }; + F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */ = {isa = PBXBuildFile; fileRef = F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */; }; + F9AB02C31F329FE000EE96C4 /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; }; + F9AB02C41F329FF400EE96C4 /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + F9AB02C51F329FFE00EE96C4 /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; }; + F9AB02C61F32A1F400EE96C4 /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F9AB02C71F32A22B00EE96C4 /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + F9AB02C81F32A23B00EE96C4 /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + F9AB02C91F32A24B00EE96C4 /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; }; + F9AB02CA1F32A25F00EE96C4 /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F9AB02CB1F32A26700EE96C4 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + F9AB02CC1F32A33C00EE96C4 /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + F9ABA06E1E289B72000F21B4 /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; }; + F9B3CAEA1EE20FE200C9A48B /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; }; + F9B3CAEC1EEB5CFB00C9A48B /* DyldCacheParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */; }; + F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */ = {isa = PBXBuildFile; fileRef = F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */; }; + F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */; }; + F9C275571DA5D67F007A5D8A /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; }; + F9C2755B1DA73EA1007A5D8A /* Loading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9C275581DA71A13007A5D8A /* Loading.cpp */; }; + F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */ = {isa = PBXBuildFile; fileRef = F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */; }; + F9C73AC21E2992730025C89E /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; }; + F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; }; + 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 */; }; + F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9CE30781208F1B50098B590 /* dsc_extractor.cpp */; }; + F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */ = {isa = PBXBuildFile; fileRef = F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */; }; + F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */ = {isa = PBXBuildFile; fileRef = F9D49CCB1458B95200F86ADD /* start_glue.s */; }; + F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */; }; + F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */; }; + F9D862411DC65A4E000A199A /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; }; + F9D862421DC65A53000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + F9D862451DC975A5000A199A /* dyld_closure_util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D862441DC9759C000A199A /* dyld_closure_util.cpp */; }; + F9D862461DC975AA000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F9D862471DC975B1000A199A /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; }; + F9D862481DC975B3000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + F9D8624B1DC976E4000A199A /* LaunchCachePrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */; }; + F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + F9D8624D1DC9783E000A199A /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + F9D8624E1DCBD06A000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F9D8624F1DCBD318000A199A /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F9D862501DCBD31D000A199A /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + F9D862511DCBD330000A199A /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + F9DDEDB91E2878EC00A753DC /* closured.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAA1E28787900A753DC /* closured.cpp */; }; + F9DDEDBA1E2878F100A753DC /* closuredProtocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + F9DDEDBB1E287C9500A753DC /* DyldSharedCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */; }; + F9DDEDBC1E287CA100A753DC /* Diagnostics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */; }; + F9DDEDBD1E287CB100A753DC /* LaunchCacheWriter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */; }; + F9DDEDBE1E287CF600A753DC /* FileUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */; }; + F9DDEDBF1E287CF600A753DC /* ImageProxy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F963546B1DD8F2A800895049 /* ImageProxy.cpp */; }; + F9DDEDC01E287CF600A753DC /* LaunchCacheReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */; }; + F9DDEDC11E287D3C00A753DC /* MachOParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */; }; + F9DDEDC21E287D8A00A753DC /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; + F9E5E2C61EB00A9F0013A0BB /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; }; + F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */; }; + 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 */; }; + F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */; }; + F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */; }; + F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */; }; + F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */; }; + F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD00630A7F100DF4E74 /* glue.c */; }; + F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */; }; + F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */; }; + F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */; }; + F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */; }; + F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */; }; + F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + F921D3160703769A000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.cpp; + isEditable = 1; + outputFiles = ( + ); + script = ""; + }; + F921D317070376A6000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + script = ""; + }; + F921D318070376B0000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.asm; + isEditable = 1; + outputFiles = ( + ); + script = ""; + }; + F921D31E070376F1000D1056 /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.cpp; + isEditable = 1; + outputFiles = ( + ); + }; + F9574C4906C94DA700142BFA /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.gcc; + fileType = sourcecode.c; + isEditable = 1; + outputFiles = ( + ); + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 37A0AD0A1C15FFF500731E50; + remoteInfo = update_dyld_shared_cache; + }; + D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F97C61A61DBAD1A900A84CD7; + remoteInfo = dyld_closure_util; + }; + F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74; + remoteInfo = libdyld.dylib; + }; + F922C8111F33B62700D8F479 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9AB02B71F329FAA00EE96C4; + remoteInfo = libclosured; + }; + F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F922C8161F33B73800D8F479; + remoteInfo = "libclosured-stub"; + }; + F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F99B8E550FEC10F600701838; + remoteInfo = dyld_shared_cache_util; + }; + F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9F2A5580F7AEE9800B7C9EB; + remoteInfo = libdsc; + }; + F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9D1001114D8D0BA00099D91; + remoteInfo = dsc_extractor; + }; + F96543A01E343601003C5540 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F97C61A61DBAD1A900A84CD7; + remoteInfo = dyld_closure_util; + }; + F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F97FF3551C23638F000ACDD2; + remoteInfo = nocr; + }; + 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 = libdsc; + }; + F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9ED4C970630A76000DF4E74; + remoteInfo = dyld; + }; + F9ED4CA80630A78A00DF4E74 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F9ED4C8B0630A72300DF4E74 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9ED4C9E0630A76B00DF4E74; + remoteInfo = libdyld; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3703A1201B38C1B300ADBA7F /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 377685FE1AC4B27D00026E6C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; + F908137211D3FB5000626CC1 /* usr|share|man|man1 */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man1"; + dstSubfolderSpec = 0; + files = ( + F908136411D3FB0300626CC1 /* dyld.1 in usr|share|man|man1 */, + ); + name = "usr|share|man|man1"; + runOnlyForDeploymentPostprocessing = 1; + }; + F908137311D3FB5000626CC1 /* usr|share|man|man3 */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man3"; + dstSubfolderSpec = 0; + files = ( + 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; + }; + F94942B11E67965C0019AE08 /* install man page */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + F94942B31E6796D70019AE08 /* closured.1 in install man page */, + ); + name = "install man page"; + runOnlyForDeploymentPostprocessing = 1; + }; + F97C61A51DBAD1A900A84CD7 /* Copy Files */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + name = "Copy Files"; + runOnlyForDeploymentPostprocessing = 1; + }; + F97FF3541C23638F000ACDD2 /* install man page */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/share/man/man1"; + dstSubfolderSpec = 0; + files = ( + F97FF3641C237F68000ACDD2 /* nocr.1 in install man page */, + ); + name = "install man page"; + runOnlyForDeploymentPostprocessing = 1; + }; + F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/local/include/mach-o"; + dstSubfolderSpec = 0; + files = ( + F98C78F00F7C02E8006257D2 /* dsc_iterator.h in usr|local|include|mach-o */, + F9CE307B1208F1C60098B590 /* dsc_extractor.h in usr|local|include|mach-o */, + ); + name = "usr|local|include|mach-o"; + runOnlyForDeploymentPostprocessing = 1; + }; + F9C69EFC14EC8AB8009CAE2E /* usr|local|include */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)/usr/local/include"; + dstSubfolderSpec = 0; + files = ( + F9C69EFE14EC8AD2009CAE2E /* objc-shared-cache.h in usr|local|include */, + ); + name = "usr|local|include"; + runOnlyForDeploymentPostprocessing = 1; + }; + F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/$(LOCAL)/share/man/man1"; + dstSubfolderSpec = 0; + files = ( + F9D238DB0A9E2FD0002B55C7 /* update_dyld_shared_cache.1 in usr|share|man|man1 */, + ); + name = "usr|share|man|man1"; + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; }; + 376ED1D71C46F2710051DD54 /* Metabom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metabom.framework; path = AppleInternal/Library/Frameworks/Metabom.framework; sourceTree = SDKROOT; }; + 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = multi_dyld_shared_cache_builder; sourceTree = BUILT_PRODUCTS_DIR; }; + 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/dyld_shared_cache_builder.mm"; sourceTree = ""; }; + 37908A281E3A853E009613FA /* Manifest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Manifest.mm; path = "dyld3/shared-cache/Manifest.mm"; sourceTree = ""; }; + 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = multi_dyld_shared_cache_builder.mm; path = "dyld3/shared-cache/multi_dyld_shared_cache_builder.mm"; sourceTree = ""; }; + 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = FileAbstraction.hpp; path = "dyld3/shared-cache/FileAbstraction.hpp"; sourceTree = ""; }; + 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = MachOFileAbstraction.hpp; path = "dyld3/shared-cache/MachOFileAbstraction.hpp"; sourceTree = ""; }; + 37908A2C1E3A85A4009613FA /* Manifest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Manifest.h; path = "dyld3/shared-cache/Manifest.h"; sourceTree = ""; }; + 37908A2D1E3A85A4009613FA /* Trie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Trie.hpp; path = "dyld3/shared-cache/Trie.hpp"; sourceTree = ""; }; + 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BuilderUtils.mm; path = "dyld3/shared-cache/BuilderUtils.mm"; sourceTree = ""; }; + 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BuilderUtils.h; path = "dyld3/shared-cache/BuilderUtils.h"; sourceTree = ""; }; + 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Tracing.cpp; path = dyld3/Tracing.cpp; sourceTree = ""; }; + 37D7DAFF1E96F0ED00D52CEA /* Tracing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Tracing.h; path = dyld3/Tracing.h; sourceTree = ""; }; + 37F7A5961BB363820039043A /* Bom.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bom.framework; path = System/Library/PrivateFrameworks/Bom.framework; sourceTree = SDKROOT; }; + EF799FE9070D27BB00F78484 /* dyld.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; name = dyld.1; path = doc/man/man1/dyld.1; sourceTree = SOURCE_ROOT; }; + EF799FEB070D27BB00F78484 /* dladdr.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dladdr.3; path = doc/man/man3/dladdr.3; sourceTree = SOURCE_ROOT; }; + EF799FEC070D27BB00F78484 /* dlclose.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlclose.3; path = doc/man/man3/dlclose.3; sourceTree = SOURCE_ROOT; }; + EF799FED070D27BB00F78484 /* dlerror.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlerror.3; path = doc/man/man3/dlerror.3; sourceTree = SOURCE_ROOT; }; + EF799FEE070D27BB00F78484 /* dlopen.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlopen.3; path = doc/man/man3/dlopen.3; sourceTree = SOURCE_ROOT; }; + EF799FEF070D27BB00F78484 /* dlsym.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dlsym.3; path = doc/man/man3/dlsym.3; sourceTree = SOURCE_ROOT; }; + EF799FF0070D27BB00F78484 /* dyld.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 30; name = dyld.3; path = doc/man/man3/dyld.3; sourceTree = SOURCE_ROOT; }; + F902031F1DEE83C000AC3F76 /* StringUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringUtils.h; path = "dyld3/shared-cache/StringUtils.h"; sourceTree = ""; }; + F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libCrashReporterClient.a; path = usr/local/lib/libCrashReporterClient.a; sourceTree = SDKROOT; }; + F913C8501E9312A100458AA3 /* com.apple.dyld.closured.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = com.apple.dyld.closured.sb; path = dyld3/closured/com.apple.dyld.closured.sb; sourceTree = ""; }; + F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIsInLibSystem.cpp; path = src/dyldAPIsInLibSystem.cpp; sourceTree = ""; }; + F918691408B16D2500E0F9DB /* dyld-interposing.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = "dyld-interposing.h"; path = "include/mach-o/dyld-interposing.h"; sourceTree = ""; }; + F922C8171F33B73800D8F479 /* libclosured.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libclosured.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "libclosured-stub.cpp"; path = "dyld3/libclosured-stub.cpp"; sourceTree = ""; }; + F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMegaDylib.cpp; path = src/ImageLoaderMegaDylib.cpp; sourceTree = ""; }; + F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMegaDylib.h; path = src/ImageLoaderMegaDylib.h; sourceTree = ""; }; + F93937320A94FAF700070A07 /* update_dyld_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; }; + F939373E0A94FC4700070A07 /* Architectures.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = Architectures.hpp; sourceTree = ""; }; + F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = CacheFileAbstraction.hpp; sourceTree = ""; }; + F93937400A94FC4700070A07 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dyld_cache_format.h; sourceTree = ""; }; + F93937410A94FC4700070A07 /* FileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = FileAbstraction.hpp; sourceTree = ""; }; + F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = MachOFileAbstraction.hpp; sourceTree = ""; }; + F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_sim_shared_cache.xcconfig; sourceTree = ""; }; + F94942B21E6796D40019AE08 /* closured.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = closured.1; sourceTree = ""; }; + F94C22241E513CA90079E5DD /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOClassic.cpp; path = src/ImageLoaderMachOClassic.cpp; sourceTree = ""; }; + F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOClassic.h; path = src/ImageLoaderMachOClassic.h; sourceTree = ""; }; + F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachOCompressed.cpp; path = src/ImageLoaderMachOCompressed.cpp; sourceTree = ""; }; + F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachOCompressed.h; path = src/ImageLoaderMachOCompressed.h; sourceTree = ""; }; + F95090D01C5AB89A0031F81D /* dyld_process_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_process_info.h; path = "include/mach-o/dyld_process_info.h"; sourceTree = ""; usesTabs = 0; }; + F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info.cpp; path = src/dyld_process_info.cpp; sourceTree = ""; usesTabs = 0; }; + F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_process_info_internal.h; path = src/dyld_process_info_internal.h; sourceTree = ""; }; + F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_process_info_notify.cpp; path = src/dyld_process_info_notify.cpp; sourceTree = ""; usesTabs = 0; }; + F95C95160E994796007B7CB8 /* MachOTrie.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MachOTrie.hpp; sourceTree = ""; }; + F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_sim_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_sim_shared_cache.cpp"; sourceTree = ""; usesTabs = 0; }; + F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = update_dyld_sim_shared_cache; sourceTree = BUILT_PRODUCTS_DIR; }; + F963546A1DD8D8D300895049 /* ImageProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ImageProxy.h; path = "dyld3/shared-cache/ImageProxy.h"; sourceTree = ""; usesTabs = 0; }; + F963546B1DD8F2A800895049 /* ImageProxy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ImageProxy.cpp; path = "dyld3/shared-cache/ImageProxy.cpp"; sourceTree = ""; usesTabs = 0; }; + F96D19711D7F63EE007AF3CE /* expand.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; name = expand.pl; path = bin/expand.pl; sourceTree = ""; }; + F96D19A51D9363D6007AF3CE /* APIs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs.cpp; path = dyld3/APIs.cpp; sourceTree = ""; usesTabs = 0; }; + F96D19A61D9363D6007AF3CE /* AllImages.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AllImages.cpp; path = dyld3/AllImages.cpp; sourceTree = ""; usesTabs = 0; }; + F96D19A71D9363D6007AF3CE /* AllImages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AllImages.h; path = dyld3/AllImages.h; sourceTree = ""; usesTabs = 0; }; + F96D19A91D94576E007AF3CE /* MachOParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachOParser.h; path = dyld3/MachOParser.h; sourceTree = ""; usesTabs = 0; }; + F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MachOParser.cpp; path = dyld3/MachOParser.cpp; sourceTree = ""; usesTabs = 0; }; + F96D19C11D95C6D6007AF3CE /* APIs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = APIs.h; path = dyld3/APIs.h; sourceTree = ""; usesTabs = 0; }; + F971DD131A4A0E0700BBDD52 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; + F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = dyld.xcconfig; sourceTree = ""; }; + F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = libdyld.xcconfig; sourceTree = ""; }; + F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = update_dyld_shared_cache.xcconfig; sourceTree = ""; }; + F976F548127B90F8004BA2A5 /* dyld.order */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dyld.order; path = src/dyld.order; sourceTree = ""; }; + F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SharedCacheRuntime.cpp; path = dyld3/SharedCacheRuntime.cpp; sourceTree = ""; }; + F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SharedCacheRuntime.h; path = dyld3/SharedCacheRuntime.h; sourceTree = ""; }; + F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = libdyldEntryVector.h; path = dyld3/libdyldEntryVector.h; sourceTree = ""; usesTabs = 0; }; + F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = libdyldEntryVector.cpp; path = dyld3/libdyldEntryVector.cpp; sourceTree = ""; usesTabs = 0; }; + F97C61A01D9CA6B800A84CD7 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = dyld3/Logging.cpp; sourceTree = ""; usesTabs = 0; }; + F97C61A11D9CA6B800A84CD7 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = dyld3/Logging.h; sourceTree = ""; usesTabs = 0; }; + F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_closure_util; sourceTree = BUILT_PRODUCTS_DIR; }; + F97FF3561C23638F000ACDD2 /* nocr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nocr; sourceTree = BUILT_PRODUCTS_DIR; }; + F97FF3581C23638F000ACDD2 /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; + F97FF35F1C236402000ACDD2 /* nocr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nocr.c; path = testing/nocr/nocr.c; sourceTree = ""; }; + F97FF3631C237F5C000ACDD2 /* nocr.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = nocr.1; path = ../../../testing/nocr/nocr.1; sourceTree = ""; }; + F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyldSyscallInterface.h; path = src/dyldSyscallInterface.h; sourceTree = ""; }; + F98692001DC3EF4800CBEDE6 /* Diagnostics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Diagnostics.h; path = dyld3/Diagnostics.h; sourceTree = ""; usesTabs = 0; }; + F98692021DC3EF4800CBEDE6 /* LaunchCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCache.h; path = dyld3/LaunchCache.h; sourceTree = ""; usesTabs = 0; }; + F98692041DC3EF4800CBEDE6 /* LaunchCacheFormat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCacheFormat.h; path = dyld3/LaunchCacheFormat.h; sourceTree = ""; usesTabs = 0; }; + F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCachePrinter.cpp; path = dyld3/LaunchCachePrinter.cpp; sourceTree = ""; usesTabs = 0; }; + F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCacheReader.cpp; path = dyld3/LaunchCacheReader.cpp; sourceTree = ""; usesTabs = 0; }; + F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LaunchCacheWriter.cpp; path = dyld3/LaunchCacheWriter.cpp; sourceTree = ""; usesTabs = 0; }; + F98692081DC3EF4800CBEDE6 /* LaunchCacheWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LaunchCacheWriter.h; path = dyld3/LaunchCacheWriter.h; sourceTree = ""; usesTabs = 0; }; + F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AdjustDylibSegments.cpp; path = "dyld3/shared-cache/AdjustDylibSegments.cpp"; sourceTree = ""; usesTabs = 0; }; + F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DyldSharedCache.h; path = "dyld3/shared-cache/DyldSharedCache.h"; sourceTree = ""; usesTabs = 0; }; + F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FileUtils.cpp; path = "dyld3/shared-cache/FileUtils.cpp"; sourceTree = ""; usesTabs = 0; }; + F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FileUtils.h; path = "dyld3/shared-cache/FileUtils.h"; sourceTree = ""; usesTabs = 0; }; + F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ObjC1Abstraction.hpp; path = "dyld3/shared-cache/ObjC1Abstraction.hpp"; sourceTree = ""; usesTabs = 0; }; + F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ObjC2Abstraction.hpp; path = "dyld3/shared-cache/ObjC2Abstraction.hpp"; sourceTree = ""; usesTabs = 0; }; + F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerBranches.cpp; path = "dyld3/shared-cache/OptimizerBranches.cpp"; sourceTree = ""; usesTabs = 0; }; + F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerLinkedit.cpp; path = "dyld3/shared-cache/OptimizerLinkedit.cpp"; sourceTree = ""; usesTabs = 0; }; + F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OptimizerObjC.cpp; path = "dyld3/shared-cache/OptimizerObjC.cpp"; sourceTree = ""; usesTabs = 0; }; + F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DyldSharedCache.cpp; path = "dyld3/shared-cache/DyldSharedCache.cpp"; sourceTree = ""; usesTabs = 0; }; + F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = update_dyld_shared_cache.cpp; path = "dyld3/shared-cache/update_dyld_shared_cache.cpp"; sourceTree = ""; usesTabs = 0; }; + F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Diagnostics.cpp; path = dyld3/Diagnostics.cpp; sourceTree = ""; usesTabs = 0; }; + F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CacheBuilder.cpp; path = "dyld3/shared-cache/CacheBuilder.cpp"; sourceTree = ""; usesTabs = 0; }; + F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CacheBuilder.h; path = "dyld3/shared-cache/CacheBuilder.h"; sourceTree = ""; usesTabs = 0; }; + F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dyld_cache_format.h; path = "dyld3/shared-cache/dyld_cache_format.h"; sourceTree = ""; usesTabs = 0; }; + F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CodeSigningTypes.h; path = dyld3/CodeSigningTypes.h; sourceTree = ""; usesTabs = 0; }; + F988F0BA1E2FDF5B003AED79 /* execserver.defs */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.mig; name = execserver.defs; path = testing/nocr/execserver.defs; sourceTree = ""; }; + F98D274C0AA79D7400416316 /* dyld_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_images.h; path = "include/mach-o/dyld_images.h"; sourceTree = ""; }; + F99986FA1F198C0C00D523F5 /* dyld-potential-framework-overrides */ = {isa = PBXFileReference; explicitFileType = text; name = "dyld-potential-framework-overrides"; path = "dyld3/dyld-potential-framework-overrides"; sourceTree = ""; }; + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dyld_shared_cache_util.cpp; sourceTree = ""; }; + F99B8E670FEC121100701838 /* dyld_shared_cache_util */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld_shared_cache_util; sourceTree = BUILT_PRODUCTS_DIR; }; + F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = libdyld_data_symbols.dirty; path = src/libdyld_data_symbols.dirty; sourceTree = ""; }; + F99EE6AE06B48D4200BF1992 /* dlfcn.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dlfcn.h; path = include/dlfcn.h; sourceTree = ""; }; + F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = dyld_stub_binder.s; path = src/dyld_stub_binder.s; sourceTree = ""; }; + F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dyldLibSystemGlue.c; path = src/dyldLibSystemGlue.c; sourceTree = ""; }; + F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = threadLocalVariables.c; path = src/threadLocalVariables.c; sourceTree = ""; }; + F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = threadLocalHelpers.s; path = src/threadLocalHelpers.s; sourceTree = ""; }; + F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libclosured.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLibSystemInterface.h; path = src/dyldLibSystemInterface.h; sourceTree = ""; }; + F9AC7E930B7BB67700FEB38B /* version.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AFEA3216F15CE300CB5161 /* start_glue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = start_glue.h; path = src/start_glue.h; sourceTree = ""; }; + F9B01E3D0739ABDE00CF981B /* dyld.exp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.exports; name = dyld.exp; path = src/dyld.exp; sourceTree = SOURCE_ROOT; }; + F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DyldCacheParser.cpp; path = dyld3/DyldCacheParser.cpp; sourceTree = ""; }; + F9B3CAED1EEB5D0D00C9A48B /* DyldCacheParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DyldCacheParser.h; path = dyld3/DyldCacheParser.h; sourceTree = ""; }; + F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = make_ios_dyld_cache.cpp; path = "dyld3/shared-cache/make_ios_dyld_cache.cpp"; sourceTree = ""; }; + F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = APIs_macOS.cpp; path = dyld3/APIs_macOS.cpp; sourceTree = ""; }; + F9C275581DA71A13007A5D8A /* Loading.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Loading.cpp; path = dyld3/Loading.cpp; sourceTree = ""; usesTabs = 0; }; + F9C275591DA71A13007A5D8A /* Loading.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Loading.h; path = dyld3/Loading.h; sourceTree = ""; usesTabs = 0; }; + F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-shared-cache.h"; path = "include/objc-shared-cache.h"; sourceTree = ""; usesTabs = 0; }; + F9CE30781208F1B50098B590 /* dsc_extractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_extractor.cpp; sourceTree = ""; }; + F9CE30791208F1B50098B590 /* dsc_extractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_extractor.h; sourceTree = ""; }; + F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = dsc_extractor.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.man; path = update_dyld_shared_cache.1; sourceTree = ""; }; + F9D49CCB1458B95200F86ADD /* start_glue.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; name = start_glue.s; path = src/start_glue.s; sourceTree = ""; }; + F9D862441DC9759C000A199A /* dyld_closure_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld_closure_util.cpp; path = "dyld3/shared-cache/dyld_closure_util.cpp"; sourceTree = ""; usesTabs = 0; }; + F9DDEDAA1E28787900A753DC /* closured.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = closured.cpp; path = dyld3/closured/closured.cpp; sourceTree = ""; }; + F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; name = closuredProtocol.defs; path = dyld3/closured/closuredProtocol.defs; sourceTree = ""; }; + F9DDEDAC1E28787900A753DC /* closuredtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = closuredtypes.h; path = dyld3/closured/closuredtypes.h; sourceTree = ""; }; + F9DDEDAD1E28787900A753DC /* com.apple.dyld.closured.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = com.apple.dyld.closured.plist; path = dyld3/closured/com.apple.dyld.closured.plist; sourceTree = ""; }; + F9DDEDB21E2878CA00A753DC /* closured */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = closured; sourceTree = BUILT_PRODUCTS_DIR; }; + F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = dlopen_preflight.3; sourceTree = ""; }; + F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ClosureBuffer.cpp; path = dyld3/ClosureBuffer.cpp; sourceTree = ""; }; + F9E5E2C51EB00A870013A0BB /* ClosureBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ClosureBuffer.h; path = dyld3/ClosureBuffer.h; sourceTree = ""; }; + F9ED4C980630A76000DF4E74 /* dyld */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dyld; 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; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dyld.cpp; path = src/dyld.cpp; sourceTree = SOURCE_ROOT; usesTabs = 1; }; + F9ED4CC80630A7F100DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = src/dyld.h; sourceTree = SOURCE_ROOT; }; + F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldAPIs.cpp; path = src/dyldAPIs.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = dyldExceptions.c; path = src/dyldExceptions.c; sourceTree = SOURCE_ROOT; }; + F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldInitialization.cpp; path = src/dyldInitialization.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldLock.cpp; path = src/dyldLock.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCD0630A7F100DF4E74 /* dyldLock.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyldLock.h; path = src/dyldLock.h; sourceTree = SOURCE_ROOT; }; + F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = dyldNew.cpp; path = src/dyldNew.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = dyldStartup.s; path = src/dyldStartup.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; }; + F9ED4CD00630A7F100DF4E74 /* glue.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = glue.c; path = src/glue.c; sourceTree = SOURCE_ROOT; }; + F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoader.cpp; path = src/ImageLoader.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoader.h; path = src/ImageLoader.h; sourceTree = SOURCE_ROOT; }; + F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ImageLoaderMachO.cpp; path = src/ImageLoaderMachO.cpp; sourceTree = SOURCE_ROOT; }; + F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = ImageLoaderMachO.h; path = src/ImageLoaderMachO.h; sourceTree = SOURCE_ROOT; }; + F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.asm; name = stub_binding_helper.s; path = src/stub_binding_helper.s; sourceTree = SOURCE_ROOT; tabWidth = 8; usesTabs = 1; }; + F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_gdb.h; path = "include/mach-o/dyld_gdb.h"; sourceTree = SOURCE_ROOT; }; + F9ED4CE90630A80600DF4E74 /* dyld_priv.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld_priv.h; path = "include/mach-o/dyld_priv.h"; sourceTree = SOURCE_ROOT; }; + F9ED4CEA0630A80600DF4E74 /* dyld.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = dyld.h; path = "include/mach-o/dyld.h"; sourceTree = SOURCE_ROOT; }; + F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; name = update_dyld_shared_cache_entitlements.plist; path = "dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist"; sourceTree = ""; }; + F9EDC09F1F0478A300B030F4 /* closured_entitlements.plist */ = {isa = PBXFileReference; explicitFileType = text.plist.info; name = closured_entitlements.plist; path = dyld3/closured/closured_entitlements.plist; sourceTree = ""; }; + F9EDC0A01F0481B400B030F4 /* closured.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = closured.xcconfig; sourceTree = ""; }; + F9F2A5590F7AEE9800B7C9EB /* libdsc.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdsc.a; sourceTree = BUILT_PRODUCTS_DIR; }; + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsc_iterator.cpp; sourceTree = ""; }; + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsc_iterator.h; sourceTree = ""; }; + F9F6F4261C1FAF8000BD8FED /* testing */ = {isa = PBXFileReference; lastKnownFileType = folder; path = testing; sourceTree = ""; }; + F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PathOverrides.cpp; path = dyld3/PathOverrides.cpp; sourceTree = ""; }; + F9F76FAF1E08CFF200828678 /* PathOverrides.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PathOverrides.h; path = dyld3/PathOverrides.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3703A11D1B38C1B300ADBA7F /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 376ABDB61C592CC0009F0011 /* Metabom.framework in Frameworks */, + 378EE3B21BE88C4A001C99FB /* Bom.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 377685FD1AC4B27D00026E6C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 376ED1D81C46F2710051DD54 /* Metabom.framework in Frameworks */, + 378EE3B11BE88C47001C99FB /* Bom.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F922C8141F33B73800D8F479 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93937300A94FAF700070A07 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F94C22251E513CA90079E5DD /* CoreFoundation.framework in Frameworks */, + F92015701DDFEBAF00816A4A /* Bom.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F963543D1DCD74A400895049 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F97C61A41DBAD1A900A84CD7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F97FF3531C23638F000ACDD2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F99B8E540FEC10F600701838 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AB02B51F329FAA00EE96C4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9D1001014D8D0BA00099D91 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9DDEDAF1E2878CA00A753DC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F90F7C081E9C6B8B00535722 /* libCrashReporterClient.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9F2A5570F7AEE9800B7C9EB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + EF799FE7070D27BB00F78484 /* man */ = { + isa = PBXGroup; + children = ( + EF799FE8070D27BB00F78484 /* man1 */, + EF799FEA070D27BB00F78484 /* man3 */, + ); + name = man; + path = doc/man; + sourceTree = SOURCE_ROOT; + }; + EF799FE8070D27BB00F78484 /* man1 */ = { + isa = PBXGroup; + children = ( + F94942B21E6796D40019AE08 /* closured.1 */, + EF799FE9070D27BB00F78484 /* dyld.1 */, + F97FF3631C237F5C000ACDD2 /* nocr.1 */, + F9D238D90A9E19A0002B55C7 /* update_dyld_shared_cache.1 */, + ); + name = man1; + path = doc/man/man1; + sourceTree = SOURCE_ROOT; + }; + EF799FEA070D27BB00F78484 /* man3 */ = { + isa = PBXGroup; + children = ( + EF799FEB070D27BB00F78484 /* dladdr.3 */, + EF799FEC070D27BB00F78484 /* dlclose.3 */, + EF799FED070D27BB00F78484 /* dlerror.3 */, + EF799FEE070D27BB00F78484 /* dlopen.3 */, + EF799FEF070D27BB00F78484 /* dlsym.3 */, + F9E572000A66EF41007D9BE9 /* dlopen_preflight.3 */, + EF799FF0070D27BB00F78484 /* dyld.3 */, + ); + name = man3; + path = doc/man/man3; + sourceTree = SOURCE_ROOT; + }; + F939373D0A94FC4700070A07 /* launch-cache */ = { + isa = PBXGroup; + children = ( + F939373E0A94FC4700070A07 /* Architectures.hpp */, + F939373F0A94FC4700070A07 /* CacheFileAbstraction.hpp */, + F93937400A94FC4700070A07 /* dyld_cache_format.h */, + F93937410A94FC4700070A07 /* FileAbstraction.hpp */, + F93937430A94FC4700070A07 /* MachOFileAbstraction.hpp */, + F95C95160E994796007B7CB8 /* MachOTrie.hpp */, + F9F2A56E0F7AEEE300B7C9EB /* dsc_iterator.cpp */, + F9F2A56F0F7AEEE300B7C9EB /* dsc_iterator.h */, + F9CE30781208F1B50098B590 /* dsc_extractor.cpp */, + F9CE30791208F1B50098B590 /* dsc_extractor.h */, + F99B8E620FEC11B400701838 /* dyld_shared_cache_util.cpp */, + ); + path = "launch-cache"; + sourceTree = ""; + }; + F94C22231E513CA90079E5DD /* Frameworks */ = { + isa = PBXGroup; + children = ( + F90F7C071E9C6B8B00535722 /* libCrashReporterClient.a */, + 37F7A5961BB363820039043A /* Bom.framework */, + 376ED1D71C46F2710051DD54 /* Metabom.framework */, + F94C22241E513CA90079E5DD /* CoreFoundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F96D19A41D9363B7007AF3CE /* dyld3 */ = { + isa = PBXGroup; + children = ( + F99986FA1F198C0C00D523F5 /* dyld-potential-framework-overrides */, + F9DDEDA91E28785800A753DC /* closured */, + F98692161DC3EF7700CBEDE6 /* shared-cache */, + F977DDC91E53BEA700609230 /* SharedCacheRuntime.cpp */, + F977DDCA1E53BEA700609230 /* SharedCacheRuntime.h */, + F9E5E2C41EB00A870013A0BB /* ClosureBuffer.cpp */, + F9E5E2C51EB00A870013A0BB /* ClosureBuffer.h */, + F9C275581DA71A13007A5D8A /* Loading.cpp */, + F9C275591DA71A13007A5D8A /* Loading.h */, + F97C61A01D9CA6B800A84CD7 /* Logging.cpp */, + F97C61A11D9CA6B800A84CD7 /* Logging.h */, + 37D7DAFE1E96F0ED00D52CEA /* Tracing.cpp */, + 37D7DAFF1E96F0ED00D52CEA /* Tracing.h */, + F98692221DC4028B00CBEDE6 /* CodeSigningTypes.h */, + F97C619D1D96C5BE00A84CD7 /* libdyldEntryVector.h */, + F97C619E1D98292700A84CD7 /* libdyldEntryVector.cpp */, + F986921B1DC3F07C00CBEDE6 /* Diagnostics.cpp */, + F98692001DC3EF4800CBEDE6 /* Diagnostics.h */, + F98692021DC3EF4800CBEDE6 /* LaunchCache.h */, + F98692041DC3EF4800CBEDE6 /* LaunchCacheFormat.h */, + F98692051DC3EF4800CBEDE6 /* LaunchCachePrinter.cpp */, + F98692061DC3EF4800CBEDE6 /* LaunchCacheReader.cpp */, + F98692071DC3EF4800CBEDE6 /* LaunchCacheWriter.cpp */, + F98692081DC3EF4800CBEDE6 /* LaunchCacheWriter.h */, + F9F76FAE1E08CFF200828678 /* PathOverrides.cpp */, + F9F76FAF1E08CFF200828678 /* PathOverrides.h */, + F96D19C11D95C6D6007AF3CE /* APIs.h */, + F96D19A51D9363D6007AF3CE /* APIs.cpp */, + F9C15A491E1F7D960006E570 /* APIs_macOS.cpp */, + F96D19A71D9363D6007AF3CE /* AllImages.h */, + F96D19A61D9363D6007AF3CE /* AllImages.cpp */, + F9B3CAED1EEB5D0D00C9A48B /* DyldCacheParser.h */, + F9B3CAEB1EEB5CFA00C9A48B /* DyldCacheParser.cpp */, + F96D19A91D94576E007AF3CE /* MachOParser.h */, + F96D19BE1D94A6DC007AF3CE /* MachOParser.cpp */, + F922C81B1F33B86300D8F479 /* libclosured-stub.cpp */, + ); + name = dyld3; + sourceTree = ""; + }; + F971DD121A4A0E0700BBDD52 /* configs */ = { + isa = PBXGroup; + children = ( + F971DD131A4A0E0700BBDD52 /* base.xcconfig */, + F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */, + F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */, + F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */, + F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */, + F9EDC0A01F0481B400B030F4 /* closured.xcconfig */, + ); + path = configs; + sourceTree = SOURCE_ROOT; + }; + F97FF3571C23638F000ACDD2 /* nocr */ = { + isa = PBXGroup; + children = ( + F97FF3581C23638F000ACDD2 /* main.c */, + ); + path = nocr; + sourceTree = ""; + }; + F98692161DC3EF7700CBEDE6 /* shared-cache */ = { + isa = PBXGroup; + children = ( + 37908A2A1E3A85A4009613FA /* FileAbstraction.hpp */, + 37908A2B1E3A85A4009613FA /* MachOFileAbstraction.hpp */, + F986921E1DC3F86C00CBEDE6 /* dyld_cache_format.h */, + F98692091DC3EF6C00CBEDE6 /* AdjustDylibSegments.cpp */, + 37C5C2FC1E5CD154006B32C9 /* BuilderUtils.h */, + 37C5C2FB1E5CD154006B32C9 /* BuilderUtils.mm */, + F986921D1DC3F86C00CBEDE6 /* CacheBuilder.h */, + F986921C1DC3F86C00CBEDE6 /* CacheBuilder.cpp */, + F986920C1DC3EF6C00CBEDE6 /* DyldSharedCache.h */, + F98692141DC3EF6C00CBEDE6 /* DyldSharedCache.cpp */, + F986920E1DC3EF6C00CBEDE6 /* FileUtils.h */, + F986920D1DC3EF6C00CBEDE6 /* FileUtils.cpp */, + F963546A1DD8D8D300895049 /* ImageProxy.h */, + F963546B1DD8F2A800895049 /* ImageProxy.cpp */, + 37908A2C1E3A85A4009613FA /* Manifest.h */, + 37908A281E3A853E009613FA /* Manifest.mm */, + F986920F1DC3EF6C00CBEDE6 /* ObjC1Abstraction.hpp */, + F98692101DC3EF6C00CBEDE6 /* ObjC2Abstraction.hpp */, + F98692111DC3EF6C00CBEDE6 /* OptimizerBranches.cpp */, + F98692121DC3EF6C00CBEDE6 /* OptimizerLinkedit.cpp */, + F98692131DC3EF6C00CBEDE6 /* OptimizerObjC.cpp */, + F902031F1DEE83C000AC3F76 /* StringUtils.h */, + 37908A2D1E3A85A4009613FA /* Trie.hpp */, + F9D862441DC9759C000A199A /* dyld_closure_util.cpp */, + 37908A271E3A853E009613FA /* dyld_shared_cache_builder.mm */, + 37908A291E3A853E009613FA /* multi_dyld_shared_cache_builder.mm */, + F9C15A451E19C2F50006E570 /* make_ios_dyld_cache.cpp */, + F963542E1DCD736000895049 /* update_dyld_sim_shared_cache.cpp */, + F98692151DC3EF6C00CBEDE6 /* update_dyld_shared_cache.cpp */, + F9EDC09E1F04767300B030F4 /* update_dyld_shared_cache_entitlements.plist */, + ); + name = "shared-cache"; + sourceTree = ""; + }; + F9DDEDA91E28785800A753DC /* closured */ = { + isa = PBXGroup; + children = ( + F9DDEDAA1E28787900A753DC /* closured.cpp */, + F9EDC09F1F0478A300B030F4 /* closured_entitlements.plist */, + F9DDEDAB1E28787900A753DC /* closuredProtocol.defs */, + F9DDEDAC1E28787900A753DC /* closuredtypes.h */, + F913C8501E9312A100458AA3 /* com.apple.dyld.closured.sb */, + F9DDEDAD1E28787900A753DC /* com.apple.dyld.closured.plist */, + ); + name = closured; + sourceTree = ""; + }; + F9ED4C870630A72200DF4E74 = { + isa = PBXGroup; + children = ( + F988F0BA1E2FDF5B003AED79 /* execserver.defs */, + F9F6F4261C1FAF8000BD8FED /* testing */, + F971DD121A4A0E0700BBDD52 /* configs */, + F9ED4CBB0630A7AA00DF4E74 /* src */, + F9ED4CC30630A7BE00DF4E74 /* doc */, + F9ED4CBE0630A7B100DF4E74 /* include */, + F97FF3571C23638F000ACDD2 /* nocr */, + F9ED4C990630A76000DF4E74 /* Products */, + F96D19A41D9363B7007AF3CE /* dyld3 */, + F939373D0A94FC4700070A07 /* launch-cache */, + F94C22231E513CA90079E5DD /* Frameworks */, + ); + indentWidth = 4; + sourceTree = ""; + tabWidth = 4; + usesTabs = 0; + }; + F9ED4C990630A76000DF4E74 /* Products */ = { + isa = PBXGroup; + children = ( + F9ED4C980630A76000DF4E74 /* dyld */, + F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */, + F93937320A94FAF700070A07 /* update_dyld_shared_cache */, + F9F2A5590F7AEE9800B7C9EB /* libdsc.a */, + F99B8E670FEC121100701838 /* dyld_shared_cache_util */, + F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */, + 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */, + 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */, + F97FF3561C23638F000ACDD2 /* nocr */, + F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */, + F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */, + F9DDEDB21E2878CA00A753DC /* closured */, + F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */, + F922C8171F33B73800D8F479 /* libclosured.dylib */, + ); + name = Products; + sourceTree = ""; + }; + F9ED4CBB0630A7AA00DF4E74 /* src */ = { + isa = PBXGroup; + children = ( + F97FF35F1C236402000ACDD2 /* nocr.c */, + F9ED4CC60630A7F100DF4E74 /* dyld_gdb.cpp */, + F9ED4CC70630A7F100DF4E74 /* dyld.cpp */, + F9ED4CC80630A7F100DF4E74 /* dyld.h */, + F9ED4CC90630A7F100DF4E74 /* dyldAPIs.cpp */, + F9ED4CCA0630A7F100DF4E74 /* dyldExceptions.c */, + F9AB709D0BA75730002F6068 /* dyldLibSystemInterface.h */, + F913FAD90630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp */, + F9ED4CCB0630A7F100DF4E74 /* dyldInitialization.cpp */, + F9ED4CCC0630A7F100DF4E74 /* dyldLock.cpp */, + F9ED4CCD0630A7F100DF4E74 /* dyldLock.h */, + F9ED4CCE0630A7F100DF4E74 /* dyldNew.cpp */, + F9ED4CCF0630A7F100DF4E74 /* dyldStartup.s */, + F9D49CCB1458B95200F86ADD /* start_glue.s */, + F9AFEA3216F15CE300CB5161 /* start_glue.h */, + F99EFC0D0EAD60E8001032B8 /* dyld_stub_binder.s */, + F9ED4CD00630A7F100DF4E74 /* glue.c */, + F981BB8B170FC24400A686D6 /* dyldSyscallInterface.h */, + F9ED4CD10630A7F100DF4E74 /* ImageLoader.cpp */, + F9ED4CD20630A7F100DF4E74 /* ImageLoader.h */, + F9ED4CD30630A7F100DF4E74 /* ImageLoaderMachO.cpp */, + F9ED4CD40630A7F100DF4E74 /* ImageLoaderMachO.h */, + F94DB9000F0A9B1700323715 /* ImageLoaderMachOClassic.cpp */, + F94DB9010F0A9B1700323715 /* ImageLoaderMachOClassic.h */, + F94DB9020F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp */, + F94DB9030F0A9B1700323715 /* ImageLoaderMachOCompressed.h */, + F9280B7A1AB9DCA000B18AEC /* ImageLoaderMegaDylib.h */, + F9280B791AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp */, + F99DE0361AAE4F0400669496 /* libdyld_data_symbols.dirty */, + F9ED4CD50630A7F100DF4E74 /* stub_binding_helper.s */, + F9B01E3D0739ABDE00CF981B /* dyld.exp */, + F976F548127B90F8004BA2A5 /* dyld.order */, + F9AC7E930B7BB67700FEB38B /* version.c */, + F9A221E60F3A6D7C00D15F73 /* dyldLibSystemGlue.c */, + F9A6D6E2116F9DF20051CC16 /* threadLocalVariables.c */, + F9A6D70B116FBBD10051CC16 /* threadLocalHelpers.s */, + F958D4751C7FCD4A00A0B199 /* dyld_process_info_internal.h */, + F958D4761C7FCD4A00A0B199 /* dyld_process_info_notify.cpp */, + F95090E41C5AD1B30031F81D /* dyld_process_info.cpp */, + ); + name = src; + sourceTree = ""; + }; + F9ED4CBE0630A7B100DF4E74 /* include */ = { + isa = PBXGroup; + children = ( + F96D19711D7F63EE007AF3CE /* expand.pl */, + F95090D01C5AB89A0031F81D /* dyld_process_info.h */, + F98D274C0AA79D7400416316 /* dyld_images.h */, + F918691408B16D2500E0F9DB /* dyld-interposing.h */, + F9ED4CEA0630A80600DF4E74 /* dyld.h */, + F9ED4CE80630A80600DF4E74 /* dyld_gdb.h */, + F9ED4CE90630A80600DF4E74 /* dyld_priv.h */, + F99EE6AE06B48D4200BF1992 /* dlfcn.h */, + F9C69EFD14EC8ABF009CAE2E /* objc-shared-cache.h */, + ); + name = include; + sourceTree = ""; + }; + F9ED4CC30630A7BE00DF4E74 /* doc */ = { + isa = PBXGroup; + children = ( + EF799FE7070D27BB00F78484 /* man */, + ); + name = doc; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + F922C8151F33B73800D8F479 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F98F1FBB1E4029CA00EF868D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F99006DD1E411BA70013456D /* dyld_images.h in Headers */, + F99006DE1E411BBC0013456D /* dyld.h in Headers */, + F98F1FBD1E4029E400EF868D /* dyld_priv.h in Headers */, + F960A78A1E40569400840176 /* dyld-interposing.h in Headers */, + F98F1FBF1E4031F800EF868D /* dyld_process_info.h in Headers */, + F99006E01E4130AE0013456D /* dyld_gdb.h in Headers */, + F960A78B1E405DE300840176 /* dyld_cache_format.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AB02B61F329FAA00EE96C4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3703A1211B38C1B300ADBA7F /* Build configuration list for PBXNativeTarget "dyld_shared_cache_builder" */; + buildPhases = ( + 3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */, + 3703A1131B38C1B300ADBA7F /* Sources */, + 3703A11D1B38C1B300ADBA7F /* Frameworks */, + 3703A1201B38C1B300ADBA7F /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dyld_shared_cache_builder; + productName = update_os_interlinked_dylib; + productReference = 3703A1241B38C1B300ADBA7F /* dyld_shared_cache_builder */; + productType = "com.apple.product-type.tool"; + }; + 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */ = { + isa = PBXNativeTarget; + buildConfigurationList = 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */; + buildPhases = ( + 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */, + 377685F41AC4B27D00026E6C /* Sources */, + 377685FD1AC4B27D00026E6C /* Frameworks */, + 377685FE1AC4B27D00026E6C /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = multi_dyld_shared_cache_builder; + productName = update_os_interlinked_dylib; + productReference = 377686021AC4B27D00026E6C /* multi_dyld_shared_cache_builder */; + productType = "com.apple.product-type.tool"; + }; + F922C8161F33B73800D8F479 /* libclosured-stub */ = { + isa = PBXNativeTarget; + buildConfigurationList = F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */; + buildPhases = ( + F922C8131F33B73800D8F479 /* Sources */, + F922C8141F33B73800D8F479 /* Frameworks */, + F922C8151F33B73800D8F479 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "libclosured-stub"; + productName = "libclosured-stub"; + productReference = F922C8171F33B73800D8F479 /* libclosured.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */; + buildPhases = ( + F91083C91702592700831889 /* create dyld_cache_config.h */, + F939372F0A94FAF700070A07 /* Sources */, + F93937300A94FAF700070A07 /* Frameworks */, + F9D238DD0A9E2FEE002B55C7 /* usr|share|man|man1 */, + F991E3030FF1A4EC0082CCC9 /* do not install duplicates */, + F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = update_dyld_shared_cache_tool; + productName = update_dyld_shared_cache; + productReference = F93937320A94FAF700070A07 /* update_dyld_shared_cache */; + productType = "com.apple.product-type.tool"; + }; + F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */ = { + isa = PBXNativeTarget; + buildConfigurationList = F96354421DCD74A400895049 /* Build configuration list for PBXNativeTarget "update_dyld_sim_shared_cache" */; + buildPhases = ( + F96354301DCD74A400895049 /* create dyld_cache_config.h */, + F96354311DCD74A400895049 /* Sources */, + F963543D1DCD74A400895049 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = update_dyld_sim_shared_cache; + productName = update_dyld_shared_cache; + productReference = F96354451DCD74A400895049 /* update_dyld_sim_shared_cache */; + productType = "com.apple.product-type.tool"; + }; + F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */ = { + isa = PBXNativeTarget; + buildConfigurationList = F97C61AB1DBAD1A900A84CD7 /* Build configuration list for PBXNativeTarget "dyld_closure_util" */; + buildPhases = ( + F97C61A31DBAD1A900A84CD7 /* Sources */, + F97C61A41DBAD1A900A84CD7 /* Frameworks */, + F97C61A51DBAD1A900A84CD7 /* Copy Files */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dyld_closure_util; + productName = dyld_closure_util; + productReference = F97C61A71DBAD1A900A84CD7 /* dyld_closure_util */; + productType = "com.apple.product-type.tool"; + }; + F97FF3551C23638F000ACDD2 /* nocr */ = { + isa = PBXNativeTarget; + buildConfigurationList = F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */; + buildPhases = ( + F97FF3521C23638F000ACDD2 /* Sources */, + F97FF3531C23638F000ACDD2 /* Frameworks */, + F97FF3541C23638F000ACDD2 /* install man page */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = nocr; + productName = nocr; + productReference = F97FF3561C23638F000ACDD2 /* nocr */; + 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"; + }; + F9AB02B71F329FAA00EE96C4 /* libclosured */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */; + buildPhases = ( + F9AB02B41F329FAA00EE96C4 /* Sources */, + F9AB02B51F329FAA00EE96C4 /* Frameworks */, + F9AB02B61F329FAA00EE96C4 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libclosured; + productName = libclosured; + productReference = F9AB02B81F329FAA00EE96C4 /* libclosured.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + F9D1001114D8D0BA00099D91 /* dsc_extractor */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */; + buildPhases = ( + F9D1000F14D8D0BA00099D91 /* Sources */, + F9D1001014D8D0BA00099D91 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dsc_extractor; + productName = dsc_extractor; + productReference = F9D1001214D8D0BA00099D91 /* dsc_extractor.bundle */; + productType = "com.apple.product-type.library.dynamic"; + }; + F9DDEDB11E2878CA00A753DC /* closured */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9DDEDB61E2878CB00A753DC /* Build configuration list for PBXNativeTarget "closured" */; + buildPhases = ( + F9DDEDAE1E2878CA00A753DC /* Sources */, + F9DDEDAF1E2878CA00A753DC /* Frameworks */, + F94942B01E6794650019AE08 /* installl plist */, + F94942B11E67965C0019AE08 /* install man page */, + F913C8511E93137700458AA3 /* Install sandbox profile */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = closured; + productName = closured; + productReference = F9DDEDB21E2878CA00A753DC /* closured */; + productType = "com.apple.product-type.tool"; + }; + F9ED4C970630A76000DF4E74 /* dyld */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */; + buildPhases = ( + F9D050C811DD701A00FB0A29 /* configure archives */, + F96D19A31D91D733007AF3CE /* make dyld_priv.h */, + F9ED4C950630A76000DF4E74 /* Sources */, + F907E2490FA6469000BFEDBD /* install iPhone file */, + F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */, + F99B8EB60FEC236500701838 /* suppress macosx dyld_shared_cache_util */, + ); + buildRules = ( + F921D318070376B0000D1056 /* PBXBuildRule */, + F921D317070376A6000D1056 /* PBXBuildRule */, + F921D3160703769A000D1056 /* PBXBuildRule */, + ); + dependencies = ( + F922C8121F33B62700D8F479 /* PBXTargetDependency */, + F99B8EB20FEC220C00701838 /* PBXTargetDependency */, + F96543A11E343601003C5540 /* PBXTargetDependency */, + ); + name = dyld; + productName = dyld; + productReference = F9ED4C980630A76000DF4E74 /* dyld */; + productType = "com.apple.product-type.tool"; + }; + F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */; + buildPhases = ( + F9ED4C9C0630A76B00DF4E74 /* Sources */, + F959621018849DF20003E4D4 /* add dyld symlink */, + F98F1FBB1E4029CA00EF868D /* Headers */, + F960A78C1E405E2300840176 /* expand dyld_priv.h macros */, + F99006DF1E411C500013456D /* install dlfcn.h */, + ); + buildRules = ( + F921D31E070376F1000D1056 /* PBXBuildRule */, + F9574C4906C94DA700142BFA /* PBXBuildRule */, + ); + dependencies = ( + F922C81E1F33B96300D8F479 /* PBXTargetDependency */, + ); + name = libdyld.dylib; + productName = libdyld; + productReference = F9ED4C9F0630A76B00DF4E74 /* libdyld.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + F9F2A5580F7AEE9800B7C9EB /* libdsc */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */; + buildPhases = ( + F9F2A5560F7AEE9800B7C9EB /* Sources */, + F9F2A5570F7AEE9800B7C9EB /* Frameworks */, + F98C78D10F7C00EA006257D2 /* usr|local|include|mach-o */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdsc; + productName = dsc; + productReference = F9F2A5590F7AEE9800B7C9EB /* libdsc.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F9ED4C8B0630A72300DF4E74 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0900; + TargetAttributes = { + 37A0AD0A1C15FFF500731E50 = { + CreatedOnToolsVersion = 8.0; + }; + F922C8161F33B73800D8F479 = { + CreatedOnToolsVersion = 9.0; + }; + F97C61A61DBAD1A900A84CD7 = { + CreatedOnToolsVersion = 8.0; + DevelopmentTeam = 59GAB85EFG; + ProvisioningStyle = Automatic; + }; + F97FF3551C23638F000ACDD2 = { + CreatedOnToolsVersion = 8.0; + }; + F9AB02B71F329FAA00EE96C4 = { + CreatedOnToolsVersion = 9.0; + }; + F9DDEDB11E2878CA00A753DC = { + CreatedOnToolsVersion = 8.2; + DevelopmentTeam = 59GAB85EFG; + ProvisioningStyle = Automatic; + }; + F9F6F4271C1FB0A700BD8FED = { + CreatedOnToolsVersion = 8.0; + }; + }; + }; + buildConfigurationList = F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = F9ED4C870630A72200DF4E74; + productRefGroup = F9ED4C990630A76000DF4E74 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F9ED4C920630A73900DF4E74 /* all */, + 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */, + F908134211D3ED0B00626CC1 /* libdyld */, + F9ED4C970630A76000DF4E74 /* dyld */, + F9DDEDB11E2878CA00A753DC /* closured */, + F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */, + F93937310A94FAF700070A07 /* update_dyld_shared_cache_tool */, + 377685F21AC4B27D00026E6C /* multi_dyld_shared_cache_builder */, + 3703A1111B38C1B300ADBA7F /* dyld_shared_cache_builder */, + F963542F1DCD74A400895049 /* update_dyld_sim_shared_cache */, + F99B8E550FEC10F600701838 /* dyld_shared_cache_util */, + F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */, + F9F2A5580F7AEE9800B7C9EB /* libdsc */, + F9D1001114D8D0BA00099D91 /* dsc_extractor */, + F9F6F4271C1FB0A700BD8FED /* dyld_tests */, + F97FF3551C23638F000ACDD2 /* nocr */, + F9AB02B71F329FAA00EE96C4 /* libclosured */, + F922C8161F33B73800D8F479 /* libclosured-stub */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3703A1121B38C1B300ADBA7F /* make dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n"; + showEnvVarsInLog = 0; + }; + 377685F31AC4B27D00026E6C /* make dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\nif [ -r \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/DirtyDataFiles/dirty-data-segments-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\nif [ -r \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" ]; then\n mkdir -p \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\n cp \"${ARM_SDK}/AppleInternal/OrderFiles/dylib-order.txt\" \"${DSTROOT}/${INSTALL_LOCATION}/usr/local/bin\"\nfi\n\n"; + showEnvVarsInLog = 0; + }; + F907E2490FA6469000BFEDBD /* install iPhone file */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "install iPhone file"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "if [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n\tmkdir -p ${DSTROOT}//System/Library/Caches/com.apple.dyld\n\techo \"existence of this file enables dyld to have dylibs override shared cache\" > ${DSTROOT}//System/Library/Caches/com.apple.dyld/enable-dylibs-to-override-cache\nfi\n"; + showEnvVarsInLog = 0; + }; + F91083C91702592700831889 /* create dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "create dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\n"; + showEnvVarsInLog = 0; + }; + F913C8511E93137700458AA3 /* Install sandbox profile */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/dyld3/closured/com.apple.dyld.closured.sb", + ); + name = "Install sandbox profile"; + outputPaths = ( + "${DSTROOT}/System/Library/Sandbox/Profiles/com.apple.dyld.closured.sb", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "if [ ${OS} = \"MACOS\" ]; then\n mkdir -p ${DSTROOT}/System/Library/Sandbox/Profiles\n cp ${SRCROOT}/dyld3/closured/com.apple.dyld.closured.sb ${DSTROOT}/System/Library/Sandbox/Profiles/com.apple.dyld.closured.sb\nfi"; + showEnvVarsInLog = 0; + }; + F9213B3F18BFC9CB001CB6E8 /* simulator entitlement */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "simulator entitlement"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "if [ \"${PRODUCT_NAME}\" = \"dyld_sim\" ]\nthen\n /usr/bin/codesign --force --sign - --entitlements ${SRCROOT}/dyld_sim-entitlements.plist ${INSTALL_DIR}/dyld_sim\nfi\n"; + showEnvVarsInLog = 0; + }; + F94182D61E60E74E00D8EF25 /* pre-platform builds */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "pre-platform builds"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "xcodebuild install -target multi_dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n\nif [ \"${RC_PURPLE}\" = \"YES\" ]\nthen\n\txcodebuild install -target dyld_shared_cache_builder SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\n\txcodebuild install -target update_dyld_sim_shared_cache SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nelse\n\txcodebuild install -target update_dyld_shared_cache_tool SDKROOT=\"${SDKROOT}\" MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET} OBJROOT=\"${OBJROOT}\" SRCROOT=\"${SRCROOT}\" DSTROOT=\"${DSTROOT}\" SYMROOT=\"${SYMROOT}\" RC_ProjectSourceVersion=\"${RC_ProjectSourceVersion}\"\nfi"; + showEnvVarsInLog = 0; + }; + F94942B01E6794650019AE08 /* installl plist */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/dyld3/closured/com.apple.dyld.closured.plist", + ); + name = "installl plist"; + outputPaths = ( + "${DSTROOT}/System/Library/LaunchDaemons/com.apple.dyld.closured.plist", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "mkdir -p ${DSTROOT}/System/Library/LaunchDaemons\nplutil -convert binary1 -o ${DSTROOT}/System/Library/LaunchDaemons/com.apple.dyld.closured.plist ${SRCROOT}/dyld3/closured/com.apple.dyld.closured.plist"; + showEnvVarsInLog = 0; + }; + F959621018849DF20003E4D4 /* add dyld symlink */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "add dyld symlink"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/bash; + shellScript = "if [[ \"${PLATFORM_NAME}\" == *simulator* ]]\nthen\n\tcd ${DSTROOT}/${INSTALL_PATH}\n\tln -s libdyld.dylib libdyld_sim.dylib\nfi\n"; + showEnvVarsInLog = 0; + }; + F960A78C1E405E2300840176 /* expand dyld_priv.h macros */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "${SRCROOT}/include/mach-o/dyld_priv.h", + ); + name = "expand dyld_priv.h macros"; + outputPaths = ( + "${DSTROOT}/usr/local/include/mach-o/dyld_priv.h", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DSTROOT}/usr/local/include/mach-o/dyld_priv.h\n"; + showEnvVarsInLog = 0; + }; + F96354301DCD74A400895049 /* create dyld_cache_config.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "create dyld_cache_config.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dyld_cache_config.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "echo \"\" > ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\nif [ -z \"${ARM_SDK}\" ]; then\n # if iOS SDK not available, use MacOSX SDK\n ARM_SDK=`xcodebuild -sdk macosx.internal -version Path`\nfi\n\nSHARED_REGION_FILE=\"${ARM_SDK}/usr/include/mach/shared_region.h\"\n\n\nif [ -r \"${SHARED_REGION_FILE}\" ]; then\n echo -n \"#define ARM_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM[ \\t]/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_START \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_BASE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n\n echo -n \"#define ARM64_SHARED_REGION_SIZE \" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n awk '/define SHARED_REGION_SIZE_ARM64/ { print $3;}' \"${SHARED_REGION_FILE}\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\n echo \"\" >> ${DERIVED_FILE_DIR}/dyld_cache_config.h\nelse\n echo \"ERROR: File needed to configure update_dyld_shared_cache does not exist '${SHARED_REGION_FILE}'\"\n exit 1\nfi\n\n"; + showEnvVarsInLog = 0; + }; + F96D19A31D91D733007AF3CE /* make dyld_priv.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/include/mach-o/dyld_priv.h", + ); + name = "make dyld_priv.h"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/mach-o/dyld_priv.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "mkdir -p ${DERIVED_FILE_DIR}/mach-o\n${SRCROOT}/bin/expand.pl < ${SRCROOT}/include/mach-o/dyld_priv.h > ${DERIVED_FILE_DIR}/mach-o/dyld_priv.h\n"; + showEnvVarsInLog = 0; + }; + F981C8C21F058F8200452F35 /* mkdir /var/db/dyld */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "mkdir /var/db/dyld"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "mkdir -p ${DSTROOT}/private/var/db/dyld"; + showEnvVarsInLog = 0; + }; + F99006DF1E411C500013456D /* install dlfcn.h */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "install dlfcn.h"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "# xcode only lets you install public headers to one directory\ncp ${SRCROOT}/include/dlfcn.h ${DSTROOT}/usr/include/\n"; + showEnvVarsInLog = 0; + }; + F991E3030FF1A4EC0082CCC9 /* do not install duplicates */ = { + isa = PBXShellScriptBuildPhase; + 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\n\trm -rf ${DSTROOT}/usr/lib\nfi"; + 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\n mkdir -p ${DSTROOT}/AppleInternal/Library/Preferences/\n cp dyld3/dyld-potential-framework-overrides ${DSTROOT}/AppleInternal/Library/Preferences/\nfi\n"; + showEnvVarsInLog = 0; + }; + F9D050C811DD701A00FB0A29 /* configure archives */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + ); + name = "configure archives"; + outputPaths = ( + "$(DERIVED_SOURCES_DIR)/archives.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# link with all .a files in /usr/local/lib/dyld\nls -1 ${SDKROOT}/usr/local/lib/dyld/*.a > ${DERIVED_SOURCES_DIR}/archives.txt \n\n# link with crash report archive if it exists\nif [ -f ${SDKROOT}/usr/local/lib/libCrashReporterClient.a ]\nthen\n echo \\\"${SDKROOT}/usr/local/lib/libCrashReporterClient.a\\\" >> ${DERIVED_SOURCES_DIR}/archives.txt \nfi\n\n"; + showEnvVarsInLog = 0; + }; + F9F6F42B1C1FB0AE00BD8FED /* build */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 12; + files = ( + ); + inputPaths = ( + ); + name = build; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "${SRCROOT}/testing/build_tests.py && cp ${SRCROOT}/testing/run_all_dyld_tests.py ${DSTROOT}/AppleInternal/CoreOS/tests/dyld/\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3703A1131B38C1B300ADBA7F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 37554F521E3F78EB00407388 /* ImageProxy.cpp in Sources */, + 37554F421E3F169600407388 /* CacheBuilder.cpp in Sources */, + 37554F481E3F16BA00407388 /* OptimizerBranches.cpp in Sources */, + 37554F441E3F16A900407388 /* OptimizerObjC.cpp in Sources */, + 37554F581E3F7B6500407388 /* PathOverrides.cpp in Sources */, + 37554F561E3F7B4300407388 /* LaunchCacheWriter.cpp in Sources */, + 37908A301E3ADD15009613FA /* Diagnostics.cpp in Sources */, + 37554F491E3F76E400407388 /* DyldSharedCache.cpp in Sources */, + 37908A311E3EB585009613FA /* MachOParser.cpp in Sources */, + F922AE581EF0D3C300926F9D /* DyldCacheParser.cpp in Sources */, + 37C5C2FE1E5CD154006B32C9 /* BuilderUtils.mm in Sources */, + 37908A2F1E3A864E009613FA /* dyld_shared_cache_builder.mm in Sources */, + 37554F541E3F7B1F00407388 /* LaunchCacheReader.cpp in Sources */, + 37554F461E3F16B600407388 /* OptimizerLinkedit.cpp in Sources */, + 37908A321E3ED667009613FA /* FileUtils.cpp in Sources */, + 37908A2E1E3A8632009613FA /* Manifest.mm in Sources */, + 37554F4B1E3F76E900407388 /* AdjustDylibSegments.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 377685F41AC4B27D00026E6C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 37554F511E3F78EB00407388 /* ImageProxy.cpp in Sources */, + 37554F3F1E3F165100407388 /* Diagnostics.cpp in Sources */, + 37554F471E3F16B900407388 /* OptimizerBranches.cpp in Sources */, + 37554F451E3F16B500407388 /* OptimizerLinkedit.cpp in Sources */, + 37554F571E3F7B6400407388 /* PathOverrides.cpp in Sources */, + 37554F551E3F7B4200407388 /* LaunchCacheWriter.cpp in Sources */, + 37554F401E3F167A00407388 /* MachOParser.cpp in Sources */, + F981C8C11EF06A7800452F35 /* DyldCacheParser.cpp in Sources */, + 37554F411E3F169500407388 /* CacheBuilder.cpp in Sources */, + 37554F431E3F16A800407388 /* OptimizerObjC.cpp in Sources */, + 37C5C2FD1E5CD154006B32C9 /* BuilderUtils.mm in Sources */, + 37554F3B1E3F0FD200407388 /* Manifest.mm in Sources */, + 37554F531E3F7B1E00407388 /* LaunchCacheReader.cpp in Sources */, + 37554F3C1E3F0FD200407388 /* DyldSharedCache.cpp in Sources */, + 37554F3D1E3F0FD200407388 /* FileUtils.cpp in Sources */, + 37554F3E1E3F0FD200407388 /* multi_dyld_shared_cache_builder.mm in Sources */, + 37554F4A1E3F76E800407388 /* AdjustDylibSegments.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F922C8131F33B73800D8F479 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F922C81C1F33B88400D8F479 /* libclosured-stub.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F939372F0A94FAF700070A07 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F98692171DC3EFD500CBEDE6 /* update_dyld_shared_cache.cpp in Sources */, + F98692181DC3EFD700CBEDE6 /* DyldSharedCache.cpp in Sources */, + F981C8BD1EEF447500452F35 /* DyldCacheParser.cpp in Sources */, + F986921F1DC3F98700CBEDE6 /* CacheBuilder.cpp in Sources */, + F98692231DC403F900CBEDE6 /* AdjustDylibSegments.cpp in Sources */, + F98692191DC3EFDA00CBEDE6 /* FileUtils.cpp in Sources */, + F98692201DC3F99300CBEDE6 /* Diagnostics.cpp in Sources */, + F9460DCD1E09FFFC00FEC613 /* PathOverrides.cpp in Sources */, + F98692211DC401B900CBEDE6 /* MachOParser.cpp in Sources */, + F9D862401DC57A27000A199A /* OptimizerObjC.cpp in Sources */, + F94182D51E60A2F100D8EF25 /* OptimizerBranches.cpp in Sources */, + F9A548B31DDBBC75002B4422 /* ImageProxy.cpp in Sources */, + F9D862411DC65A4E000A199A /* LaunchCacheWriter.cpp in Sources */, + F9D862421DC65A53000A199A /* LaunchCacheReader.cpp in Sources */, + F9D8623F1DC41043000A199A /* OptimizerLinkedit.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F96354311DCD74A400895049 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F96354461DCD74BC00895049 /* update_dyld_sim_shared_cache.cpp in Sources */, + F96354331DCD74A400895049 /* DyldSharedCache.cpp in Sources */, + F981C8BF1EEF733C00452F35 /* DyldCacheParser.cpp in Sources */, + F981C8BE1EEF733800452F35 /* ClosureBuffer.cpp in Sources */, + F96354341DCD74A400895049 /* CacheBuilder.cpp in Sources */, + F96354351DCD74A400895049 /* AdjustDylibSegments.cpp in Sources */, + F963546C1DD8F38300895049 /* ImageProxy.cpp in Sources */, + F96354361DCD74A400895049 /* FileUtils.cpp in Sources */, + F96354371DCD74A400895049 /* Diagnostics.cpp in Sources */, + F9460DCE1E0A000600FEC613 /* PathOverrides.cpp in Sources */, + F96354381DCD74A400895049 /* MachOParser.cpp in Sources */, + F96354391DCD74A400895049 /* OptimizerObjC.cpp in Sources */, + F963543A1DCD74A400895049 /* LaunchCacheWriter.cpp in Sources */, + 37C5C2FF1E60D7DE006B32C9 /* OptimizerBranches.cpp in Sources */, + F963543B1DCD74A400895049 /* LaunchCacheReader.cpp in Sources */, + F963543C1DCD74A400895049 /* OptimizerLinkedit.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F97C61A31DBAD1A900A84CD7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9D862451DC975A5000A199A /* dyld_closure_util.cpp in Sources */, + F97C61B31DBAE14200A84CD7 /* MachOParser.cpp in Sources */, + F9D8624D1DC9783E000A199A /* FileUtils.cpp in Sources */, + F9D862461DC975AA000A199A /* Diagnostics.cpp in Sources */, + F926C0471DDBFB7A00941CB1 /* ImageProxy.cpp in Sources */, + F9F76FB01E09CDF400828678 /* PathOverrides.cpp in Sources */, + F9D8624C1DC97717000A199A /* DyldSharedCache.cpp in Sources */, + F9D862471DC975B1000A199A /* LaunchCacheWriter.cpp in Sources */, + F9B3CAEC1EEB5CFB00C9A48B /* DyldCacheParser.cpp in Sources */, + F9D8624B1DC976E4000A199A /* LaunchCachePrinter.cpp in Sources */, + F9D862481DC975B3000A199A /* LaunchCacheReader.cpp in Sources */, + F9E5E2C61EB00A9F0013A0BB /* ClosureBuffer.cpp in Sources */, + F9ABA06E1E289B72000F21B4 /* closuredProtocol.defs in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F97FF3521C23638F000ACDD2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F97FF3611C23640C000ACDD2 /* nocr.c in Sources */, + F988F0BB1E2FDF5B003AED79 /* execserver.defs in Sources */, + ); + 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; + }; + F9AB02B41F329FAA00EE96C4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9AB02CC1F32A33C00EE96C4 /* FileUtils.cpp in Sources */, + F9AB02CB1F32A26700EE96C4 /* PathOverrides.cpp in Sources */, + F9AB02CA1F32A25F00EE96C4 /* DyldCacheParser.cpp in Sources */, + F9AB02C91F32A24B00EE96C4 /* ClosureBuffer.cpp in Sources */, + F9AB02C81F32A23B00EE96C4 /* MachOParser.cpp in Sources */, + F9AB02C71F32A22B00EE96C4 /* LaunchCacheReader.cpp in Sources */, + F9AB02C61F32A1F400EE96C4 /* Diagnostics.cpp in Sources */, + F9AB02C41F329FF400EE96C4 /* DyldSharedCache.cpp in Sources */, + F9AB02C31F329FE000EE96C4 /* ImageProxy.cpp in Sources */, + F9AB02C51F329FFE00EE96C4 /* LaunchCacheWriter.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9D1000F14D8D0BA00099D91 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9D1001814D8D13D00099D91 /* dsc_extractor.cpp in Sources */, + F9D1001D14D8D19500099D91 /* dsc_iterator.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9DDEDAE1E2878CA00A753DC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9DDEDB91E2878EC00A753DC /* closured.cpp in Sources */, + F9DDEDBA1E2878F100A753DC /* closuredProtocol.defs in Sources */, + F9DDEDBB1E287C9500A753DC /* DyldSharedCache.cpp in Sources */, + F9DDEDBC1E287CA100A753DC /* Diagnostics.cpp in Sources */, + F9DDEDC01E287CF600A753DC /* LaunchCacheReader.cpp in Sources */, + F9DDEDBD1E287CB100A753DC /* LaunchCacheWriter.cpp in Sources */, + F9DDEDC21E287D8A00A753DC /* PathOverrides.cpp in Sources */, + F9DDEDC11E287D3C00A753DC /* MachOParser.cpp in Sources */, + F981C8C01EEF7D4100452F35 /* DyldCacheParser.cpp in Sources */, + F9DDEDBE1E287CF600A753DC /* FileUtils.cpp in Sources */, + F9DDEDBF1E287CF600A753DC /* ImageProxy.cpp in Sources */, + F9B3CAEA1EE20FE200C9A48B /* ClosureBuffer.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9ED4C950630A76000DF4E74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 37D7DB001E96F0ED00D52CEA /* Tracing.cpp in Sources */, + F9ED4CDF0630A7F100DF4E74 /* dyldStartup.s in Sources */, + F9ED4CDB0630A7F100DF4E74 /* dyldInitialization.cpp in Sources */, + F9ED4CD70630A7F100DF4E74 /* dyld.cpp in Sources */, + F9ED4CD90630A7F100DF4E74 /* dyldAPIs.cpp in Sources */, + F9ED4CDA0630A7F100DF4E74 /* dyldExceptions.c in Sources */, + F9ED4CD60630A7F100DF4E74 /* dyld_gdb.cpp in Sources */, + F9ED4CE00630A7F100DF4E74 /* glue.c in Sources */, + F9280B7B1AB9DCA000B18AEC /* ImageLoaderMegaDylib.cpp in Sources */, + F9ED4CE10630A7F100DF4E74 /* ImageLoader.cpp in Sources */, + F9ED4CE30630A7F100DF4E74 /* ImageLoaderMachO.cpp in Sources */, + F9ED4CE50630A7F100DF4E74 /* stub_binding_helper.s in Sources */, + F9ED4CDE0630A7F100DF4E74 /* dyldNew.cpp in Sources */, + F94DB9040F0A9B1700323715 /* ImageLoaderMachOClassic.cpp in Sources */, + F94DB9050F0A9B1700323715 /* ImageLoaderMachOCompressed.cpp in Sources */, + F9C2755B1DA73EA1007A5D8A /* Loading.cpp in Sources */, + F9C275571DA5D67F007A5D8A /* MachOParser.cpp in Sources */, + F922AE5A1EF0DC7200926F9D /* DyldCacheParser.cpp in Sources */, + F9D8624F1DCBD318000A199A /* Diagnostics.cpp in Sources */, + F9D862501DCBD31D000A199A /* LaunchCacheReader.cpp in Sources */, + F9C73AC21E2992730025C89E /* closuredProtocol.defs in Sources */, + F977DDCB1E53BF5500609230 /* SharedCacheRuntime.cpp in Sources */, + F9D862511DCBD330000A199A /* DyldSharedCache.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9ED4C9C0630A76B00DF4E74 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9D49CCC1458B95200F86ADD /* start_glue.s in Sources */, + 37D7DB011E96F3EB00D52CEA /* Tracing.cpp in Sources */, + F9F256360639DBCC00A7427D /* dyldLock.cpp in Sources */, + F9BA514B0ECE4F4200D1D62E /* dyld_stub_binder.s in Sources */, + F9A221E70F3A6D7C00D15F73 /* dyldLibSystemGlue.c in Sources */, + F913FADA0630A8AE00B7AE9D /* dyldAPIsInLibSystem.cpp in Sources */, + F9A6D6E4116F9DF20051CC16 /* threadLocalVariables.c in Sources */, + F9A6D70C116FBBD10051CC16 /* threadLocalHelpers.s in Sources */, + F95090E51C5AD1E80031F81D /* dyld_process_info.cpp in Sources */, + F958D4771C7FCE6700A0B199 /* dyld_process_info_notify.cpp in Sources */, + F96D19BF1D94A6DC007AF3CE /* MachOParser.cpp in Sources */, + F922AE591EF0DBA500926F9D /* DyldCacheParser.cpp in Sources */, + F9D8624E1DCBD06A000A199A /* Diagnostics.cpp in Sources */, + F92015711DE3F3B000816A4A /* DyldSharedCache.cpp in Sources */, + F96D19C01D94BFCE007AF3CE /* AllImages.cpp in Sources */, + F96D19A81D93661A007AF3CE /* APIs.cpp in Sources */, + F9C15A4A1E1F7DAC0006E570 /* APIs_macOS.cpp in Sources */, + F97C61A21D9CAE3500A84CD7 /* Logging.cpp in Sources */, + F9C2755A1DA71CE8007A5D8A /* Loading.cpp in Sources */, + F90108611E2AD96000870568 /* PathOverrides.cpp in Sources */, + F9D862431DC90A4F000A199A /* LaunchCacheReader.cpp in Sources */, + F9E5E2C71EB00AAA0013A0BB /* ClosureBuffer.cpp in Sources */, + F9C86B651E2B16C600FD8669 /* closuredProtocol.defs in Sources */, + F97C619F1D9829AA00A84CD7 /* libdyldEntryVector.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9F2A5560F7AEE9800B7C9EB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9F2A5700F7AEEE300B7C9EB /* dsc_iterator.cpp in Sources */, + F9CE307A1208F1B50098B590 /* dsc_extractor.cpp in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 37A0AD0F1C16000F00731E50 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 37A0AD0A1C15FFF500731E50 /* update_dyld_shared_cache */; + targetProxy = 37A0AD0E1C16000F00731E50 /* PBXContainerItemProxy */; + }; + D8668AD01ECE335F005E7D31 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */; + targetProxy = D8668ACF1ECE335F005E7D31 /* PBXContainerItemProxy */; + }; + F908134811D3ED1A00626CC1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */; + targetProxy = F908134711D3ED1A00626CC1 /* PBXContainerItemProxy */; + }; + F922C8121F33B62700D8F479 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9AB02B71F329FAA00EE96C4 /* libclosured */; + targetProxy = F922C8111F33B62700D8F479 /* PBXContainerItemProxy */; + }; + F922C81E1F33B96300D8F479 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F922C8161F33B73800D8F479 /* libclosured-stub */; + targetProxy = F922C81D1F33B96300D8F479 /* PBXContainerItemProxy */; + }; + F94182D81E60F0BE00D8EF25 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; + targetProxy = F94182D71E60F0BE00D8EF25 /* PBXContainerItemProxy */; + }; + F94182DA1E60F0C000D8EF25 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9F2A5580F7AEE9800B7C9EB /* libdsc */; + targetProxy = F94182D91E60F0C000D8EF25 /* PBXContainerItemProxy */; + }; + F94182DC1E60F16900D8EF25 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9D1001114D8D0BA00099D91 /* dsc_extractor */; + targetProxy = F94182DB1E60F16900D8EF25 /* PBXContainerItemProxy */; + }; + F96543A11E343601003C5540 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F97C61A61DBAD1A900A84CD7 /* dyld_closure_util */; + targetProxy = F96543A01E343601003C5540 /* PBXContainerItemProxy */; + }; + F97FF3661C237F97000ACDD2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F97FF3551C23638F000ACDD2 /* nocr */; + targetProxy = F97FF3651C237F97000ACDD2 /* PBXContainerItemProxy */; + }; + F99B8EB20FEC220C00701838 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F99B8E550FEC10F600701838 /* dyld_shared_cache_util */; + targetProxy = F99B8EB10FEC220C00701838 /* PBXContainerItemProxy */; + }; + F9B4D78012AD9736000605A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9F2A5580F7AEE9800B7C9EB /* libdsc */; + targetProxy = F9B4D77F12AD9736000605A6 /* PBXContainerItemProxy */; + }; + F9ED4CA70630A78A00DF4E74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9ED4C970630A76000DF4E74 /* dyld */; + targetProxy = F9ED4CA60630A78A00DF4E74 /* PBXContainerItemProxy */; + }; + F9ED4CA90630A78A00DF4E74 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9ED4C9E0630A76B00DF4E74 /* libdyld.dylib */; + targetProxy = F9ED4CA80630A78A00DF4E74 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 3703A1221B38C1B300ADBA7F /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "BUILDING_CACHE_BUILDER=1", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = macosx; + TOOLCHAINS = default; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "x86_64 x86_64h"; + }; + name = Debug; + }; + 3703A1231B38C1B300ADBA7F /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(APPLE_INTERNAL_LIBRARY_DIR)/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = macosx; + TOOLCHAINS = default; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "x86_64 x86_64h"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 377686001AC4B27D00026E6C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)/AppleInternal/Library/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + "BUILDING_CACHE_BUILDER=1", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = multi_dyld_shared_cache_builder; + SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = NO; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "x86_64 x86_64h"; + }; + name = Debug; + }; + 377686011AC4B27D00026E6C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)/AppleInternal/Library/Frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CACHE_BUILDER=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CFLAGS = "-DBOM_SUPPORT=1"; + PRODUCT_NAME = multi_dyld_shared_cache_builder; + SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = NO; + USER_HEADER_SEARCH_PATHS = "../launch-cache/"; + VALID_ARCHS = "x86_64 x86_64h"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 37A0AD0C1C15FFF500731E50 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 37A0AD0D1C15FFF500731E50 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 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"; + INSTALLHDRS_COPY_PHASE = YES; + PRODUCT_NAME = libdyld; + ZERO_LINK = NO; + }; + name = Release; + }; + F922C8191F33B73800D8F479 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_TESTABILITY = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + LD_DYLIB_INSTALL_NAME = /usr/lib/closure/libclosured.dylib; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = "-nostdlib"; + PRODUCT_NAME = closured; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + F922C81A1F33B73800D8F479 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_NS_ASSERTIONS = NO; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + LD_DYLIB_INSTALL_NAME = /usr/lib/closure/libclosured.dylib; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "-nostdlib"; + PRODUCT_NAME = closured; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; + F93937350A94FB2900070A07 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/include", + "$(SRCROOT)/dyld3", + "$(SRCROOT)/dyld3/shared-cache", + ); + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = "-stdlib=libc++"; + PRODUCT_NAME = update_dyld_shared_cache; + SDKROOT = macosx.internal; + USE_HEADERMAP = NO; + VALID_ARCHS = x86_64; + }; + name = Debug; + }; + F93937360A94FB2900070A07 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD161A4A0E0700BBDD52 /* update_dyld_shared_cache.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/include", + "$(SRCROOT)/dyld3", + "$(SRCROOT)/dyld3/shared-cache", + ); + INSTALL_PATH = /usr/bin; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = "-stdlib=libc++"; + PRODUCT_NAME = update_dyld_shared_cache; + SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + USE_HEADERMAP = NO; + VALID_ARCHS = x86_64; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + F96354431DCD74A400895049 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; + GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/include", + "$(SRCROOT)/dyld3", + "$(SRCROOT)/dyld3/shared-cache", + ); + INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = "-stdlib=libc++"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)"; + USE_HEADERMAP = NO; + VALID_ARCHS = x86_64; + }; + name = Debug; + }; + F96354441DCD74A400895049 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F94182DE1E60FFDC00D8EF25 /* update_dyld_sim_shared_cache.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_THREADSAFE_STATICS = NO; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES; + GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/include", + "$(SRCROOT)/dyld3", + "$(SRCROOT)/dyld3/shared-cache", + ); + INSTALL_PATH = "$(DEVICE_PLATFORM_INSTALL_DIR)/Developer/Library/CoreSimulator/Profiles/Runtimes/$(SIMULATOR_DIR_NAME).simruntime/Contents/Resources"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)"; + OTHER_LDFLAGS = "-stdlib=libc++"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + SIMULATOR_DIR_NAME = "$(PLATFORM_FAMILY_NAME_$(DEVICE_PLATFORM_NAME)) $(IPHONE_SDK_MARKETING_VERSION)"; + STRIP_INSTALLED_PRODUCT = YES; + STRIP_STYLE = debugging; + USE_HEADERMAP = NO; + VALID_ARCHS = x86_64; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + F97C61AC1DBAD1A900A84CD7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 59GAB85EFG; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Debug; + }; + F97C61AD1DBAD1A900A84CD7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 59GAB85EFG; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Release; + }; + F97FF35A1C23638F000ACDD2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DERIVED_SOURCES_DIR)/**", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + }; + name = Debug; + }; + F97FF35B1C23638F000ACDD2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(DERIVED_SOURCES_DIR)/**", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + F99B8E580FEC10F600701838 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.10.xctoolchain/usr/include, + "$(SRCROOT)/interlinked-dylibs/", + ); + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + PRODUCT_NAME = dyld_shared_cache_util; + SDKROOT = macosx.internal; + SUPPORTED_PLATFORMS = macosx; + }; + name = Debug; + }; + F99B8E590FEC10F600701838 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/OSX10.10.xctoolchain/usr/include, + "$(SRCROOT)/interlinked-dylibs/", + ); + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/bin"; + PRODUCT_NAME = dyld_shared_cache_util; + SDKROOT = macosx.internal; + SKIP_INSTALL = NO; + SUPPORTED_PLATFORMS = macosx; + }; + name = Release; + }; + F9AB02C01F329FAA00EE96C4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_TESTABILITY = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_ENABLE_CPP_RTTI = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DYLD_IN_PROCESS=0", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INSTALL_PATH = /usr/lib/closure; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ( + "-Wl,-exported_symbol,__ZN5dyld325closured_CreateImageGroupERKNS_13ClosureBufferE", + "-Wl,-no_inits", + ); + PRODUCT_NAME = closured; + SDKROOT = macosx.internal; + }; + name = Debug; + }; + F9AB02C11F329FAA00EE96C4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + ENABLE_NS_ASSERTIONS = NO; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_ENABLE_CPP_EXCEPTIONS = YES; + GCC_ENABLE_CPP_RTTI = YES; + GCC_PREPROCESSOR_DEFINITIONS = "DYLD_IN_PROCESS=0"; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + INSTALL_PATH = /usr/lib/closure; + MACOSX_DEPLOYMENT_TARGET = 10.12; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ( + "-Wl,-exported_symbol,__ZN5dyld325closured_CreateImageGroupERKNS_13ClosureBufferE", + "-Wl,-no_inits", + ); + PRODUCT_NAME = closured; + SDKROOT = macosx.internal; + }; + name = Release; + }; + F9D1001314D8D0BB00099D91 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = ""; + DYLIB_CURRENT_VERSION = ""; + EXECUTABLE_EXTENSION = bundle; + GCC_DYNAMIC_NO_PIC = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/lib"; + MACH_O_TYPE = mh_bundle; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress", + ); + PRODUCT_NAME = dsc_extractor; + }; + name = Debug; + }; + F9D1001414D8D0BB00099D91 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = ""; + DYLIB_CURRENT_VERSION = ""; + EXECUTABLE_EXTENSION = bundle; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + INSTALL_PATH = "$(INSTALL_PATH_PREFIX)$(INSTALL_LOCATION)/usr/lib"; + MACH_O_TYPE = mh_bundle; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-stdlib=libc++", + "-Wl,-exported_symbol,_dyld_shared_cache_extract_dylibs_progress", + ); + PRODUCT_NAME = dsc_extractor; + ZERO_LINK = NO; + }; + name = Release; + }; + F9D8C7DE087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = "-"; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_BUILTIN_FUNCTIONS = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DYLD_VERSION=$(RC_ProjectSourceVersion)", + "_LIBCPP_NO_EXCEPTIONS=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO; + GCC_WARN_MISSING_PARENTHESES = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + HEADER_SEARCH_PATHS = ( + "$(DERIVED_FILE_DIR)/**", + ./include, + "./launch-cache", + ); + LD_GENERATE_MAP_FILE = YES; + OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "@$(DERIVED_SOURCES_DIR)/archives.txt", + "-nostdlib", + "-Wl,-dylinker", + "-Wl,-dylinker_install_name,/usr/lib/dyld", + "-stdlib=libc++", + "$(ALIGNMENT)", + "$(ENTRY)", + ); + STRIPFLAGS = "-S"; + UNSTRIPPED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + ); + }; + name = Debug; + }; + F9D8C7E0087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD141A4A0E0700BBDD52 /* dyld.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = "-"; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DYLD_VERSION=$(RC_ProjectSourceVersion)", + "_LIBCPP_NO_EXCEPTIONS=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_SHADOW = YES; + GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES; + HEADER_SEARCH_PATHS = ( + "$(DERIVED_FILE_DIR)/**", + ./include, + "./launch-cache", + ); + LD_GENERATE_MAP_FILE = YES; + ORDER_FILE = "$(SRCROOT)/src/dyld.order"; + OTHER_CFLAGS = ""; + "OTHER_CFLAGS[arch=armv6]" = "-mthumb"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "@$(DERIVED_SOURCES_DIR)/archives.txt", + "-nostdlib", + "-Wl,-dylinker", + "-Wl,-dylinker_install_name,/usr/lib/dyld", + "-stdlib=libc++", + "$(ALIGNMENT)", + "$(ENTRY)", + "-Wl,-no_data_const", + "-Wl,-section_order,__DATA,__all_image_info:__nl_symbol_ptr:__got:__const:__crash_info:__data:__bss:__common", + ); + STRIPFLAGS = "-S"; + UNSTRIPPED_PRODUCT = NO; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + F9D8C7E2087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 500; + DEAD_CODE_STRIPPING = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GENERATE_TEXT_BASED_STUBS = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALLHDRS_COPY_PHASE = YES; + INSTALLHDRS_SCRIPT_PHASE = YES; + ONLY_ACTIVE_ARCH = NO; + OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-Wglobal-constructors", + ); + OTHER_LDFLAGS = ( + "-Wl,-no_inits", + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + ); + OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 "; + PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o"; + PRODUCT_NAME = dyld; + PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o"; + SKIP_INSTALL = NO; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Debug; + }; + F9D8C7E4087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD151A4A0E0700BBDD52 /* libdyld.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_WARN_EMPTY_BODY = YES; + CODE_SIGN_IDENTITY = "-"; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GENERATE_TEXT_BASED_STUBS = YES; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALLHDRS_COPY_PHASE = YES; + INSTALLHDRS_SCRIPT_PHASE = YES; + OTHER_CFLAGS = ""; + OTHER_CPLUSPLUSFLAGS = ( + "-fno-exceptions", + "$(OTHER_CFLAGS)", + ); + OTHER_LDFLAGS = ( + "-Wl,-no_inits", + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + "-Wl,-no_inits", + ); + "OTHER_LDFLAGS[sdk=iphoneos*]" = ( + "-Wl,-no_inits", + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + "-Wl,-dirty_data_list,$(SRCROOT)/src/libdyld_data_symbols.dirty", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ( + "-Wl,-no_inits", + "-nostdlib", + "$(LIBSYSTEM_LIBS)", + "-umbrella", + System, + "-L$(SDKROOT)/usr/lib/system", + ); + OTHER_TAPI_FLAGS = "-extra-public-header ./include/dlfcn.h -extra-private-header ./dyld3/libdyldEntryVector.h -ObjC++ -std=c++11 "; + PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include/mach-o"; + PRODUCT_NAME = dyld; + PUBLIC_HEADERS_FOLDER_PATH = "/usr//include/mach-o"; + SEPARATE_STRIP = YES; + SKIP_INSTALL = NO; + STRIP_INSTALLED_PRODUCT = YES; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_EXPORT_DECL = "__attribute__((visibility(\"default\")))"; + WARNING_CFLAGS = ( + "-Wmost", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + ); + }; + name = Release; + }; + F9D8C7E6087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = all; + }; + name = Debug; + }; + F9D8C7E8087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = all; + }; + name = Release; + }; + F9D8C7EA087B087300E93EFB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LIBRARY = "compiler-default"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) build DerivedData closure-tests"; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx.internal; + }; + name = Debug; + }; + F9D8C7EC087B087300E93EFB /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F971DD131A4A0E0700BBDD52 /* base.xcconfig */; + buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LIBRARY = "compiler-default"; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; + EXCLUDED_INSTALLSRC_SUBDIRECTORY_PATTERNS = "$(inherited) build DerivedData closure-tests"; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + SDKROOT = macosx.internal; + }; + name = Release; + }; + F9DDEDB71E2878CB00A753DC /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F9EDC0A01F0481B400B030F4 /* closured.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 59GAB85EFG; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "BUILDING_CLOSURED=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALL_PATH = /usr/libexec/; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = YES; + PLIST_FILE_OUTPUT_FORMAT = binary; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIPFLAGS = "-S"; + VERSIONING_SYSTEM = ""; + }; + name = Debug; + }; + F9DDEDB81E2878CB00A753DC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F9EDC0A01F0481B400B030F4 /* closured.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 59GAB85EFG; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = "BUILDING_CLOSURED=1"; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + HEADER_SEARCH_PATHS = "$(SRCROOT)/include"; + INSTALL_PATH = /usr/libexec/; + MACOSX_DEPLOYMENT_TARGET = 10.13; + MTL_ENABLE_DEBUG_INFO = NO; + PLIST_FILE_OUTPUT_FORMAT = binary; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIPFLAGS = "-S"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + F9F2A55A0F7AEE9900B7C9EB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_INLINES_ARE_PRIVATE_EXTERN = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + PRODUCT_NAME = dsc; + }; + name = Debug; + }; + F9F2A55B0F7AEE9900B7C9EB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = 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 = NO; + GCC_WARN_SHADOW = NO; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALLHDRS_COPY_PHASE = YES; + INSTALL_PATH = "$(INSTALL_LOCATION)/usr/local/lib"; + OTHER_CPLUSPLUSFLAGS = ( + "-stdlib=libc++", + "$(OTHER_CFLAGS)", + ); + PRODUCT_NAME = dsc; + ZERO_LINK = NO; + }; + name = Release; + }; + F9F6F4281C1FB0A700BD8FED /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + F9F6F4291C1FB0A700BD8FED /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3703A1211B38C1B300ADBA7F /* Build configuration list for PBXNativeTarget "dyld_shared_cache_builder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3703A1221B38C1B300ADBA7F /* Debug */, + 3703A1231B38C1B300ADBA7F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 377685FF1AC4B27D00026E6C /* Build configuration list for PBXNativeTarget "multi_dyld_shared_cache_builder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 377686001AC4B27D00026E6C /* Debug */, + 377686011AC4B27D00026E6C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 37A0AD0B1C15FFF500731E50 /* Build configuration list for PBXAggregateTarget "update_dyld_shared_cache" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 37A0AD0C1C15FFF500731E50 /* Debug */, + 37A0AD0D1C15FFF500731E50 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F908135211D3ED9000626CC1 /* Build configuration list for PBXAggregateTarget "libdyld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F908134311D3ED0C00626CC1 /* Debug */, + F908134411D3ED0C00626CC1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F922C8181F33B73800D8F479 /* Build configuration list for PBXNativeTarget "libclosured-stub" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F922C8191F33B73800D8F479 /* Debug */, + F922C81A1F33B73800D8F479 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93937340A94FB2900070A07 /* Build configuration list for PBXNativeTarget "update_dyld_shared_cache_tool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93937350A94FB2900070A07 /* Debug */, + F93937360A94FB2900070A07 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F96354421DCD74A400895049 /* Build configuration list for PBXNativeTarget "update_dyld_sim_shared_cache" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F96354431DCD74A400895049 /* Debug */, + F96354441DCD74A400895049 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F97C61AB1DBAD1A900A84CD7 /* Build configuration list for PBXNativeTarget "dyld_closure_util" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F97C61AC1DBAD1A900A84CD7 /* Debug */, + F97C61AD1DBAD1A900A84CD7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F97FF35C1C23638F000ACDD2 /* Build configuration list for PBXNativeTarget "nocr" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F97FF35A1C23638F000ACDD2 /* Debug */, + F97FF35B1C23638F000ACDD2 /* Release */, + ); + 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; + }; + F9AB02C21F329FAA00EE96C4 /* Build configuration list for PBXNativeTarget "libclosured" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9AB02C01F329FAA00EE96C4 /* Debug */, + F9AB02C11F329FAA00EE96C4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D1001714D8D0F100099D91 /* Build configuration list for PBXNativeTarget "dsc_extractor" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D1001314D8D0BB00099D91 /* Debug */, + F9D1001414D8D0BB00099D91 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7DD087B087300E93EFB /* Build configuration list for PBXNativeTarget "dyld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7DE087B087300E93EFB /* Debug */, + F9D8C7E0087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7E1087B087300E93EFB /* Build configuration list for PBXNativeTarget "libdyld.dylib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7E2087B087300E93EFB /* Debug */, + F9D8C7E4087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7E5087B087300E93EFB /* Build configuration list for PBXAggregateTarget "all" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7E6087B087300E93EFB /* Debug */, + F9D8C7E8087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9D8C7E9087B087300E93EFB /* Build configuration list for PBXProject "dyld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9D8C7EA087B087300E93EFB /* Debug */, + F9D8C7EC087B087300E93EFB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9DDEDB61E2878CB00A753DC /* Build configuration list for PBXNativeTarget "closured" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9DDEDB71E2878CB00A753DC /* Debug */, + F9DDEDB81E2878CB00A753DC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9F2A56B0F7AEEB100B7C9EB /* Build configuration list for PBXNativeTarget "libdsc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9F2A55A0F7AEE9900B7C9EB /* Debug */, + F9F2A55B0F7AEE9900B7C9EB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9F6F42A1C1FB0A700BD8FED /* Build configuration list for PBXAggregateTarget "dyld_tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9F6F4281C1FB0A700BD8FED /* Debug */, + F9F6F4291C1FB0A700BD8FED /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F9ED4C8B0630A72300DF4E74 /* Project object */; +} diff --git a/dyld/dyld3/APIs.cpp b/dyld/dyld3/APIs.cpp new file mode 100644 index 0000000..360a1cd --- /dev/null +++ b/dyld/dyld3/APIs.cpp @@ -0,0 +1,1474 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include <_simple.h> +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dlfcn.h" +#include "dyld_priv.h" + +#include "AllImages.h" +#include "MachOParser.h" +#include "Loading.h" +#include "Logging.h" +#include "Diagnostics.h" +#include "DyldSharedCache.h" +#include "PathOverrides.h" +#include "APIs.h" +#include "StringUtils.h" + + + +extern "C" { + #include "closuredProtocol.h" +} + + +namespace dyld { + extern dyld_all_image_infos dyld_all_image_infos; +} + + +namespace dyld3 { + + +uint32_t _dyld_image_count(void) +{ + log_apis("_dyld_image_count()\n"); + + return gAllImages.count(); +} + +const mach_header* _dyld_get_image_header(uint32_t imageIndex) +{ + log_apis("_dyld_get_image_header(%d)\n", imageIndex); + + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress); + if ( image.valid() ) + return loadAddress; + return nullptr; +} + +intptr_t _dyld_get_image_slide(const mach_header* mh) +{ + log_apis("_dyld_get_image_slide(%p)\n", mh); + + MachOParser parser(mh); + return parser.getSlide(); +} + +intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex) +{ + log_apis("_dyld_get_image_vmaddr_slide(%d)\n", imageIndex); + + const mach_header* mh = _dyld_get_image_header(imageIndex); + if ( mh != nullptr ) + return dyld3::_dyld_get_image_slide(mh); + return 0; +} + +const char* _dyld_get_image_name(uint32_t imageIndex) +{ + log_apis("_dyld_get_image_name(%d)\n", imageIndex); + + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByLoadOrder(imageIndex, &loadAddress); + if ( image.valid() ) + return gAllImages.imagePath(image.binaryData()); + return nullptr; +} + + +static bool nameMatch(const char* installName, const char* libraryName) +{ + const char* leafName = strrchr(installName, '/'); + if ( leafName == NULL ) + leafName = installName; + else + leafName++; + + // -framework case is exact match of leaf name + if ( strcmp(leafName, libraryName) == 0 ) + return true; + + // -lxxx case: leafName must match "lib" ["." ?] ".dylib" + size_t leafNameLen = strlen(leafName); + size_t libraryNameLen = strlen(libraryName); + if ( leafNameLen < (libraryNameLen+9) ) + return false; + if ( strncmp(leafName, "lib", 3) != 0 ) + return false; + if ( strcmp(&leafName[leafNameLen-6], ".dylib") != 0 ) + return false; + if ( strncmp(&leafName[3], libraryName, libraryNameLen) != 0 ) + return false; + return (leafName[libraryNameLen+3] == '.'); +} + + +// +// BETTER, USE: dyld_get_program_sdk_version() +// +// Scans the main executable and returns the version of the specified dylib the program was built against. +// +// The library to find is the leaf name that would have been passed to linker tool +// (e.g. -lfoo or -framework foo would use "foo"). +// +// Returns -1 if the main executable did not link against the specified library, or is malformed. +// +int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) +{ + log_apis("NSVersionOfLinkTimeLibrary(\"%s\")\n", libraryName); + + __block int32_t result = -1; + MachOParser parser(gAllImages.mainExecutable()); + parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) { + if ( nameMatch(loadPath, libraryName) ) + result = currentVersion; + }); + log_apis(" NSVersionOfLinkTimeLibrary() => 0x%08X\n", result); + return result; +} + + +// +// Searches loaded images for the requested dylib and returns its current version. +// +// The library to find is the leaf name that would have been passed to linker tool +// (e.g. -lfoo or -framework foo would use "foo"). +// +// If the specified library is not loaded, -1 is returned. +// +int32_t NSVersionOfRunTimeLibrary(const char* libraryName) +{ + log_apis("NSVersionOfRunTimeLibrary(\"%s\")\n", libraryName); + + uint32_t count = gAllImages.count(); + for (uint32_t i=0; i < count; ++i) { + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByLoadOrder(i, &loadAddress); + if ( image.valid() ) { + MachOParser parser(loadAddress); + const char* installName; + uint32_t currentVersion; + uint32_t compatVersion; + if ( parser.getDylibInstallName(&installName, &compatVersion, ¤tVersion) && nameMatch(installName, libraryName) ) { + log_apis(" NSVersionOfRunTimeLibrary() => 0x%08X\n", currentVersion); + return currentVersion; + } + } + } + log_apis(" NSVersionOfRunTimeLibrary() => -1\n"); + return -1; +} + + +#if __WATCH_OS_VERSION_MIN_REQUIRED + +static uint32_t watchVersToIOSVers(uint32_t vers) +{ + return vers + 0x00070000; +} + +uint32_t dyld_get_program_sdk_watch_os_version() +{ + log_apis("dyld_get_program_sdk_watch_os_version()\n"); + + Platform platform; + uint32_t minOS; + uint32_t sdk; + + MachOParser parser(gAllImages.mainExecutable()); + if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) { + if ( platform == Platform::watchOS ) + return sdk; + } + return 0; +} + +uint32_t dyld_get_program_min_watch_os_version() +{ + log_apis("dyld_get_program_min_watch_os_version()\n"); + + Platform platform; + uint32_t minOS; + uint32_t sdk; + + MachOParser parser(gAllImages.mainExecutable()); + if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) { + if ( platform == Platform::watchOS ) + return minOS; // return raw minOS (not mapped to iOS version) + } + return 0; +} +#endif + + +#if TARGET_OS_BRIDGE + +static uint32_t bridgeVersToIOSVers(uint32_t vers) +{ + return vers + 0x00090000; +} + +uint32_t dyld_get_program_sdk_bridge_os_version() +{ + log_apis("dyld_get_program_sdk_bridge_os_version()\n"); + + Platform platform; + uint32_t minOS; + uint32_t sdk; + + MachOParser parser(gAllImages.mainExecutable()); + if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) { + if ( platform == Platform::bridgeOS ) + return sdk; + } + return 0; +} + +uint32_t dyld_get_program_min_bridge_os_version() +{ + log_apis("dyld_get_program_min_bridge_os_version()\n"); + + Platform platform; + uint32_t minOS; + uint32_t sdk; + + MachOParser parser(gAllImages.mainExecutable()); + if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) { + if ( platform == Platform::bridgeOS ) + return minOS; // return raw minOS (not mapped to iOS version) + } + return 0; +} + +#endif + + +#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED && !TARGET_OS_BRIDGE + +#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff)) + +static uint32_t deriveSDKVersFromDylibs(const mach_header* mh) +{ + __block uint32_t foundationVers = 0; + __block uint32_t libSystemVers = 0; + MachOParser parser(mh); + parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t compatVersion, uint32_t currentVersion, bool& stop) { + if ( strcmp(loadPath, "/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation") == 0 ) + foundationVers = currentVersion; + else if ( strcmp(loadPath, "/usr/lib/libSystem.B.dylib") == 0 ) + libSystemVers = currentVersion; + }); + + struct DylibToOSMapping { + uint32_t dylibVersion; + uint32_t osVersion; + }; + + #if __IPHONE_OS_VERSION_MIN_REQUIRED + static const DylibToOSMapping foundationMapping[] = { + { PACKED_VERSION(678,24,0), 0x00020000 }, + { PACKED_VERSION(678,26,0), 0x00020100 }, + { PACKED_VERSION(678,29,0), 0x00020200 }, + { PACKED_VERSION(678,47,0), 0x00030000 }, + { PACKED_VERSION(678,51,0), 0x00030100 }, + { PACKED_VERSION(678,60,0), 0x00030200 }, + { PACKED_VERSION(751,32,0), 0x00040000 }, + { PACKED_VERSION(751,37,0), 0x00040100 }, + { PACKED_VERSION(751,49,0), 0x00040200 }, + { PACKED_VERSION(751,58,0), 0x00040300 }, + { PACKED_VERSION(881,0,0), 0x00050000 }, + { PACKED_VERSION(890,1,0), 0x00050100 }, + { PACKED_VERSION(992,0,0), 0x00060000 }, + { PACKED_VERSION(993,0,0), 0x00060100 }, + { PACKED_VERSION(1038,14,0),0x00070000 }, + { PACKED_VERSION(0,0,0), 0x00070000 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. + }; + + if ( foundationVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=foundationMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( foundationVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } + } + + #else + // Note: versions are for the GM release. The last entry should + // always be zero. At the start of the next major version, + // a new last entry needs to be added and the previous zero + // updated to the GM dylib version. + static const DylibToOSMapping libSystemMapping[] = { + { PACKED_VERSION(88,1,3), 0x000A0400 }, + { PACKED_VERSION(111,0,0), 0x000A0500 }, + { PACKED_VERSION(123,0,0), 0x000A0600 }, + { PACKED_VERSION(159,0,0), 0x000A0700 }, + { PACKED_VERSION(169,3,0), 0x000A0800 }, + { PACKED_VERSION(1197,0,0), 0x000A0900 }, + { PACKED_VERSION(0,0,0), 0x000A0900 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. + }; + + if ( libSystemVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=libSystemMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( libSystemVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } + } + #endif + return 0; +} +#endif + + +// +// Returns the sdk version (encode as nibble XXXX.YY.ZZ) that the +// specified binary was built against. +// +// First looks for LC_VERSION_MIN_* in binary and if sdk field is +// not zero, return that value. +// Otherwise, looks for the libSystem.B.dylib the binary linked +// against and uses a table to convert that to an sdk version. +// +uint32_t dyld_get_sdk_version(const mach_header* mh) +{ + log_apis("dyld_get_sdk_version(%p)\n", mh); + + Platform platform; + uint32_t minOS; + uint32_t sdk; + + if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) ) + return 0; + MachOParser parser(mh); + if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) { + switch (platform) { +#if TARGET_OS_BRIDGE + case Platform::bridgeOS: + // new binary. sdk version looks like "2.0" but API wants "11.0" + return bridgeVersToIOSVers(sdk); + case Platform::iOS: + // old binary. sdk matches API semantics so can return directly. + return sdk; +#elif __WATCH_OS_VERSION_MIN_REQUIRED + case Platform::watchOS: + // new binary. sdk version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(sdk); + case Platform::iOS: + // old binary. sdk matches API semantics so can return directly. + return sdk; +#elif __TV_OS_VERSION_MIN_REQUIRED + case Platform::tvOS: + case Platform::iOS: + return sdk; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + case Platform::iOS: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#else + case Platform::macOS: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#endif + default: + // wrong binary for this platform + break; + } + } + +#if __WATCH_OS_VERSION_MIN_REQUIRED ||__TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE + // All watchOS and tvOS binaries should have version load command. + return 0; +#else + // MacOSX and iOS have old binaries without version load commmand. + return deriveSDKVersFromDylibs(mh); +#endif +} + +uint32_t dyld_get_program_sdk_version() +{ + log_apis("dyld_get_program_sdk_version()\n"); + + return dyld3::dyld_get_sdk_version(gAllImages.mainExecutable()); +} + +uint32_t dyld_get_min_os_version(const mach_header* mh) +{ + log_apis("dyld_get_min_os_version(%p)\n", mh); + + Platform platform; + uint32_t minOS; + uint32_t sdk; + + if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) ) + return 0; + MachOParser parser(mh); + if ( parser.getPlatformAndVersion(&platform, &minOS, &sdk) ) { + switch (platform) { +#if TARGET_OS_BRIDGE + case Platform::bridgeOS: + // new binary. sdk version looks like "2.0" but API wants "11.0" + return bridgeVersToIOSVers(minOS); + case Platform::iOS: + // old binary. sdk matches API semantics so can return directly. + return minOS; +#elif __WATCH_OS_VERSION_MIN_REQUIRED + case Platform::watchOS: + // new binary. OS version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(minOS); + case Platform::iOS: + // old binary. OS matches API semantics so can return directly. + return minOS; +#elif __TV_OS_VERSION_MIN_REQUIRED + case Platform::tvOS: + case Platform::iOS: + return minOS; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + case Platform::iOS: + return minOS; +#else + case Platform::macOS: + return minOS; +#endif + default: + // wrong binary for this platform + break; + } + } + return 0; +} + + +uint32_t dyld_get_program_min_os_version() +{ + log_apis("dyld_get_program_min_os_version()\n"); + + return dyld3::dyld_get_min_os_version(gAllImages.mainExecutable()); +} + + +bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid) +{ + log_apis("_dyld_get_image_uuid(%p, %p)\n", mh, uuid); + + if ( !MachOParser::wellFormedMachHeaderAndLoadCommands(mh) ) + return false; + MachOParser parser(mh); + return parser.getUuid(uuid); +} + +// +// _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, +// and *bufsize is left unchanged. It returns -1 if the buffer is not large enough, and *bufsize is set +// to the size required. +// +int _NSGetExecutablePath(char* buf, uint32_t* bufsize) +{ + log_apis("_NSGetExecutablePath(%p, %p)\n", buf, bufsize); + + launch_cache::Image image = gAllImages.mainExecutableImage(); + if ( image.valid() ) { + const char* path = gAllImages.imagePath(image.binaryData()); + size_t pathSize = strlen(path) + 1; + if ( *bufsize >= pathSize ) { + strcpy(buf, path); + return 0; + } + *bufsize = (uint32_t)pathSize; + } + return -1; +} + +void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) +{ + log_apis("_dyld_register_func_for_add_image(%p)\n", func); + + gAllImages.addLoadNotifier(func); +} + +void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) +{ + log_apis("_dyld_register_func_for_remove_image(%p)\n", func); + + gAllImages.addUnloadNotifier(func); +} + +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped) +{ + log_apis("_dyld_objc_notify_register(%p, %p, %p)\n", mapped, init, unmapped); + + gAllImages.setObjCNotifiers(mapped, init, unmapped); +} + + +const mach_header* dyld_image_header_containing_address(const void* addr) +{ + log_apis("dyld_image_header_containing_address(%p)\n", addr); + + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress); + if ( image.valid() ) + return loadAddress; + return nullptr; +} + + +const char* dyld_image_path_containing_address(const void* addr) +{ + log_apis("dyld_image_path_containing_address(%p)\n", addr); + + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress); + if ( image.valid() ) { + const char* path = gAllImages.imagePath(image.binaryData()); + log_apis(" dyld_image_path_containing_address() => %s\n", path); + return path; + } + log_apis(" dyld_image_path_containing_address() => NULL\n"); + return nullptr; +} + +bool _dyld_is_memory_immutable(const void* addr, size_t length) +{ + uintptr_t checkStart = (uintptr_t)addr; + uintptr_t checkEnd = checkStart + length; + + // quick check to see if in r/o region of shared cache. If so return true. + const DyldSharedCache* cache = (DyldSharedCache*)gAllImages.cacheLoadAddress(); + if ( cache != nullptr ) { + __block bool firstVMAddr = 0; + __block bool isReadOnlyInCache = false; + __block bool isInCache = false; + cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if ( firstVMAddr == 0 ) + firstVMAddr = vmAddr; + uintptr_t regionStart = (uintptr_t)cache + (uintptr_t)(vmAddr - firstVMAddr); + uintptr_t regionEnd = regionStart + (uintptr_t)size; + if ( (regionStart < checkStart) && (checkEnd < regionEnd) ) { + isInCache = true; + isReadOnlyInCache = ((permissions & VM_PROT_WRITE) != 0); + } + }); + if ( isInCache ) + return isReadOnlyInCache; + } + + // go slow route of looking at each image's segments + const mach_header* loadAddress; + uint8_t permissions; + launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress, &permissions); + if ( !image.valid() ) + return false; + if ( (permissions & VM_PROT_WRITE) != 0 ) + return false; + return !gAllImages.imageUnloadable(image, loadAddress); +} + + +int dladdr(const void* addr, Dl_info* info) +{ + log_apis("dladdr(%p, %p)\n", addr, info); + + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByOwnedAddress(addr, &loadAddress); + if ( !image.valid() ) { + log_apis(" dladdr() => 0\n"); + return 0; + } + MachOParser parser(loadAddress); + info->dli_fname = gAllImages.imagePath(image.binaryData()); + info->dli_fbase = (void*)(loadAddress); + if ( addr == info->dli_fbase ) { + // special case lookup of header + info->dli_sname = "__dso_handle"; + info->dli_saddr = info->dli_fbase; + } + else if ( parser.findClosestSymbol(addr, &(info->dli_sname), (const void**)&(info->dli_saddr)) ) { + // never return the mach_header symbol + if ( info->dli_saddr == info->dli_fbase ) { + info->dli_sname = nullptr; + info->dli_saddr = nullptr; + } + // strip off leading underscore + else if ( (info->dli_sname != nullptr) && (info->dli_sname[0] == '_') ) { + info->dli_sname = info->dli_sname + 1; + } + } + else { + info->dli_sname = nullptr; + info->dli_saddr = nullptr; + } + log_apis(" dladdr() => 1, { \"%s\", %p, \"%s\", %p }\n", info->dli_fname, info->dli_fbase, info->dli_sname, info->dli_saddr); + return 1; +} + + +struct PerThreadErrorMessage +{ + size_t sizeAllocated; + bool valid; + char message[1]; +}; + +static pthread_key_t dlerror_perThreadKey() +{ + static dispatch_once_t onceToken; + static pthread_key_t dlerrorPThreadKey; + dispatch_once(&onceToken, ^{ + pthread_key_create(&dlerrorPThreadKey, &free); + }); + return dlerrorPThreadKey; +} + +static void clearErrorString() +{ + PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey()); + if ( errorBuffer != nullptr ) + errorBuffer->valid = false; +} + +__attribute__((format(printf, 1, 2))) +static void setErrorString(const char* format, ...) +{ + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf != nullptr ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + size_t strLen = strlen(_simple_string(buf)) + 1; + size_t sizeNeeded = sizeof(PerThreadErrorMessage) + strLen; + PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey()); + if ( errorBuffer != nullptr ) { + if ( errorBuffer->sizeAllocated < sizeNeeded ) { + free(errorBuffer); + errorBuffer = nullptr; + } + } + if ( errorBuffer == nullptr ) { + size_t allocSize = std::max(sizeNeeded, (size_t)256); + PerThreadErrorMessage* p = (PerThreadErrorMessage*)malloc(allocSize); + p->sizeAllocated = allocSize; + p->valid = false; + pthread_setspecific(dlerror_perThreadKey(), p); + errorBuffer = p; + } + strcpy(errorBuffer->message, _simple_string(buf)); + errorBuffer->valid = true; + _simple_sfree(buf); + } +} + +char* dlerror() +{ + log_apis("dlerror()\n"); + + PerThreadErrorMessage* errorBuffer = (PerThreadErrorMessage*)pthread_getspecific(dlerror_perThreadKey()); + if ( errorBuffer != nullptr ) { + if ( errorBuffer->valid ) { + // you can only call dlerror() once, then the message is cleared + errorBuffer->valid = false; + return errorBuffer->message; + } + } + return nullptr; +} + +#if __arm64__ + #define CURRENT_CPU_TYPE CPU_TYPE_ARM64 +#elif __arm__ + #define CURRENT_CPU_TYPE CPU_TYPE_ARM +#endif + + +class VIS_HIDDEN RecursiveAutoLock +{ +public: + RecursiveAutoLock() { + pthread_mutex_lock(&_sMutex); + } + ~RecursiveAutoLock() { + pthread_mutex_unlock(&_sMutex); + } +private: + static pthread_mutex_t _sMutex; +}; + +pthread_mutex_t RecursiveAutoLock::_sMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +static void* makeDlHandle(const mach_header* mh, bool dontContinue) +{ + uintptr_t flags = (dontContinue ? 1 : 0); + return (void*)((((uintptr_t)mh) >> 5) | flags); +} + +VIS_HIDDEN +void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue) +{ + *dontContinue = (((uintptr_t)h) & 1); + *mh = (const mach_header*)((((uintptr_t)h) & (-2)) << 5); +} + +int dlclose(void* handle) +{ + log_apis("dlclose(%p)\n", handle); + + // silently accept magic handles for main executable + if ( handle == RTLD_MAIN_ONLY ) + return 0; + if ( handle == RTLD_DEFAULT ) + return 0; + + // from here on, serialize all dlopen()s + RecursiveAutoLock dlopenSerializer; + + const mach_header* mh; + bool dontContinue; + parseDlHandle(handle, &mh, &dontContinue); + launch_cache::Image image = gAllImages.findByLoadAddress(mh); + if ( image.valid() ) { + // removes image if reference count went to zero + if ( !image.neverUnload() ) + gAllImages.decRefCount(mh); + clearErrorString(); + return 0; + } + else { + setErrorString("invalid handle passed to dlclose()"); + return -1; + } +} + + + +VIS_HIDDEN +const mach_header* loadImageAndDependents(Diagnostics& diag, const launch_cache::binary_format::Image* imageToLoad, bool bumpDlopenCount) +{ + launch_cache::Image topImage(imageToLoad); + uint32_t maxLoad = topImage.maxLoadCount(); + // first construct array of all BinImage* objects that dlopen'ed image depends on + const dyld3::launch_cache::binary_format::Image* fullImageList[maxLoad]; + dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]); + imageSet.add(imageToLoad); + STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList); + gAllImages.copyCurrentGroups(currentGroupsList); + if ( !topImage.recurseAllDependentImages(currentGroupsList, imageSet, nullptr) ) { + diag.error("unexpected > %d images loaded", maxLoad); + return nullptr; + } + + // build array of BinImage* that are not already loaded + const dyld3::launch_cache::binary_format::Image* toLoadImageList[maxLoad]; + const dyld3::launch_cache::binary_format::Image** toLoadImageArray = toLoadImageList; + __block int needToLoadCount = 0; + imageSet.forEach(^(const dyld3::launch_cache::binary_format::Image* aBinImage) { + if ( gAllImages.findLoadAddressByImage(aBinImage) == nullptr ) + toLoadImageArray[needToLoadCount++] = aBinImage; + }); + assert(needToLoadCount > 0); + + // build one array of all existing and to-be-loaded images + uint32_t alreadyLoadImageCount = gAllImages.count(); + STACK_ALLOC_DYNARRAY(loader::ImageInfo, alreadyLoadImageCount + needToLoadCount, allImages); + loader::ImageInfo* allImagesArray = &allImages[0]; + gAllImages.forEachImage(^(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop) { + launch_cache::ImageGroup grp = image.group(); + loader::ImageInfo& info= allImagesArray[imageIndex]; + info.imageData = image.binaryData(); + info.loadAddress = loadAddress; + info.groupNum = grp.groupNum(); + info.indexInGroup = grp.indexInGroup(info.imageData); + info.previouslyFixedUp = true; + info.justMapped = false; + info.justUsedFromDyldCache = false; + info.neverUnload = false; + }); + for (int i=0; i < needToLoadCount; ++i) { + launch_cache::Image img(toLoadImageArray[i]); + launch_cache::ImageGroup grp = img.group(); + loader::ImageInfo& info= allImages[alreadyLoadImageCount+i]; + info.imageData = toLoadImageArray[i]; + info.loadAddress = nullptr; + info.groupNum = grp.groupNum(); + info.indexInGroup = grp.indexInGroup(img.binaryData()); + info.previouslyFixedUp = false; + info.justMapped = false; + info.justUsedFromDyldCache = false; + info.neverUnload = false; + } + + // map new images and apply all fixups + mapAndFixupImages(diag, allImages, (const uint8_t*)gAllImages.cacheLoadAddress(), &dyld3::log_loads, &dyld3::log_segments, &dyld3::log_fixups, &dyld3::log_dofs); + if ( diag.hasError() ) + return nullptr; + const mach_header* topLoadAddress = allImages[alreadyLoadImageCount].loadAddress; + + // bump dlopen refcount of image directly loaded + if ( bumpDlopenCount ) + gAllImages.incRefCount(topLoadAddress); + + // tell gAllImages about new images + dyld3::launch_cache::DynArray newImages(needToLoadCount, &allImages[alreadyLoadImageCount]); + gAllImages.addImages(newImages); + + // tell gAllImages about any old images which now have never unload set + for (int i=0; i < alreadyLoadImageCount; ++i) { + if (allImages[i].neverUnload && !allImages[i].imageData->neverUnload) + gAllImages.setNeverUnload(allImages[i]); + } + + // run initializers + gAllImages.runInitialzersBottomUp(topLoadAddress); + + return topLoadAddress; +} + + +void* dlopen(const char* path, int mode) +{ + log_apis("dlopen(\"%s\", 0x%08X)\n", ((path==NULL) ? "NULL" : path), mode); + + clearErrorString(); + + // passing NULL for path means return magic object + if ( path == NULL ) { + // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images + if ( (mode & RTLD_FIRST) != 0 ) + return RTLD_MAIN_ONLY; + else + return RTLD_DEFAULT; + } + + // from here on, serialize all dlopen()s + RecursiveAutoLock dlopenSerializer; + + const char* leafName = strrchr(path, '/'); + if ( leafName != nullptr ) + ++leafName; + else + leafName = path; + + // RTLD_FIRST means when dlsym() is called with handle, only search the image and not those loaded after it + bool dontContinue = (mode & RTLD_FIRST); + bool bumpRefCount = true; + + // check if dylib with same inode/mtime is already loaded + __block const mach_header* alreadyLoadMH = nullptr; + struct stat statBuf; + if ( stat(path, &statBuf) == 0 ) { + alreadyLoadMH = gAllImages.alreadyLoaded(statBuf.st_ino, statBuf.st_mtime, bumpRefCount); + if ( alreadyLoadMH != nullptr) { + log_apis(" dlopen: path inode/mtime matches already loaded image\n"); + void* result = makeDlHandle(alreadyLoadMH, dontContinue); + log_apis(" dlopen(%s) => %p\n", leafName, result); + return result; + } + } + + // check if already loaded, and if so, just bump ref-count + gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) { + alreadyLoadMH = gAllImages.alreadyLoaded(possiblePath, bumpRefCount); + if ( alreadyLoadMH != nullptr ) { + log_apis(" dlopen: matches already loaded image %s\n", possiblePath); + stop = true; + } + }); + if ( alreadyLoadMH != nullptr) { + void* result = makeDlHandle(alreadyLoadMH, dontContinue); + log_apis(" dlopen(%s) => %p\n", leafName, result); + return result; + } + + // it may be that the path supplied is a symlink to something already loaded + char resolvedPath[PATH_MAX]; + const char* realPathResult = realpath(path, resolvedPath); + // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT + bool checkRealPathToo = ((realPathResult != nullptr) || (errno == ENOENT)) && (strcmp(path, resolvedPath) != 0); + if ( checkRealPathToo ) { + alreadyLoadMH = gAllImages.alreadyLoaded(resolvedPath, bumpRefCount); + log_apis(" dlopen: real path=%s\n", resolvedPath); + if ( alreadyLoadMH != nullptr) { + void* result = makeDlHandle(alreadyLoadMH, dontContinue); + log_apis(" dlopen(%s) => %p\n", leafName, result); + return result; + } + } + + // check if image is in a known ImageGroup + __block const launch_cache::binary_format::Image* imageToLoad = nullptr; + gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stop) { + log_apis(" dlopen: checking for pre-built closure for path: %s\n", possiblePath); + imageToLoad = gAllImages.findImageInKnownGroups(possiblePath); + if ( imageToLoad != nullptr ) + stop = true; + }); + if ( (imageToLoad == nullptr) && checkRealPathToo ) { + gPathOverrides.forEachPathVariant(resolvedPath, ^(const char* possiblePath, bool& stop) { + log_apis(" dlopen: checking for pre-built closure for real path: %s\n", possiblePath); + imageToLoad = gAllImages.findImageInKnownGroups(possiblePath); + if ( imageToLoad != nullptr ) + stop = true; + }); + } + + // check if image from a known ImageGroup is already loaded (via a different path) + if ( imageToLoad != nullptr ) { + alreadyLoadMH = gAllImages.alreadyLoaded(imageToLoad, bumpRefCount); + if ( alreadyLoadMH != nullptr) { + void* result = makeDlHandle(alreadyLoadMH, dontContinue); + log_apis(" dlopen(%s) => %p\n", leafName, result); + return result; + } + } + + // RTLD_NOLOAD means do nothing if image not already loaded + if ( mode & RTLD_NOLOAD ) { + log_apis(" dlopen(%s) => NULL\n", leafName); + return nullptr; + } + + // if we have a closure, optimistically use it. If out of date, it will fail + if ( imageToLoad != nullptr ) { + log_apis(" dlopen: trying existing closure image=%p\n", imageToLoad); + Diagnostics diag; + const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true); + if ( diag.noError() ) { + void* result = makeDlHandle(topLoadAddress, dontContinue); + log_apis(" dlopen(%s) => %p\n", leafName, result); + return result; + } + // image is no longer valid, will need to build one + imageToLoad = nullptr; + log_apis(" dlopen: existing closure no longer valid\n"); + } + + // if no existing closure, RPC to closured to create one + const char* closuredErrorMessages[3]; + int closuredErrorMessagesCount = 0; + if ( imageToLoad == nullptr ) { + imageToLoad = gAllImages.messageClosured(path, "dlopen", closuredErrorMessages, closuredErrorMessagesCount); + } + + // load images using new closure + if ( imageToLoad != nullptr ) { + log_apis(" dlopen: using closured built image=%p\n", imageToLoad); + Diagnostics diag; + const mach_header* topLoadAddress = loadImageAndDependents(diag, imageToLoad, true); + if ( diag.noError() ) { + void* result = makeDlHandle(topLoadAddress, dontContinue); + log_apis(" dlopen(%s) => %p\n", leafName, result); + return result; + } + if ( closuredErrorMessagesCount < 3 ) { + closuredErrorMessages[closuredErrorMessagesCount++] = strdup(diag.errorMessage()); + } + } + + // otherwise, closured failed to build needed load info + switch ( closuredErrorMessagesCount ) { + case 0: + setErrorString("dlopen(%s, 0x%04X): closured error", path, mode); + log_apis(" dlopen: closured error\n"); + break; + case 1: + setErrorString("dlopen(%s, 0x%04X): %s", path, mode, closuredErrorMessages[0]); + log_apis(" dlopen: closured error: %s\n", closuredErrorMessages[0]); + break; + case 2: + setErrorString("dlopen(%s, 0x%04X): %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1]); + log_apis(" dlopen: closured error: %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1]); + break; + case 3: + setErrorString("dlopen(%s, 0x%04X): %s %s %s", path, mode, closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]); + log_apis(" dlopen: closured error: %s %s %s\n", closuredErrorMessages[0], closuredErrorMessages[1], closuredErrorMessages[2]); + break; + } + for (int i=0; i < closuredErrorMessagesCount;++i) + free((void*)closuredErrorMessages[i]); + + log_apis(" dlopen(%s) => NULL\n", leafName); + + return nullptr; +} + +bool dlopen_preflight(const char* path) +{ + log_apis("dlopen_preflight(%s)\n", path); + + if ( gAllImages.alreadyLoaded(path, false) != nullptr ) + return true; + + if ( gAllImages.findImageInKnownGroups(path) != nullptr ) + return true; + + // map whole file + struct stat statBuf; + if ( ::stat(path, &statBuf) != 0 ) + return false; + int fd = ::open(path, O_RDONLY); + if ( fd < 0 ) + return false; + const void* fileBuffer = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + ::close(fd); + if ( fileBuffer == MAP_FAILED ) + return false; + size_t mappedSize = (size_t)statBuf.st_size; + + // check if it is current arch mach-o or fat with slice for current arch + __block bool result = false; + __block Diagnostics diag; + if ( MachOParser::isMachO(diag, fileBuffer, mappedSize) ) { + result = true; + } + else { + if ( FatUtil::isFatFile(fileBuffer) ) { + FatUtil::forEachSlice(diag, fileBuffer, mappedSize, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSz, bool& stop) { + if ( MachOParser::isMachO(diag, sliceStart, sliceSz) ) { + result = true; + stop = true; + } + }); + } + } + ::munmap((void*)fileBuffer, mappedSize); + + // FIXME: may be symlink to something in dyld cache + + // FIXME: maybe ask closured + + return result; +} + +static void* dlsym_search(const char* symName, const mach_header* startImageLoadAddress, const launch_cache::Image& startImage, bool searchStartImage, MachOParser::DependentFinder reExportFollower) +{ + // construct array of all BinImage* objects that dlopen'ed image depends on + uint32_t maxLoad = startImage.maxLoadCount(); + const dyld3::launch_cache::binary_format::Image* fullImageList[maxLoad]; + dyld3::launch_cache::SlowLoadSet imageSet(&fullImageList[0], &fullImageList[maxLoad]); + imageSet.add(startImage.binaryData()); + STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList); + gAllImages.copyCurrentGroups(currentGroupsList); + + __block void* result = nullptr; + auto handler = ^(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop) { + const mach_header* loadAddress = gAllImages.findLoadAddressByImage(aBinImage); + if ( !searchStartImage && (loadAddress == startImageLoadAddress) ) + return; + if ( loadAddress != nullptr ) { + MachOParser parser(loadAddress); + if ( parser.hasExportedSymbol(symName, reExportFollower, &result) ) { + stop = true; + } + } + }; + + bool stop = false; + handler(startImage.binaryData(), stop); + if (stop) + return result; + + // check each dependent image for symbol + if ( !startImage.recurseAllDependentImages(currentGroupsList, imageSet, handler) ) { + setErrorString("unexpected > %d images loaded", maxLoad); + return nullptr; + } + return result; +} + +void* dlsym(void* handle, const char* symbolName) +{ + log_apis("dlsym(%p, \"%s\")\n", handle, symbolName); + + clearErrorString(); + + // dlsym() assumes symbolName passed in is same as in C source code + // dyld assumes all symbol names have an underscore prefix + char underscoredName[strlen(symbolName)+2]; + underscoredName[0] = '_'; + strcpy(&underscoredName[1], symbolName); + + // this block is only used if hasExportedSymbol() needs to trace re-exported dylibs to find a symbol + MachOParser::DependentFinder reExportFollower = ^(uint32_t targetDepIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) { + if ( (strncmp(depLoadPath, "@rpath/", 7) == 0) && (extra != nullptr) ) { + const mach_header* parentMH = (mach_header*)extra; + launch_cache::Image parentImage = gAllImages.findByLoadAddress(parentMH); + if ( parentImage.valid() ) { + STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList); + gAllImages.copyCurrentGroups(currentGroupsList); + parentImage.forEachDependentImage(currentGroupsList, ^(uint32_t parentDepIndex, dyld3::launch_cache::Image parentDepImage, dyld3::launch_cache::Image::LinkKind kind, bool &stop) { + if ( parentDepIndex != targetDepIndex ) + return; + const mach_header* parentDepMH = gAllImages.findLoadAddressByImage(parentDepImage.binaryData()); + if ( parentDepMH != nullptr ) { + *foundMH = parentDepMH; + stop = true; + } + }); + } + } + else { + *foundMH = gAllImages.alreadyLoaded(depLoadPath, false); + } + return (*foundMH != nullptr); + }; + + if ( handle == RTLD_DEFAULT ) { + // magic "search all in load order" handle + for (uint32_t index=0; index < gAllImages.count(); ++index) { + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByLoadOrder(index, &loadAddress); + if ( image.valid() ) { + MachOParser parser(loadAddress); + void* result; + //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress); + if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) { + log_apis(" dlsym() => %p\n", result); + return result; + } + } + } + setErrorString("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName); + log_apis(" dlsym() => NULL\n"); + return nullptr; + } + else if ( handle == RTLD_MAIN_ONLY ) { + // magic "search only main executable" handle + MachOParser parser(gAllImages.mainExecutable()); + //log_apis(" dlsym(): index=%d, loadAddress=%p\n", index, loadAddress); + void* result; + if ( parser.hasExportedSymbol(underscoredName, reExportFollower, &result) ) { + log_apis(" dlsym() => %p\n", result); + return result; + } + setErrorString("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName); + log_apis(" dlsym() => NULL\n"); + return nullptr; + } + + // rest of cases search in dependency order + const mach_header* startImageLoadAddress; + launch_cache::Image startImage(nullptr); + void* result = nullptr; + if ( handle == RTLD_NEXT ) { + // magic "search what I would see" handle + void* callerAddress = __builtin_return_address(0); + startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress); + if ( ! startImage.valid() ) { + setErrorString("dlsym(RTLD_NEXT, %s): called by unknown image (caller=%p)", symbolName, callerAddress); + return nullptr; + } + result = dlsym_search(underscoredName, startImageLoadAddress, startImage, false, reExportFollower); + } + else if ( handle == RTLD_SELF ) { + // magic "search me, then what I would see" handle + void* callerAddress = __builtin_return_address(0); + startImage = gAllImages.findByOwnedAddress(callerAddress, &startImageLoadAddress); + if ( ! startImage.valid() ) { + setErrorString("dlsym(RTLD_SELF, %s): called by unknown image (caller=%p)", symbolName, callerAddress); + return nullptr; + } + result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower); + } + else { + // handle value was something returned by dlopen() + bool dontContinue; + parseDlHandle(handle, &startImageLoadAddress, &dontContinue); + startImage = gAllImages.findByLoadAddress(startImageLoadAddress); + if ( !startImage.valid() ) { + setErrorString("dlsym(%p, %s): invalid handle", handle, symbolName); + log_apis(" dlsym() => NULL\n"); + return nullptr; + } + if ( dontContinue ) { + // RTLD_FIRST only searches one place + MachOParser parser(startImageLoadAddress); + parser.hasExportedSymbol(underscoredName, reExportFollower, &result); + } + else { + result = dlsym_search(underscoredName, startImageLoadAddress, startImage, true, reExportFollower); + } + } + + if ( result != nullptr ) { + log_apis(" dlsym() => %p\n", result); + return result; + } + + setErrorString("dlsym(%p, %s): symbol not found", handle, symbolName); + log_apis(" dlsym() => NULL\n"); + return nullptr; +} + + +const struct dyld_all_image_infos* _dyld_get_all_image_infos() +{ + return gAllImages.oldAllImageInfo(); +} + +bool dyld_shared_cache_some_image_overridden() +{ + log_apis("dyld_shared_cache_some_image_overridden()\n"); + + assert(0 && "not implemented yet"); +} + +bool _dyld_get_shared_cache_uuid(uuid_t uuid) +{ + log_apis("_dyld_get_shared_cache_uuid()\n"); + + if ( gAllImages.oldAllImageInfo() != nullptr ) { + memcpy(uuid, gAllImages.oldAllImageInfo()->sharedCacheUUID, sizeof(uuid_t)); + return true; + } + return false; +} + +const void* _dyld_get_shared_cache_range(size_t* mappedSize) +{ + log_apis("_dyld_get_shared_cache_range()\n"); + + const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress(); + if ( sharedCache != nullptr ) { + *mappedSize = (size_t)sharedCache->mappedSize(); + return sharedCache; + } + *mappedSize = 0; + return NULL; +} + +bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + log_apis("_dyld_find_unwind_sections(%p, %p)\n", addr, info); + + const mach_header* mh = dyld_image_header_containing_address(addr); + if ( mh == nullptr ) + return false; + + info->mh = mh; + info->dwarf_section = nullptr; + info->dwarf_section_length = 0; + info->compact_unwind_section = nullptr; + info->compact_unwind_section_length = 0; + + MachOParser parser(mh); + parser.forEachSection(^(const char* segName, const char* sectName, uint32_t flags, const void* content, size_t sectSize, bool illegalSectionSize, bool& stop) { + if ( strcmp(segName, "__TEXT") == 0 ) { + if ( strcmp(sectName, "__eh_frame") == 0 ) { + info->dwarf_section = content; + info->dwarf_section_length = sectSize; + } + else if ( strcmp(sectName, "__unwind_info") == 0 ) { + info->compact_unwind_section = content; + info->compact_unwind_section_length = sectSize; + } + } + }); + + return true; +} + + +bool dyld_process_is_restricted() +{ + log_apis("dyld_process_is_restricted()\n"); + + launch_cache::Closure closure(gAllImages.mainClosure()); + return closure.isRestricted(); +} + + +const char* dyld_shared_cache_file_path() +{ + log_apis("dyld_shared_cache_file_path()\n"); + + return gAllImages.dyldCachePath(); +} + + +void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count) +{ + log_apis("dyld_dynamic_interpose(%p, %p, %lu)\n", mh, array, count); + // FIXME +} + + +static void* mapStartOfCache(const char* path, size_t length) +{ + struct stat statbuf; + if ( ::stat(path, &statbuf) == -1 ) + return NULL; + + if ( statbuf.st_size < length ) + return NULL; + + int cache_fd = ::open(path, O_RDONLY); + if ( cache_fd < 0 ) + return NULL; + + void* result = ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, cache_fd, 0); + close(cache_fd); + + if ( result == MAP_FAILED ) + return NULL; + + return result; +} + +static const DyldSharedCache* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath, size_t& sizeMapped) +{ + DIR* dirp = ::opendir(dirPath); + if ( dirp != NULL) { + dirent entry; + dirent* entp = NULL; + char cachePath[PATH_MAX]; + while ( ::readdir_r(dirp, &entry, &entp) == 0 ) { + if ( entp == NULL ) + break; + if ( entp->d_type != DT_REG ) + continue; + if ( strlcpy(cachePath, dirPath, PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(cachePath, "/", PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX ) + continue; + if ( const DyldSharedCache* cache = (DyldSharedCache*)mapStartOfCache(cachePath, 0x00100000) ) { + uuid_t foundUuid; + cache->getUUID(foundUuid); + if ( ::memcmp(foundUuid, cacheUuid, 16) != 0 ) { + // wrong uuid, unmap and keep looking + ::munmap((void*)cache, 0x00100000); + } + else { + // found cache + closedir(dirp); + sizeMapped = 0x00100000; + return cache; + } + } + } + closedir(dirp); + } + return nullptr; +} + +int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)) +{ + log_apis("dyld_shared_cache_find_iterate_text()\n"); + + // see if requested cache is the active one in this process + size_t sizeMapped = 0; + const DyldSharedCache* sharedCache = (DyldSharedCache*)gAllImages.cacheLoadAddress(); + if ( sharedCache != nullptr ) { + uuid_t runningUuid; + sharedCache->getUUID(runningUuid); + if ( ::memcmp(runningUuid, cacheUuid, 16) != 0 ) + sharedCache = nullptr; + } + if ( sharedCache == nullptr ) { + // if not, look in default location for cache files + #if __IPHONE_OS_VERSION_MIN_REQUIRED + const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR; + #else + const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR; + #endif + sharedCache = findCacheInDirAndMap(cacheUuid, defaultSearchDir, sizeMapped); + // if not there, look in extra search locations + if ( sharedCache == nullptr ) { + for (const char** p = extraSearchDirs; *p != nullptr; ++p) { + sharedCache = findCacheInDirAndMap(cacheUuid, *p, sizeMapped); + if ( sharedCache != nullptr ) + break; + } + } + } + if ( sharedCache == nullptr ) + return -1; + + // get base address of cache + __block uint64_t cacheUnslidBaseAddress = 0; + sharedCache->forEachRegion(^(const void *content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if ( cacheUnslidBaseAddress == 0 ) + cacheUnslidBaseAddress = vmAddr; + }); + + // iterate all images + sharedCache->forEachImageTextSegment(^(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName) { + dyld_shared_cache_dylib_text_info dylibTextInfo; + dylibTextInfo.version = 2; + dylibTextInfo.loadAddressUnslid = loadAddressUnslid; + dylibTextInfo.textSegmentSize = textSegmentSize; + dylibTextInfo.path = installName; + ::memcpy(dylibTextInfo.dylibUuid, dylibUUID, 16); + dylibTextInfo.textSegmentOffset = loadAddressUnslid - cacheUnslidBaseAddress; + callback(&dylibTextInfo); + }); + + if ( sizeMapped != 0 ) + ::munmap((void*)sharedCache, sizeMapped); + + return 0; +} + +int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)) +{ + log_apis("dyld_shared_cache_iterate_text()\n"); + + const char* extraSearchDirs[] = { NULL }; + return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback); +} + + + +} // namespace dyld3 + diff --git a/dyld/dyld3/APIs.h b/dyld/dyld3/APIs.h new file mode 100644 index 0000000..544f090 --- /dev/null +++ b/dyld/dyld3/APIs.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2017 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_APIS_H__ +#define __DYLD_APIS_H__ + +#include +#include + +#include "dlfcn.h" +#include "dyld_priv.h" + + +#define TEMP_HIDDEN __attribute__((visibility("hidden"))) + +namespace dyld3 { + + +uint32_t _dyld_image_count() TEMP_HIDDEN; + +const mach_header* _dyld_get_image_header(uint32_t imageIndex) TEMP_HIDDEN; + +intptr_t _dyld_get_image_slide(const mach_header* mh) TEMP_HIDDEN; + +intptr_t _dyld_get_image_vmaddr_slide(uint32_t imageIndex) TEMP_HIDDEN; + +const char* _dyld_get_image_name(uint32_t imageIndex) TEMP_HIDDEN; + +int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) TEMP_HIDDEN; + +int32_t NSVersionOfRunTimeLibrary(const char* libraryName) TEMP_HIDDEN; + +#if __WATCH_OS_VERSION_MIN_REQUIRED +uint32_t dyld_get_program_sdk_watch_os_version() TEMP_HIDDEN; + +uint32_t dyld_get_program_min_watch_os_version() TEMP_HIDDEN; +#endif + +#if TARGET_OS_BRIDGE +uint32_t dyld_get_program_sdk_bridge_os_version() TEMP_HIDDEN; + +uint32_t dyld_get_program_min_bridge_os_version() TEMP_HIDDEN; +#endif + + +uint32_t dyld_get_sdk_version(const mach_header* mh) TEMP_HIDDEN; + + +uint32_t dyld_get_program_sdk_version() TEMP_HIDDEN; +uint32_t dyld_get_min_os_version(const mach_header* mh) TEMP_HIDDEN; + +uint32_t dyld_get_program_min_os_version() TEMP_HIDDEN; + + +bool _dyld_get_image_uuid(const mach_header* mh, uuid_t uuid) TEMP_HIDDEN; + +int _NSGetExecutablePath(char* buf, uint32_t* bufsize) TEMP_HIDDEN; + +void _dyld_register_func_for_add_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) TEMP_HIDDEN; + +void _dyld_register_func_for_remove_image(void (*func)(const mach_header *mh, intptr_t vmaddr_slide)) TEMP_HIDDEN; + +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped) TEMP_HIDDEN; + +const mach_header* dyld_image_header_containing_address(const void* addr) TEMP_HIDDEN; + +const mach_header* _dyld_get_image_header_containing_address(const void* address) TEMP_HIDDEN; + +bool _dyld_image_containing_address(const void* address) TEMP_HIDDEN; + +const char* dyld_image_path_containing_address(const void* addr) TEMP_HIDDEN; + +bool _dyld_is_memory_immutable(const void* addr, size_t length) TEMP_HIDDEN; + + +int dladdr(const void* addr, Dl_info* info) TEMP_HIDDEN; + +char* dlerror() TEMP_HIDDEN; + +int dlclose(void* handle) TEMP_HIDDEN; + +void* dlopen(const char* path, int mode) TEMP_HIDDEN; + +bool dlopen_preflight(const char* path) TEMP_HIDDEN; + +void* dlsym(void* handle, const char* symbolName) TEMP_HIDDEN; + +const struct dyld_all_image_infos* _dyld_get_all_image_infos() TEMP_HIDDEN; + +bool dyld_shared_cache_some_image_overridden() TEMP_HIDDEN; + +bool _dyld_get_shared_cache_uuid(uuid_t uuid) TEMP_HIDDEN; + +const void* _dyld_get_shared_cache_range(size_t* length) TEMP_HIDDEN; + +bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) TEMP_HIDDEN; + +bool dyld_process_is_restricted() TEMP_HIDDEN; + +const char* dyld_shared_cache_file_path() TEMP_HIDDEN; + +void dyld_dynamic_interpose(const mach_header* mh, const dyld_interpose_tuple array[], size_t count) TEMP_HIDDEN; + +int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)) TEMP_HIDDEN; + +int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)) TEMP_HIDDEN; + +void _dyld_fork_child() TEMP_HIDDEN; + +// only in macOS and deprecated +#if __MAC_OS_X_VERSION_MIN_REQUIRED +NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) TEMP_HIDDEN; +NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) TEMP_HIDDEN; +bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN; +uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN; +const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) TEMP_HIDDEN; +uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) TEMP_HIDDEN; +const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) TEMP_HIDDEN; +bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) TEMP_HIDDEN; +void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) TEMP_HIDDEN; +const char* NSNameOfModule(NSModule m) TEMP_HIDDEN; +const char* NSLibraryNameForModule(NSModule m) TEMP_HIDDEN; +NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) TEMP_HIDDEN; +bool NSUnLinkModule(NSModule module, uint32_t options) TEMP_HIDDEN; +bool NSIsSymbolNameDefined(const char* symbolName) TEMP_HIDDEN; +bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) TEMP_HIDDEN; +bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) TEMP_HIDDEN; +NSSymbol NSLookupAndBindSymbol(const char* symbolName) TEMP_HIDDEN; +NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) TEMP_HIDDEN; +NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) TEMP_HIDDEN; +NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) TEMP_HIDDEN; +const char* NSNameOfSymbol(NSSymbol symbol) TEMP_HIDDEN; +void* NSAddressOfSymbol(NSSymbol symbol) TEMP_HIDDEN; +NSModule NSModuleForSymbol(NSSymbol symbol) TEMP_HIDDEN; +void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) TEMP_HIDDEN; +bool NSAddLibrary(const char* pathName) TEMP_HIDDEN; +bool NSAddLibraryWithSearching(const char* pathName) TEMP_HIDDEN; +const struct mach_header* NSAddImage(const char* image_name, uint32_t options) TEMP_HIDDEN; +void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) TEMP_HIDDEN; +bool _dyld_present(void) TEMP_HIDDEN; +bool _dyld_launched_prebound(void) TEMP_HIDDEN; +bool _dyld_all_twolevel_modules_prebound(void) TEMP_HIDDEN; +bool _dyld_bind_fully_image_containing_address(const void* address) TEMP_HIDDEN; +bool _dyld_image_containing_address(const void* address) TEMP_HIDDEN; +void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) TEMP_HIDDEN; +void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) TEMP_HIDDEN; +void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) TEMP_HIDDEN; +const struct mach_header* _dyld_get_image_header_containing_address(const void* address) TEMP_HIDDEN; +#endif + +} // namespace dyld3 + +#endif // __DYLD_APIS_H__ + + diff --git a/dyld/dyld3/APIs_macOS.cpp b/dyld/dyld3/APIs_macOS.cpp new file mode 100644 index 0000000..41fdff3 --- /dev/null +++ b/dyld/dyld3/APIs_macOS.cpp @@ -0,0 +1,617 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include <_simple.h> +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dlfcn.h" +#include "dyld_priv.h" + +#include "AllImages.h" +#include "MachOParser.h" +#include "Loading.h" +#include "Logging.h" +#include "Diagnostics.h" +#include "DyldSharedCache.h" +#include "APIs.h" + + + +typedef dyld3::launch_cache::binary_format::Image BinaryImage; + + +namespace dyld3 { + +// from APIs.cpp +void parseDlHandle(void* h, const mach_header** mh, bool* dontContinue); +const mach_header* loadImageAndDependents(Diagnostics& diag, const launch_cache::binary_format::Image* imageToLoad, bool bumpDlopenCount); + + +// only in macOS and deprecated +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +// macOS needs to support an old API that only works with fileype==MH_BUNDLE. +// In this deprecated API (unlike dlopen), loading and linking are separate steps. +// NSCreateObjectFileImageFrom*() just maps in the bundle mach-o file. +// NSLinkModule() does the load of dependent modules and rebasing/binding. +// To unload one of these, you must call NSUnLinkModule() and NSDestroyObjectFileImage() in any order! +// + +NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* path, NSObjectFileImage* ofi) +{ + log_apis("NSCreateObjectFileImageFromFile(\"%s\", %p)\n", path, ofi); + + // verify path exists + struct stat statbuf; + if ( ::stat(path, &statbuf) == -1 ) + return NSObjectFileImageFailure; + + // create ofi that just contains path. NSLinkModule does all the work + __NSObjectFileImage* result = gAllImages.addNSObjectFileImage(); + result->path = strdup(path); + result->memSource = nullptr; + result->memLength = 0; + result->loadAddress = nullptr; + result->binImage = nullptr; + *ofi = result; + + log_apis("NSCreateObjectFileImageFromFile() => %p\n", result); + + return NSObjectFileImageSuccess; +} + +NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* memImage, size_t memImageSize, NSObjectFileImage *ofi) +{ + log_apis("NSCreateObjectFileImageFromMemory(%p, 0x%0lX, %p)\n", memImage, memImageSize, ofi); + + // sanity check the buffer is a mach-o file + __block Diagnostics diag; + __block const mach_header* foundMH = nullptr; + if ( MachOParser::isMachO(diag, memImage, memImageSize) ) { + foundMH = (mach_header*)memImage; + } + else { + FatUtil::forEachSlice(diag, memImage, memImageSize, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) { + if ( MachOParser::isMachO(diag, sliceStart, sliceSize) ) { + foundMH = (mach_header*)sliceStart; + stop = true; + } + }); + } + if ( foundMH == nullptr ) { + log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n"); + return NSObjectFileImageFailure; + } + + // this API can only be used with bundles + if ( foundMH->filetype != MH_BUNDLE ) { + log_apis("NSCreateObjectFileImageFromMemory() not a bundle, filetype=%d\n", foundMH->filetype); + return NSObjectFileImageInappropriateFile; + } + + // allocate ofi that just lists the memory range + __NSObjectFileImage* result = gAllImages.addNSObjectFileImage(); + result->path = nullptr; + result->memSource = memImage; + result->memLength = memImageSize; + result->loadAddress = nullptr; + result->binImage = nullptr; + *ofi = result; + + log_apis("NSCreateObjectFileImageFromMemory() => %p\n", result); + + return NSObjectFileImageSuccess; +} + +NSModule NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options) +{ + log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi, moduleName, options); + + // ofi is invalid if not in list + if ( !gAllImages.hasNSObjectFileImage(ofi) ) { + log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n"); + return nullptr; + } + + // if this is memory based image, write to temp file, then use file based loading + const BinaryImage* imageToLoad = nullptr; + if ( ofi->memSource != nullptr ) { + // make temp file with content of memory buffer + bool successfullyWritten = false; + ofi->path = ::tempnam(nullptr, "NSCreateObjectFileImageFromMemory-"); + if ( ofi->path != nullptr ) { + int fd = ::open(ofi->path, O_WRONLY | O_CREAT | O_EXCL, 0644); + if ( fd != -1 ) { + ssize_t writtenSize = ::pwrite(fd, ofi->memSource, ofi->memLength, 0); + if ( writtenSize == ofi->memLength ) + successfullyWritten = true; + ::close(fd); + } + } + if ( !successfullyWritten ) { + if ( ofi->path != nullptr ) { + free((void*)ofi->path); + ofi->path = nullptr; + } + log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n"); + return nullptr; + } + } + else { + // check if image is in a known ImageGroup, but not loaded. if so, load using existing closure info + log_apis(" NSLinkModule: checking for pre-built closure for path: %s\n", ofi->path); + imageToLoad = gAllImages.findImageInKnownGroups(ofi->path); + // TODO: check symlinks, realpath + } + + // if no existing closure, RPC to closured to create one + if ( imageToLoad == nullptr ) { + const char* closuredErrorMessages[3]; + int closuredErrorMessagesCount = 0; + if ( imageToLoad == nullptr ) { + imageToLoad = gAllImages.messageClosured(ofi->path, "NSLinkModule", closuredErrorMessages, closuredErrorMessagesCount); + } + for (int i=0; i < closuredErrorMessagesCount; ++i) { + log_apis(" NSLinkModule: failed: %s\n", closuredErrorMessages[i]); + free((void*)closuredErrorMessages[i]); + } + } + + // use Image info to load and fixup image and all its dependents + if ( imageToLoad != nullptr ) { + Diagnostics diag; + ofi->loadAddress = loadImageAndDependents(diag, imageToLoad, true); + if ( diag.hasError() ) + log_apis(" NSLinkModule: failed: %s\n", diag.errorMessage()); + } + + // if memory based load, delete temp file + if ( ofi->memSource != nullptr ) { + log_apis(" NSLinkModule: delete temp file: %s\n", ofi->path); + ::unlink(ofi->path); + } + + log_apis("NSLinkModule() => %p\n", ofi->loadAddress); + return (NSModule)ofi->loadAddress; +} + +// NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage +bool NSUnLinkModule(NSModule module, uint32_t options) +{ + log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options); + + bool result = false; + const mach_header* mh = (mach_header*)module; + launch_cache::Image image = gAllImages.findByLoadAddress(mh); + if ( image.valid() ) { + // removes image if reference count went to zero + gAllImages.decRefCount(mh); + result = true; + } + + log_apis("NSUnLinkModule() => %d\n", result); + + return result; +} + +// NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use +bool NSDestroyObjectFileImage(NSObjectFileImage ofi) +{ + log_apis("NSDestroyObjectFileImage(%p)\n", ofi); + + // ofi is invalid if not in list + if ( !gAllImages.hasNSObjectFileImage(ofi) ) + return false; + + // keep copy of info + const void* memSource = ofi->memSource; + size_t memLength = ofi->memLength; + const char* path = ofi->path; + + // remove from list + gAllImages.removeNSObjectFileImage(ofi); + + // if object was created from a memory, release that memory + // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands ownership of the memory to dyld + if ( memSource != nullptr ) { + // we don't know if memory came from malloc or vm_allocate, so ask malloc + if ( malloc_size(memSource) != 0 ) + free((void*)(memSource)); + else + vm_deallocate(mach_task_self(), (vm_address_t)memSource, memLength); + } + free((void*)path); + + return true; +} + +uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) +{ + halt("NSSymbolDefinitionCountInObjectFileImage() is obsolete"); +} + +const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) +{ + halt("NSSymbolDefinitionNameInObjectFileImage() is obsolete"); +} + +uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) +{ + halt("NSSymbolReferenceCountInObjectFileImage() is obsolete"); +} + +const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) +{ + halt("NSSymbolReferenceNameInObjectFileImage() is obsolete"); +} + +bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage ofi, const char* symbolName) +{ + log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", ofi, symbolName); + + // ofi is invalid if not in list + if ( !gAllImages.hasNSObjectFileImage(ofi) ) + return false; + + void* addr; + MachOParser parser(ofi->loadAddress); + return parser.hasExportedSymbol(symbolName, ^(uint32_t , const char*, void*, const mach_header**, void**) { + return false; + }, &addr); +} + +void* NSGetSectionDataInObjectFileImage(NSObjectFileImage ofi, const char* segmentName, const char* sectionName, size_t* size) +{ + // ofi is invalid if not in list + if ( !gAllImages.hasNSObjectFileImage(ofi) ) + return nullptr; + + __block void* result = nullptr; + MachOParser parser(ofi->loadAddress); + parser.forEachSection(^(const char* aSegName, const char* aSectName, uint32_t flags, const void* content, size_t aSize, bool illegalSectionSize, bool& stop) { + if ( (strcmp(sectionName, aSectName) == 0) && (strcmp(segmentName, aSegName) == 0) ) { + result = (void*)content; + if ( size != nullptr ) + *size = aSize; + stop = true; + } + }); + return result; +} + +const char* NSNameOfModule(NSModule m) +{ + log_apis("NSNameOfModule(%p)\n", m); + + const mach_header* foundInLoadAddress; + launch_cache::Image image = gAllImages.findByOwnedAddress(m, &foundInLoadAddress); + if ( image.valid() ) { + return gAllImages.imagePath(image.binaryData()); + } + return nullptr; +} + +const char* NSLibraryNameForModule(NSModule m) +{ + log_apis("NSLibraryNameForModule(%p)\n", m); + + const mach_header* foundInLoadAddress; + launch_cache::Image image = gAllImages.findByOwnedAddress(m, &foundInLoadAddress); + if ( image.valid() ) { + return gAllImages.imagePath(image.binaryData()); + } + return nullptr; +} + + +static bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress) +{ + for (uint32_t index=0; index < gAllImages.count(); ++index) { + const mach_header* loadAddress; + launch_cache::Image image = gAllImages.findByLoadOrder(index, &loadAddress); + if ( image.valid() ) { + MachOParser parser(loadAddress); + if ( parser.hasExportedSymbol(symbolName, ^(uint32_t , const char* , void* , const mach_header** , void**) { return false; }, symbolAddress) ) { + *foundInImageAtLoadAddress = loadAddress; + return true; + } + } + } + return false; +} + +bool NSIsSymbolNameDefined(const char* symbolName) +{ + log_apis("NSIsSymbolNameDefined(%s)\n", symbolName); + + const mach_header* foundInImageAtLoadAddress; + void* address; + return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress); +} + +bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) +{ + log_apis("NSIsSymbolNameDefinedWithHint(%s, %s)\n", symbolName, libraryNameHint); + + const mach_header* foundInImageAtLoadAddress; + void* address; + return flatFindSymbol(symbolName, &address, &foundInImageAtLoadAddress); +} + +bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName) +{ + log_apis("NSIsSymbolNameDefinedInImage(%p, %s)\n", mh, symbolName); + + MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) { + *foundMH = gAllImages.alreadyLoaded(depLoadPath, false); + return (*foundMH != nullptr); + }; + + MachOParser parser(mh); + void* result; + return parser.hasExportedSymbol(symbolName, reExportFollower, &result); +} + +NSSymbol NSLookupAndBindSymbol(const char* symbolName) +{ + log_apis("NSLookupAndBindSymbol(%s)\n", symbolName); + + const mach_header* foundInImageAtLoadAddress; + void* symbolAddress; + if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) { + return (NSSymbol)symbolAddress; + } + return nullptr; +} + +NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) +{ + log_apis("NSLookupAndBindSymbolWithHint(%s, %s)\n", symbolName, libraryNameHint); + + const mach_header* foundInImageAtLoadAddress; + void* symbolAddress; + if ( flatFindSymbol(symbolName, &symbolAddress, &foundInImageAtLoadAddress) ) { + return (NSSymbol)symbolAddress; + } + return nullptr; +} + +NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) +{ + log_apis("NSLookupSymbolInModule(%p. %s)\n", module, symbolName); + + MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) { + *foundMH = gAllImages.alreadyLoaded(depLoadPath, false); + return (*foundMH != nullptr); + }; + + const mach_header* mh = (const mach_header*)module; + uint32_t loadIndex; + if ( gAllImages.findIndexForLoadAddress(mh, loadIndex) ) { + MachOParser parser(mh); + void* symAddress; + if ( parser.hasExportedSymbol(symbolName, reExportFollower, &symAddress) ) { + return (NSSymbol)symAddress; + } + } + return nullptr; +} + +NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options) +{ + log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh, symbolName, options); + + MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) { + *foundMH = gAllImages.alreadyLoaded(depLoadPath, false); + return (*foundMH != nullptr); + }; + + MachOParser parser(mh); + void* result; + if ( parser.hasExportedSymbol(symbolName, reExportFollower, &result) ) { + log_apis(" NSLookupSymbolInImage() => %p\n", result); + return (NSSymbol)result; + } + + if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR ) { + log_apis(" NSLookupSymbolInImage() => NULL\n"); + return nullptr; + } + return nullptr; +} + +const char* NSNameOfSymbol(NSSymbol symbol) +{ + halt("NSNameOfSymbol() is obsolete"); +} + +void* NSAddressOfSymbol(NSSymbol symbol) +{ + log_apis("NSAddressOfSymbol(%p)\n", symbol); + + // in dyld 1.0, NSSymbol was a pointer to the nlist entry in the symbol table + return (void*)symbol; +} + +NSModule NSModuleForSymbol(NSSymbol symbol) +{ + log_apis("NSModuleForSymbol(%p)\n", symbol); + + const mach_header* foundInLoadAddress; + launch_cache::Image image = gAllImages.findByOwnedAddress(symbol, &foundInLoadAddress); + if ( image.valid() ) { + return (NSModule)foundInLoadAddress; + } + return nullptr; +} + +void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) +{ + log_apis("NSLinkEditError(%p, %p, %p, %p)\n", c, errorNumber, fileName, errorString); + *c = NSLinkEditOtherError; + *errorNumber = 0; + *fileName = NULL; + *errorString = NULL; +} + +bool NSAddLibrary(const char* pathName) +{ + log_apis("NSAddLibrary(%s)\n", pathName); + + return ( dlopen(pathName, 0) != nullptr); +} + +bool NSAddLibraryWithSearching(const char* pathName) +{ + log_apis("NSAddLibraryWithSearching(%s)\n", pathName); + + return ( dlopen(pathName, 0) != nullptr); +} + +const mach_header* NSAddImage(const char* imageName, uint32_t options) +{ + log_apis("NSAddImage(\"%s\", 0x%08X)\n", imageName, options); + + // Note: this is a quick and dirty implementation that just uses dlopen() and ignores some option flags + uint32_t dloptions = 0; + if ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 ) + dloptions |= RTLD_NOLOAD; + + void* h = dlopen(imageName, dloptions); + if ( h != nullptr ) { + const mach_header* mh; + bool dontContinue; + parseDlHandle(h, &mh, &dontContinue); + return mh; + } + + if ( (options & (NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED)) == 0 ) { + halt("NSAddImage() image not found"); + } + return nullptr; +} + +void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) +{ + halt("NSInstallLinkEditErrorHandlers() is obsolete"); +} + +bool _dyld_present(void) +{ + log_apis("_dyld_present()\n"); + + return true; +} + +bool _dyld_launched_prebound(void) +{ + halt("_dyld_launched_prebound() is obsolete"); +} + +bool _dyld_all_twolevel_modules_prebound(void) +{ + halt("_dyld_all_twolevel_modules_prebound() is obsolete"); +} + +bool _dyld_bind_fully_image_containing_address(const void* address) +{ + log_apis("_dyld_bind_fully_image_containing_address(%p)\n", address); + + // in dyld3, everything is always fully bound + return true; +} + +bool _dyld_image_containing_address(const void* address) +{ + log_apis("_dyld_image_containing_address(%p)\n", address); + + return (dyld_image_header_containing_address(address) != nullptr); +} + +void _dyld_lookup_and_bind(const char* symbolName, void **address, NSModule* module) +{ + log_apis("_dyld_lookup_and_bind(%s, %p, %p)\n", symbolName, address, module); + + const mach_header* foundInImageAtLoadAddress; + if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) { + *module = (NSModule)foundInImageAtLoadAddress; + return; + } + + *address = 0; + *module = 0; +} + +void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* libraryNameHint, void** address, NSModule* module) +{ + log_apis("_dyld_lookup_and_bind_with_hint(%s, %s, %p, %p)\n", symbolName, libraryNameHint, address, module); + + const mach_header* foundInImageAtLoadAddress; + if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) { + *module = (NSModule)foundInImageAtLoadAddress; + return; + } + + *address = 0; + *module = 0; +} + + +void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module) +{ + log_apis("_dyld_lookup_and_bind_fully(%s, %p, %p)\n", symbolName, address, module); + + const mach_header* foundInImageAtLoadAddress; + if ( flatFindSymbol(symbolName, address, &foundInImageAtLoadAddress) ) { + *module = (NSModule)foundInImageAtLoadAddress; + return; + } + + *address = 0; + *module = 0; +} + +const struct mach_header* _dyld_get_image_header_containing_address(const void* address) +{ + log_apis("_dyld_get_image_header_containing_address(%p)\n", address); + + return dyld_image_header_containing_address(address); +} + +#endif + + +} // namespace dyld3 + diff --git a/dyld/dyld3/AllImages.cpp b/dyld/dyld3/AllImages.cpp new file mode 100644 index 0000000..6d64e36 --- /dev/null +++ b/dyld/dyld3/AllImages.cpp @@ -0,0 +1,1562 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include // mach_absolute_time() +#include +#include + +#include +#include + +#include "AllImages.h" +#include "MachOParser.h" +#include "libdyldEntryVector.h" +#include "Logging.h" +#include "Loading.h" +#include "Tracing.h" +#include "LaunchCache.h" +#include "DyldSharedCache.h" +#include "PathOverrides.h" +#include "DyldCacheParser.h" + +extern const char** appleParams; + +// should be a header for these +struct __cxa_range_t { + const void* addr; + size_t length; +}; +extern "C" void __cxa_finalize_ranges(const __cxa_range_t ranges[], unsigned int count); + +VIS_HIDDEN bool gUseDyld3 = false; + + +namespace dyld3 { + +class VIS_HIDDEN LoadedImage { +public: + enum class State { uninited=3, beingInited=2, inited=0 }; + typedef launch_cache::binary_format::Image BinaryImage; + + LoadedImage(const mach_header* mh, const BinaryImage* bi); + bool operator==(const LoadedImage& rhs) const; + void init(const mach_header* mh, const BinaryImage* bi); + const mach_header* loadedAddress() const { return (mach_header*)((uintptr_t)_loadAddress & ~0x7ULL); } + State state() const { return (State)((uintptr_t)_loadAddress & 0x3ULL); } + const BinaryImage* image() const { return _image; } + bool neverUnload() const { return ((uintptr_t)_loadAddress & 0x4ULL); } + void setState(State s) { _loadAddress = (mach_header*)((((uintptr_t)_loadAddress) & ~0x3ULL) | (uintptr_t)s); } + void setNeverUnload() { _loadAddress = (mach_header*)(((uintptr_t)_loadAddress) | 0x4ULL); } + +private: + const mach_header* _loadAddress; // low bits: bit2=neverUnload, bit1/bit0 contain State + const BinaryImage* _image; +}; + + +bool LoadedImage::operator==(const LoadedImage& rhs) const +{ + return (_image == rhs._image) && (loadedAddress() == rhs.loadedAddress()); +} + + + +struct VIS_HIDDEN DlopenCount { + bool operator==(const DlopenCount& rhs) const; + const mach_header* loadAddress; + uintptr_t refCount; +}; + +bool DlopenCount::operator==(const DlopenCount& rhs) const +{ + return (loadAddress == rhs.loadAddress) && (refCount == rhs.refCount); +} + +LoadedImage::LoadedImage(const mach_header* mh, const BinaryImage* bi) + : _loadAddress(mh), _image(bi) +{ + assert(loadedAddress() == mh); + setState(State::uninited); +} + +void LoadedImage::init(const mach_header* mh, const BinaryImage* bi) +{ + _loadAddress = mh; + _image = bi; + assert(loadedAddress() == mh); + setState(State::uninited); +} + +// forward reference +template class ReaderWriterChunkedVector; + +template +class VIS_HIDDEN ChunkedVector { +public: + static ChunkedVector* make(uint32_t count); + + void forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const; + void forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop)); + T* add(const T& value); + T* add(uint32_t count, const T values[]); + void remove(uint32_t index); + uint32_t count() const { return _inUseCount; } + uint32_t freeCount() const { return _allocCount - _inUseCount; } +private: + T& element(uint32_t index) { return ((T*)_elements)[index]; } + const T& element(uint32_t index) const { return ((T*)_elements)[index]; } + + friend class ReaderWriterChunkedVector; + + ChunkedVector* _next = nullptr; + uint32_t _allocCount = C; + uint32_t _inUseCount = 0; + uint8_t _elements[C*sizeof(T)] = { 0 }; +}; + +template +class VIS_HIDDEN ReaderWriterChunkedVector { +public: + T* add(uint32_t count, const T values[]); + T* add(const T& value) { return add(1, &value); } + T* addNoLock(uint32_t count, const T values[]); + T* addNoLock(const T& value) { return addNoLock(1, &value); } + void remove(const T& value); + uint32_t count() const; + void forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const; + void forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop)); + void forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const; + T& operator[](size_t index); + uint32_t countNoLock() const; + + void withReadLock(void (^withLock)()) const; + void withWriteLock(void (^withLock)()) const; + void acquireWriteLock(); + void releaseWriteLock(); + void dump(void (^callback)(const T& value)) const; + +private: + mutable pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER; + ChunkedVector _firstChunk; +}; + + +typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide); + +static ReaderWriterChunkedVector sLoadNotifiers; +static ReaderWriterChunkedVector sUnloadNotifiers; +static ReaderWriterChunkedVector sLoadedImages; +static ReaderWriterChunkedVector sDlopenRefCounts; +static ReaderWriterChunkedVector sKnownGroups; +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static ReaderWriterChunkedVector<__NSObjectFileImage, 2> sNSObjectFileImages; +#endif + + +///////////////////// ChunkedVector //////////////////////////// + +template +ChunkedVector* ChunkedVector::make(uint32_t count) +{ + size_t size = sizeof(ChunkedVector) + sizeof(T) * (count-C); + ChunkedVector* result = (ChunkedVector*)malloc(size); + result->_next = nullptr; + result->_allocCount = count; + result->_inUseCount = 0; + return result; +} + +template +void ChunkedVector::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const +{ + for (uint32_t i=0; i < _inUseCount; ++i) { + callback(outerIndex, element(i), outerStop); + ++outerIndex; + if ( outerStop ) + break; + } +} + +template +void ChunkedVector::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop)) +{ + for (uint32_t i=0; i < _inUseCount; ++i) { + callback(outerIndex, element(i), outerStop); + ++outerIndex; + if ( outerStop ) + break; + } +} + +template +T* ChunkedVector::add(const T& value) +{ + return add(1, &value); +} + +template +T* ChunkedVector::add(uint32_t count, const T values[]) +{ + assert(count <= (_allocCount - _inUseCount)); + T* result = &element(_inUseCount); + memmove(result, values, sizeof(T)*count); + _inUseCount += count; + return result; +} + +template +void ChunkedVector::remove(uint32_t index) +{ + assert(index < _inUseCount); + int moveCount = _inUseCount - index - 1; + if ( moveCount >= 1 ) { + memmove(&element(index), &element(index+1), sizeof(T)*moveCount); + } + _inUseCount--; +} + + +///////////////////// ReaderWriterChunkedVector //////////////////////////// + + + +template +void ReaderWriterChunkedVector::withReadLock(void (^work)()) const +{ + assert(pthread_rwlock_rdlock(&_lock) == 0); + work(); + assert(pthread_rwlock_unlock(&_lock) == 0); +} + +template +void ReaderWriterChunkedVector::withWriteLock(void (^work)()) const +{ + assert(pthread_rwlock_wrlock(&_lock) == 0); + work(); + assert(pthread_rwlock_unlock(&_lock) == 0); +} + +template +void ReaderWriterChunkedVector::acquireWriteLock() +{ + assert(pthread_rwlock_wrlock(&_lock) == 0); +} + +template +void ReaderWriterChunkedVector::releaseWriteLock() +{ + assert(pthread_rwlock_unlock(&_lock) == 0); +} + +template +uint32_t ReaderWriterChunkedVector::count() const +{ + __block uint32_t result = 0; + withReadLock(^() { + for (const ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + result += chunk->count(); + } + }); + return result; +} + +template +uint32_t ReaderWriterChunkedVector::countNoLock() const +{ + uint32_t result = 0; + for (const ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + result += chunk->count(); + } + return result; +} + +template +T* ReaderWriterChunkedVector::addNoLock(uint32_t count, const T values[]) +{ + T* result = nullptr; + ChunkedVector* lastChunk = &_firstChunk; + while ( lastChunk->_next != nullptr ) + lastChunk = lastChunk->_next; + + if ( lastChunk->freeCount() >= count ) { + // append to last chunk + result = lastChunk->add(count, values); + } + else { + // append new chunk + uint32_t allocCount = count; + uint32_t remainder = count % C; + if ( remainder != 0 ) + allocCount = count + C - remainder; + ChunkedVector* newChunk = ChunkedVector::make(allocCount); + result = newChunk->add(count, values); + lastChunk->_next = newChunk; + } + + return result; +} + +template +T* ReaderWriterChunkedVector::add(uint32_t count, const T values[]) +{ + __block T* result = nullptr; + withWriteLock(^() { + result = addNoLock(count, values); + }); + return result; +} + +template +void ReaderWriterChunkedVector::remove(const T& valueToRemove) +{ + __block bool stopStorage = false; + withWriteLock(^() { + ChunkedVector* chunkNowEmpty = nullptr; + __block uint32_t indexStorage = 0; + __block bool found = false; + for (ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + uint32_t chunkStartIndex = indexStorage; + __block uint32_t foundIndex = 0; + chunk->forEach(indexStorage, stopStorage, ^(uint32_t index, const T& value, bool& stop) { + if ( value == valueToRemove ) { + foundIndex = index - chunkStartIndex; + found = true; + stop = true; + } + }); + if ( found ) { + chunk->remove(foundIndex); + found = false; + if ( chunk->count() == 0 ) + chunkNowEmpty = chunk; + } + } + // if chunk is now empty, remove from linked list and free + if ( chunkNowEmpty ) { + for (ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + if ( chunk->_next == chunkNowEmpty ) { + chunk->_next = chunkNowEmpty->_next; + if ( chunkNowEmpty != &_firstChunk ) + free(chunkNowEmpty); + break; + } + } + } + }); +} + +template +void ReaderWriterChunkedVector::forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const +{ + __block uint32_t index = 0; + __block bool stop = false; + withReadLock(^() { + for (const ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + chunk->forEach(index, stop, callback); + if ( stop ) + break; + } + }); +} + +template +void ReaderWriterChunkedVector::forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop)) +{ + __block uint32_t index = 0; + __block bool stop = false; + withReadLock(^() { + for (ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + chunk->forEach(index, stop, callback); + if ( stop ) + break; + } + }); +} + +template +void ReaderWriterChunkedVector::forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const +{ + uint32_t index = 0; + bool stop = false; + for (const ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + chunk->forEach(index, stop, callback); + if ( stop ) + break; + } +} + +template +T& ReaderWriterChunkedVector::operator[](size_t targetIndex) +{ + __block T* result = nullptr; + forEachNoLock(^(uint32_t index, T const& value, bool& stop) { + if ( index == targetIndex ) { + result = (T*)&value; + stop = true; + } + }); + return *result; +} + +template +void ReaderWriterChunkedVector::dump(void (^callback)(const T& value)) const +{ + log("dump ReaderWriterChunkedVector at %p\n", this); + __block uint32_t index = 0; + __block bool stop = false; + withReadLock(^() { + for (const ChunkedVector* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) { + log(" chunk at %p\n", chunk); + chunk->forEach(index, stop, ^(uint32_t i, const T& value, bool& s) { + callback(value); + }); + } + }); +} + + + +///////////////////// AllImages //////////////////////////// + + +AllImages gAllImages; + + + +void AllImages::init(const BinaryClosure* closure, const void* dyldCacheLoadAddress, const char* dyldCachePath, + const dyld3::launch_cache::DynArray& initialImages) +{ + _mainClosure = closure; + _initialImages = &initialImages; + _dyldCacheAddress = dyldCacheLoadAddress; + _dyldCachePath = dyldCachePath; + + // Make temporary old image array, so libSystem initializers can be debugged + uint32_t count = (uint32_t)initialImages.count(); + dyld_image_info oldDyldInfo[count]; + for (int i=0; i < count; ++i) { + launch_cache::Image img(initialImages[i].imageData); + oldDyldInfo[i].imageLoadAddress = initialImages[i].loadAddress; + oldDyldInfo[i].imageFilePath = img.path(); + oldDyldInfo[i].imageFileModDate = 0; + } + _oldAllImageInfos->infoArray = oldDyldInfo; + _oldAllImageInfos->infoArrayCount = count; + _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo); + _oldAllImageInfos->infoArray = nullptr; + _oldAllImageInfos->infoArrayCount = 0; +} + +void AllImages::setProgramVars(ProgramVars* vars) +{ + _programVars = vars; +} + +void AllImages::applyInitialImages() +{ + addImages(*_initialImages); + _initialImages = nullptr; // this was stack allocated +} + +void AllImages::mirrorToOldAllImageInfos() +{ + // set infoArray to NULL to denote it is in-use + _oldAllImageInfos->infoArray = nullptr; + + // if array not large enough, re-alloc it + uint32_t imageCount = sLoadedImages.countNoLock(); + if ( _oldArrayAllocCount < imageCount ) { + uint32_t newAllocCount = imageCount + 16; + dyld_image_info* newArray = (dyld_image_info*)malloc(sizeof(dyld_image_info)*newAllocCount); + if ( _oldAllImageArray != nullptr ) { + memcpy(newArray, _oldAllImageArray, sizeof(dyld_image_info)*_oldAllImageInfos->infoArrayCount); + free(_oldAllImageArray); + } + _oldAllImageArray = newArray; + _oldArrayAllocCount = newAllocCount; + } + + // fill out array to mirror current image list + sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& loadedImage, bool& stop) { + launch_cache::Image img(loadedImage.image()); + _oldAllImageArray[index].imageLoadAddress = loadedImage.loadedAddress(); + _oldAllImageArray[index].imageFilePath = imagePath(loadedImage.image()); + _oldAllImageArray[index].imageFileModDate = 0; + }); + + // set infoArray back to base address of array (so other process can now read) + _oldAllImageInfos->infoArrayCount = imageCount; + _oldAllImageInfos->infoArrayChangeTimestamp = mach_absolute_time(); + _oldAllImageInfos->infoArray = _oldAllImageArray; +} + +void AllImages::addImages(const launch_cache::DynArray& newImages) +{ + uint32_t count = (uint32_t)newImages.count(); + assert(count != 0); + + // build stack array of LoadedImage to copy into sLoadedImages + STACK_ALLOC_DYNARRAY(LoadedImage, count, loadedImagesArray); + for (uint32_t i=0; i < count; ++i) { + loadedImagesArray[i].init(newImages[i].loadAddress, newImages[i].imageData); + if (newImages[i].neverUnload) + loadedImagesArray[i].setNeverUnload(); + } + sLoadedImages.add(count, &loadedImagesArray[0]); + + if ( _oldAllImageInfos != nullptr ) { + // sync to old all image infos struct + if ( _initialImages != nullptr ) { + // libSystem not initialized yet, don't use locks + mirrorToOldAllImageInfos(); + } + else { + sLoadedImages.withReadLock(^{ + mirrorToOldAllImageInfos(); + }); + } + + // tell debugger about new images + dyld_image_info oldDyldInfo[count]; + for (int i=0; i < count; ++i) { + launch_cache::Image img(newImages[i].imageData); + oldDyldInfo[i].imageLoadAddress = newImages[i].loadAddress; + oldDyldInfo[i].imageFilePath = imagePath(newImages[i].imageData); + oldDyldInfo[i].imageFileModDate = 0; + } + _oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo); + } + + // log loads + for (int i=0; i < count; ++i) { + launch_cache::Image img(newImages[i].imageData); + log_loads("dyld: %s\n", imagePath(newImages[i].imageData)); + } + +#if !TARGET_IPHONE_SIMULATOR + // call kdebug trace for each image + if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) { + for (uint32_t i=0; i < count; ++i) { + launch_cache::Image img(newImages[i].imageData); + struct stat stat_buf; + fsid_t fsid = {{ 0, 0 }}; + fsobj_id_t fsobjid = { 0, 0 }; + if (img.isDiskImage() && stat(imagePath(newImages[i].imageData), &stat_buf) == 0 ) { + fsobjid = *(fsobj_id_t*)&stat_buf.st_ino; + fsid = {{ stat_buf.st_dev, 0 }}; + } + kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, img.uuid(), fsobjid, fsid, newImages[i].loadAddress); + } + } +#endif + // call each _dyld_register_func_for_add_image function with each image + const uint32_t existingNotifierCount = sLoadNotifiers.count(); + NotifyFunc existingNotifiers[existingNotifierCount]; + NotifyFunc* existingNotifierArray = existingNotifiers; + sLoadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) { + if ( index < existingNotifierCount ) + existingNotifierArray[index] = func; + }); + // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock) + for (uint32_t j=0; j < existingNotifierCount; ++j) { + NotifyFunc func = existingNotifierArray[j]; + for (uint32_t i=0; i < count; ++i) { + MachOParser parser(newImages[i].loadAddress); + log_notifications("dyld: add notifier %p called with mh=%p\n", func, newImages[i].loadAddress); + func(newImages[i].loadAddress, parser.getSlide()); + } + } + + // call objc about images that use objc + if ( _objcNotifyMapped != nullptr ) { + const char* pathsBuffer[count]; + const mach_header* mhBuffer[count]; + uint32_t imagesWithObjC = 0; + for (uint32_t i=0; i < count; ++i) { + launch_cache::Image img(newImages[i].imageData); + if ( img.hasObjC() ) { + pathsBuffer[imagesWithObjC] = imagePath(newImages[i].imageData); + mhBuffer[imagesWithObjC] = newImages[i].loadAddress; + ++imagesWithObjC; + } + } + if ( imagesWithObjC != 0 ) { + (*_objcNotifyMapped)(imagesWithObjC, pathsBuffer, mhBuffer); + if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) { + for (uint32_t i=0; i < imagesWithObjC; ++i) { + log_notifications("dyld: objc-mapped: %p %s\n", mhBuffer[i], pathsBuffer[i]); + } + } + } + } + + // notify any processes tracking loads in this process + notifyMonitorLoads(newImages); +} + +void AllImages::removeImages(const launch_cache::DynArray& unloadImages) +{ + uint32_t count = (uint32_t)unloadImages.count(); + assert(count != 0); + + // call each _dyld_register_func_for_remove_image function with each image + // do this before removing image from internal data structures so that the callback can query dyld about the image + const uint32_t existingNotifierCount = sUnloadNotifiers.count(); + NotifyFunc existingNotifiers[existingNotifierCount]; + NotifyFunc* existingNotifierArray = existingNotifiers; + sUnloadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) { + if ( index < existingNotifierCount ) + existingNotifierArray[index] = func; + }); + // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock) + for (uint32_t j=0; j < existingNotifierCount; ++j) { + NotifyFunc func = existingNotifierArray[j]; + for (uint32_t i=0; i < count; ++i) { + MachOParser parser(unloadImages[i].loadAddress); + log_notifications("dyld: remove notifier %p called with mh=%p\n", func, unloadImages[i].loadAddress); + func(unloadImages[i].loadAddress, parser.getSlide()); + } + } + + // call objc about images going away + if ( _objcNotifyUnmapped != nullptr ) { + for (uint32_t i=0; i < count; ++i) { + launch_cache::Image img(unloadImages[i].imageData); + if ( img.hasObjC() ) { + (*_objcNotifyUnmapped)(imagePath(unloadImages[i].imageData), unloadImages[i].loadAddress); + log_notifications("dyld: objc-unmapped-notifier called with image %p %s\n", unloadImages[i].loadAddress, imagePath(unloadImages[i].imageData)); + } + } + } + +#if !TARGET_IPHONE_SIMULATOR + // call kdebug trace for each image + if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) { + for (uint32_t i=0; i < count; ++i) { + launch_cache::Image img(unloadImages[i].imageData); + struct stat stat_buf; + fsid_t fsid = {{ 0, 0 }}; + fsobj_id_t fsobjid = { 0, 0 }; + if (stat(imagePath(unloadImages[i].imageData), &stat_buf) == 0 ) { + fsobjid = *(fsobj_id_t*)&stat_buf.st_ino; + fsid = {{ stat_buf.st_dev, 0 }}; + } + kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, img.uuid(), fsobjid, fsid, unloadImages[i].loadAddress); + } + } +#endif + + // remove each from sLoadedImages + for (uint32_t i=0; i < count; ++i) { + LoadedImage info(unloadImages[i].loadAddress, unloadImages[i].imageData); + sLoadedImages.remove(info); + } + + // sync to old all image infos struct + sLoadedImages.withReadLock(^{ + mirrorToOldAllImageInfos(); + }); + + // tell debugger about removed images + dyld_image_info oldDyldInfo[count]; + for (int i=0; i < count; ++i) { + launch_cache::Image img(unloadImages[i].imageData); + oldDyldInfo[i].imageLoadAddress = unloadImages[i].loadAddress; + oldDyldInfo[i].imageFilePath = imagePath(unloadImages[i].imageData); + oldDyldInfo[i].imageFileModDate = 0; + } + _oldAllImageInfos->notification(dyld_image_removing, count, oldDyldInfo); + + // unmap images + for (int i=0; i < count; ++i) { + launch_cache::Image img(unloadImages[i].imageData); + loader::unmapImage(unloadImages[i].imageData, unloadImages[i].loadAddress); + log_loads("dyld: unloaded %s\n", imagePath(unloadImages[i].imageData)); + } + + // notify any processes tracking loads in this process + notifyMonitorUnloads(unloadImages); +} + +void AllImages::setNeverUnload(const loader::ImageInfo& existingImage) +{ + sLoadedImages.forEachWithWriteLock(^(uint32_t index, dyld3::LoadedImage &value, bool &stop) { + if (value.image() == existingImage.imageData) { + value.setNeverUnload(); + stop = true; + } + }); +} + +uint32_t AllImages::count() const +{ + return sLoadedImages.count(); +} + + +launch_cache::Image AllImages::findByLoadOrder(uint32_t index, const mach_header** loadAddress) const +{ + __block const BinaryImage* foundImage = nullptr; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + if ( anIndex == index ) { + foundImage = loadedImage.image(); + *loadAddress = loadedImage.loadedAddress(); + stop = true; + } + }); + return launch_cache::Image(foundImage); +} + +launch_cache::Image AllImages::findByLoadAddress(const mach_header* loadAddress) const +{ + __block const BinaryImage* foundImage = nullptr; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + if ( loadedImage.loadedAddress() == loadAddress ) { + foundImage = loadedImage.image(); + stop = true; + } + }); + return launch_cache::Image(foundImage); +} + +bool AllImages::findIndexForLoadAddress(const mach_header* loadAddress, uint32_t& index) +{ + __block bool result = false; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + if ( loadedImage.loadedAddress() == loadAddress ) { + index = anIndex; + result = true; + stop = true; + } + }); + return result; +} + +void AllImages::forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const +{ + sLoadedImages.forEachWithReadLock(^(uint32_t imageIndex, const LoadedImage& loadedImage, bool& stop) { + handler(imageIndex, loadedImage.loadedAddress(), launch_cache::Image(loadedImage.image()), stop); + }); +} + +launch_cache::Image AllImages::findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions) const +{ + if ( _initialImages != nullptr ) { + // being called during libSystem initialization, so sLoadedImages not allocated yet + for (int i=0; i < _initialImages->count(); ++i) { + const loader::ImageInfo& entry = (*_initialImages)[i]; + launch_cache::Image anImage(entry.imageData); + if ( anImage.containsAddress(addr, entry.loadAddress, permissions) ) { + *loadAddress = entry.loadAddress; + return entry.imageData; + } + } + return launch_cache::Image(nullptr); + } + + // if address is in cache, do fast search of cache + if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) { + const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress; + if ( addr < (void*)((uint8_t*)_dyldCacheAddress+dyldCache->mappedSize()) ) { + size_t cacheVmOffset = ((uint8_t*)addr - (uint8_t*)_dyldCacheAddress); + DyldCacheParser cacheParser(dyldCache, false); + launch_cache::ImageGroup cachedDylibsGroup(cacheParser.cachedDylibsGroup()); + uint32_t mhCacheOffset; + uint8_t foundPermissions; + launch_cache::Image image(cachedDylibsGroup.findImageByCacheOffset(cacheVmOffset, mhCacheOffset, foundPermissions)); + if ( image.valid() ) { + *loadAddress = (mach_header*)((uint8_t*)_dyldCacheAddress + mhCacheOffset); + if ( permissions != nullptr ) + *permissions = foundPermissions; + return image; + } + } + } + + __block const BinaryImage* foundImage = nullptr; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + launch_cache::Image anImage(loadedImage.image()); + if ( anImage.containsAddress(addr, loadedImage.loadedAddress(), permissions) ) { + *loadAddress = loadedImage.loadedAddress(); + foundImage = loadedImage.image(); + stop = true; + } + }); + return launch_cache::Image(foundImage); +} + +const mach_header* AllImages::findLoadAddressByImage(const BinaryImage* targetImage) const +{ + __block const mach_header* foundAddress = nullptr; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + if ( targetImage == loadedImage.image() ) { + foundAddress = loadedImage.loadedAddress(); + stop = true; + } + }); + return foundAddress; +} + +const mach_header* AllImages::mainExecutable() const +{ + assert(_programVars != nullptr); + return (const mach_header*)_programVars->mh; +} + +launch_cache::Image AllImages::mainExecutableImage() const +{ + assert(_mainClosure != nullptr); + const launch_cache::Closure mainClosure(_mainClosure); + const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group(); + const uint32_t mainExecutableIndex = mainClosure.mainExecutableImageIndex(); + const dyld3::launch_cache::Image mainImage = mainGroup.image(mainExecutableIndex); + return mainImage; +} + +void AllImages::setMainPath(const char* path ) +{ + _mainExeOverridePath = path; +} + +const char* AllImages::imagePath(const BinaryImage* binImage) const +{ +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // on iOS and watchOS, apps may be moved on device after closure built + if ( _mainExeOverridePath != nullptr ) { + if ( binImage == mainExecutableImage().binaryData() ) + return _mainExeOverridePath; + } +#endif + launch_cache::Image image(binImage); + return image.path(); +} + +void AllImages::setInitialGroups() +{ + DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false); + sKnownGroups.addNoLock(cacheParser.cachedDylibsGroup()); + sKnownGroups.addNoLock(cacheParser.otherDylibsGroup()); + launch_cache::Closure closure(_mainClosure); + sKnownGroups.addNoLock(closure.group().binaryData()); +} + +const launch_cache::binary_format::ImageGroup* AllImages::cachedDylibsGroup() +{ + return sKnownGroups[0]; +} + +const launch_cache::binary_format::ImageGroup* AllImages::otherDylibsGroup() +{ + return sKnownGroups[1]; +} + +const AllImages::BinaryImageGroup* AllImages::mainClosureGroup() +{ + return sKnownGroups[2]; +} + +uint32_t AllImages::currentGroupsCount() const +{ + return sKnownGroups.count(); +} + +void AllImages::copyCurrentGroups(ImageGroupList& groups) const +{ + sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) { + if ( index < groups.count() ) + groups[index] = grpData; + }); +} + +void AllImages::copyCurrentGroupsNoLock(ImageGroupList& groups) const +{ + sKnownGroups.forEachNoLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) { + if ( index < groups.count() ) + groups[index] = grpData; + }); +} + +const mach_header* AllImages::alreadyLoaded(uint64_t inode, uint64_t mtime, bool bumpRefCount) +{ + __block const mach_header* result = nullptr; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + launch_cache::Image img(loadedImage.image()); + if ( img.validateUsingModTimeAndInode() ) { + if ( (img.fileINode() == inode) && (img.fileModTime() == mtime) ) { + result = loadedImage.loadedAddress(); + if ( bumpRefCount && !loadedImage.neverUnload() ) + incRefCount(loadedImage.loadedAddress()); + stop = true; + } + } + }); + return result; +} + +const mach_header* AllImages::alreadyLoaded(const char* path, bool bumpRefCount) +{ + __block const mach_header* result = nullptr; + uint32_t targetHash = launch_cache::ImageGroup::hashFunction(path); + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + launch_cache::Image img(loadedImage.image()); + if ( (img.pathHash() == targetHash) && (strcmp(path, imagePath(loadedImage.image())) == 0) ) { + result = loadedImage.loadedAddress(); + if ( bumpRefCount && !loadedImage.neverUnload() ) + incRefCount(loadedImage.loadedAddress()); + stop = true; + } + }); + if ( result == nullptr ) { + // perhaps there was an image override + launch_cache::ImageGroup mainGroup(mainClosureGroup()); + STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList); + copyCurrentGroups(currentGroupsList); + mainGroup.forEachImageRefOverride(currentGroupsList, ^(launch_cache::Image standardDylib, launch_cache::Image overrideDyilb, bool& stop) { + if ( strcmp(standardDylib.path(), path) == 0 ) { + result = alreadyLoaded(overrideDyilb.path(), bumpRefCount); + stop = true; + } + }); + } + return result; +} + +const mach_header* AllImages::alreadyLoaded(const BinaryImage* binImage, bool bumpRefCount) +{ + const mach_header* result = findLoadAddressByImage(binImage); + if ( result != nullptr ) { + launch_cache::Image loadedImage(binImage); + if ( bumpRefCount && !loadedImage.neverUnload() ) + incRefCount(result); + } + return result; +} + +void AllImages::incRefCount(const mach_header* loadAddress) +{ + __block bool found = false; + sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) { + if ( entry.loadAddress == loadAddress ) { + found = true; + entry.refCount += 1; + stop = true; + } + }); + if ( !found ) { + DlopenCount newEnty = { loadAddress, 1 }; + sDlopenRefCounts.add(newEnty); + } +} + +void AllImages::decRefCount(const mach_header* loadAddress) +{ + __block bool refCountNowZero = false; + sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) { + if ( entry.loadAddress == loadAddress ) { + entry.refCount -= 1; + stop = true; + if ( entry.refCount == 0 ) + refCountNowZero = true; + } + }); + if ( refCountNowZero ) { + DlopenCount delEnty = { loadAddress, 0 }; + sDlopenRefCounts.remove(delEnty); + garbageCollectImages(); + } +} + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +__NSObjectFileImage* AllImages::addNSObjectFileImage() +{ + // look for empty slot first + __block __NSObjectFileImage* result = nullptr; + sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) { + if ( (value.path == nullptr) && (value.memSource == nullptr) ) { + result = &value; + stop = true; + } + }); + if ( result != nullptr ) + return result; + + // otherwise allocate new slot + __NSObjectFileImage empty; + return sNSObjectFileImages.add(empty); +} + +bool AllImages::hasNSObjectFileImage(__NSObjectFileImage* ofi) +{ + __block bool result = false; + sNSObjectFileImages.forEachNoLock(^(uint32_t index, const __NSObjectFileImage& value, bool& stop) { + if ( &value == ofi ) { + result = ((value.memSource != nullptr) || (value.path != nullptr)); + stop = true; + } + }); + return result; +} + +void AllImages::removeNSObjectFileImage(__NSObjectFileImage* ofi) +{ + sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) { + if ( &value == ofi ) { + // mark slot as empty + ofi->path = nullptr; + ofi->memSource = nullptr; + ofi->memLength = 0; + ofi->loadAddress = nullptr; + ofi->binImage = nullptr; + stop = true; + } + }); +} +#endif + + +class VIS_HIDDEN Reaper +{ +public: + Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray); + void garbageCollect(); + void finalizeDeadImages(); + +private: + typedef launch_cache::binary_format::Image BinaryImage; + + void markDirectlyDlopenedImagesAsUsed(); + void markDependentOfInUseImages(); + void markDependentsOf(const LoadedImage*); + bool loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& index); + bool imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex); + uint32_t inUseCount(); + void dump(const char* msg); + + const LoadedImage** _unloadablesArray; + bool* _inUseArray; + uint32_t _arrayCount; + uint32_t _deadCount; +}; + +Reaper::Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray) + : _unloadablesArray(unloadables), _inUseArray(inUseArray),_arrayCount(count) +{ +} + + +bool Reaper::loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& foundIndex) +{ + for (uint32_t i=0; i < _arrayCount; ++i) { + if ( _unloadablesArray[i]->loadedAddress() == loadAddr ) { + foundIndex = i; + return true; + } + } + return false; +} + +bool Reaper::imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex) +{ + for (uint32_t i=0; i < _arrayCount; ++i) { + if ( _unloadablesArray[i]->image() == binImage ) { + foundIndex = i; + return true; + } + } + return false; +} + +void Reaper::markDirectlyDlopenedImagesAsUsed() +{ + sDlopenRefCounts.forEachWithReadLock(^(uint32_t refCountIndex, const dyld3::DlopenCount& dlEntry, bool& stop) { + if ( dlEntry.refCount != 0 ) { + uint32_t foundIndex; + if ( loadAddressIsUnloadable(dlEntry.loadAddress, foundIndex) ) { + _inUseArray[foundIndex] = true; + } + } + }); +} + +uint32_t Reaper::inUseCount() +{ + uint32_t count = 0; + for (uint32_t i=0; i < _arrayCount; ++i) { + if ( _inUseArray[i] ) + ++count; + } + return count; +} + +void Reaper::markDependentsOf(const LoadedImage* entry) +{ + const launch_cache::Image image(entry->image()); + STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList); + gAllImages.copyCurrentGroups(currentGroupsList); + image.forEachDependentImage(currentGroupsList, ^(uint32_t depIndex, dyld3::launch_cache::Image depImage, dyld3::launch_cache::Image::LinkKind kind, bool& stop) { + uint32_t foundIndex; + if ( !depImage.neverUnload() && imageIsUnloadable(depImage.binaryData(), foundIndex) ) { + _inUseArray[foundIndex] = true; + } + }); +} + +void Reaper::markDependentOfInUseImages() +{ + for (uint32_t i=0; i < _arrayCount; ++i) { + if ( _inUseArray[i] ) + markDependentsOf(_unloadablesArray[i]); + } +} + +void Reaper::dump(const char* msg) +{ + //log("%s:\n", msg); + for (uint32_t i=0; i < _arrayCount; ++i) { + dyld3::launch_cache::Image image(_unloadablesArray[i]->image()); + //log(" in-used=%d %s\n", _inUseArray[i], image.path()); + } +} + +void Reaper::garbageCollect() +{ + //dump("all unloadable images"); + + // mark all dylibs directly dlopen'ed as in use + markDirectlyDlopenedImagesAsUsed(); + + //dump("directly dlopen()'ed marked"); + + // iteratively mark dependents of in-use dylibs as in-use until in-use count stops changing + uint32_t lastCount = inUseCount(); + bool countChanged = false; + do { + markDependentOfInUseImages(); + //dump("dependents marked"); + uint32_t newCount = inUseCount(); + countChanged = (newCount != lastCount); + lastCount = newCount; + } while (countChanged); + + _deadCount = _arrayCount - inUseCount(); +} + +void Reaper::finalizeDeadImages() +{ + if ( _deadCount == 0 ) + return; + __cxa_range_t ranges[_deadCount]; + __cxa_range_t* rangesArray = ranges; + __block unsigned int rangesCount = 0; + for (uint32_t i=0; i < _arrayCount; ++i) { + if ( _inUseArray[i] ) + continue; + dyld3::launch_cache::Image image(_unloadablesArray[i]->image()); + image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) { + if ( permissions & VM_PROT_EXECUTE ) { + rangesArray[rangesCount].addr = (char*)(_unloadablesArray[i]->loadedAddress()) + vmOffset; + rangesArray[rangesCount].length = (size_t)vmSize; + ++rangesCount; + } + }); + } + __cxa_finalize_ranges(ranges, rangesCount); +} + + +// This function is called at the end of dlclose() when the reference count goes to zero. +// The dylib being unloaded may have brought in other dependent dylibs when it was loaded. +// Those dependent dylibs need to be unloaded, but only if they are not referenced by +// something else. We use a standard mark and sweep garbage collection. +// +// The tricky part is that when a dylib is unloaded it may have a termination function that +// can run and itself call dlclose() on yet another dylib. The problem is that this +// sort of gabage collection is not re-entrant. Instead a terminator's call to dlclose() +// which calls garbageCollectImages() will just set a flag to re-do the garbage collection +// when the current pass is done. +// +// Also note that this is done within the sLoadedImages writer lock, so any dlopen/dlclose +// on other threads are blocked while this garbage collections runs +// +void AllImages::garbageCollectImages() +{ + // if some other thread is currently GC'ing images, let other thread do the work + int32_t newCount = OSAtomicIncrement32(&_gcCount); + if ( newCount != 1 ) + return; + + do { + const uint32_t loadedImageCount = sLoadedImages.count(); + const LoadedImage* unloadables[loadedImageCount]; + bool unloadableInUse[loadedImageCount]; + const LoadedImage** unloadablesArray = unloadables; + bool* unloadableInUseArray = unloadableInUse; + __block uint32_t unloadableCount = 0; + // do GC with lock, so no other images can be added during GC + sLoadedImages.withReadLock(^() { + sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& entry, bool& stop) { + const launch_cache::Image image(entry.image()); + if ( !image.neverUnload() && !entry.neverUnload() ) { + unloadablesArray[unloadableCount] = &entry; + unloadableInUseArray[unloadableCount] = false; + //log("unloadable[%d] %p %s\n", unloadableCount, entry.loadedAddress(), image.path()); + ++unloadableCount; + } + }); + // make reaper object to do garbage collection and notifications + Reaper reaper(unloadableCount, unloadablesArray, unloadableInUseArray); + reaper.garbageCollect(); + + // FIXME: we should sort dead images so higher level ones are terminated first + + // call cxa_finalize_ranges of dead images + reaper.finalizeDeadImages(); + + // FIXME: call static terminators of dead images + + // FIXME: DOF unregister + }); + + //log("sLoadedImages before GC removals:\n"); + //sLoadedImages.dump(^(const LoadedImage& entry) { + // const launch_cache::Image image(entry.image()); + // log(" loadAddr=%p, path=%s\n", entry.loadedAddress(), image.path()); + //}); + + // make copy of LoadedImages we want to remove + // because unloadables[] points into ChunkVector we are shrinking + uint32_t removalCount = 0; + for (uint32_t i=0; i < unloadableCount; ++i) { + if ( !unloadableInUse[i] ) + ++removalCount; + } + if ( removalCount > 0 ) { + STACK_ALLOC_DYNARRAY(loader::ImageInfo, removalCount, unloadImages); + uint32_t removalIndex = 0; + for (uint32_t i=0; i < unloadableCount; ++i) { + if ( !unloadableInUse[i] ) { + unloadImages[removalIndex].loadAddress = unloadables[i]->loadedAddress(); + unloadImages[removalIndex].imageData = unloadables[i]->image(); + ++removalIndex; + } + } + // remove entries from sLoadedImages + removeImages(unloadImages); + + //log("sLoadedImages after GC removals:\n"); + //sLoadedImages.dump(^(const LoadedImage& entry) { + // const launch_cache::Image image(entry.image()); + // //log(" loadAddr=%p, path=%s\n", entry.loadedAddress(), image.path()); + //}); + } + + // if some other thread called GC during our work, redo GC on its behalf + newCount = OSAtomicDecrement32(&_gcCount); + } + while (newCount > 0); +} + + + +VIS_HIDDEN +const launch_cache::binary_format::Image* AllImages::messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount) +{ + __block const launch_cache::binary_format::Image* result = nullptr; + sKnownGroups.withWriteLock(^() { + ClosureBuffer::CacheIdent cacheIdent; + bzero(&cacheIdent, sizeof(cacheIdent)); + if ( _dyldCacheAddress != nullptr ) { + const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress; + dyldCache->getUUID(cacheIdent.cacheUUID); + cacheIdent.cacheAddress = (unsigned long)_dyldCacheAddress; + cacheIdent.cacheMappedSize = dyldCache->mappedSize(); + } + gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stopVariants) { + struct stat statBuf; + if ( stat(possiblePath, &statBuf) == 0 ) { + if ( S_ISDIR(statBuf.st_mode) ) { + log_apis(" %s: path is directory: %s\n", apiName, possiblePath); + if ( closuredErrorMessagesCount < 3 ) + closuredErrorMessages[closuredErrorMessagesCount++] = strdup("not a file"); + } + else { + // file exists, ask closured to build info for it + STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, sKnownGroups.countNoLock(), currentGroupsList); + gAllImages.copyCurrentGroupsNoLock(currentGroupsList); + dyld3::launch_cache::DynArray nonCacheGroupList(currentGroupsList.count()-2, ¤tGroupsList[2]); + const dyld3::launch_cache::binary_format::ImageGroup* closuredCreatedGroupData = nullptr; + ClosureBuffer closureBuilderInput(cacheIdent, path, nonCacheGroupList, gPathOverrides); + ClosureBuffer closureBuilderOutput = dyld3::closured_CreateImageGroup(closureBuilderInput); + if ( !closureBuilderOutput.isError() ) { + vm_protect(mach_task_self(), closureBuilderOutput.vmBuffer(), closureBuilderOutput.vmBufferSize(), false, VM_PROT_READ); + closuredCreatedGroupData = closureBuilderOutput.imageGroup(); + log_apis(" %s: closured built ImageGroup for path: %s\n", apiName, possiblePath); + sKnownGroups.addNoLock(closuredCreatedGroupData); + launch_cache::ImageGroup group(closuredCreatedGroupData); + result = group.imageBinary(0); + stopVariants = true; + } + else { + log_apis(" %s: closured failed for path: %s, error: %s\n", apiName, possiblePath, closureBuilderOutput.errorMessage()); + if ( closuredErrorMessagesCount < 3 ) { + closuredErrorMessages[closuredErrorMessagesCount++] = strdup(closureBuilderOutput.errorMessage()); + } + closureBuilderOutput.free(); + } + } + } + else { + log_apis(" %s: file does not exist for path: %s\n", apiName, possiblePath); + } + }); + }); + + return result; +} + +const AllImages::BinaryImage* AllImages::findImageInKnownGroups(const char* path) +{ + __block const AllImages::BinaryImage* result = nullptr; + sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const& grpData, bool& stop) { + launch_cache::ImageGroup group(grpData); + uint32_t ignore; + if ( const AllImages::BinaryImage* binImage = group.findImageByPath(path, ignore) ) { + result = binImage; + stop = true; + } + }); + return result; +} + +bool AllImages::imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const +{ + // check if statically determined in clousre that this can never be unloaded + if ( image.neverUnload() ) + return false; + + // check if some runtime decision made this be never-unloadable + __block bool foundAsNeverUnload = false; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + if ( loadedImage.loadedAddress() == loadAddress ) { + stop = true; + if ( loadedImage.neverUnload() ) + foundAsNeverUnload = true; + } + }); + if ( foundAsNeverUnload ) + return false; + + return true; +} + +void AllImages::addLoadNotifier(NotifyFunc func) +{ + // callback about already loaded images + const uint32_t existingCount = sLoadedImages.count(); + const mach_header* existingMHs[existingCount]; + const mach_header** existingArray = existingMHs; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + if ( anIndex < existingCount ) + existingArray[anIndex] = loadedImage.loadedAddress(); + }); + // we don't want to hold lock while calling out, so prebuild array (with lock) then do calls on that array (without lock) + for (uint32_t i=0; i < existingCount; i++) { + MachOParser parser(existingArray[i]); + log_notifications("dyld: add notifier %p called with mh=%p\n", func, existingArray[i]); + func(existingArray[i], parser.getSlide()); + } + + // add to list of functions to call about future loads + sLoadNotifiers.add(func); +} + +void AllImages::addUnloadNotifier(NotifyFunc func) +{ + // add to list of functions to call about future unloads + sUnloadNotifiers.add(func); +} + +void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmap) +{ + _objcNotifyMapped = map; + _objcNotifyInit = init; + _objcNotifyUnmapped = unmap; + + // callback about already loaded images + uint32_t maxCount = count(); + const char* pathsBuffer[maxCount]; + const mach_header* mhBuffer[maxCount]; + __block const char** paths = pathsBuffer; + __block const mach_header** mhs = mhBuffer; + __block uint32_t imagesWithObjC = 0; + sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) { + launch_cache::Image img(loadedImage.image()); + if ( img.hasObjC() ) { + mhs[imagesWithObjC] = loadedImage.loadedAddress(); + paths[imagesWithObjC] = imagePath(loadedImage.image()); + ++imagesWithObjC; + } + }); + if ( imagesWithObjC != 0 ) { + (*map)(imagesWithObjC, pathsBuffer, mhBuffer); + if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) { + for (uint32_t i=0; i < imagesWithObjC; ++i) { + log_notifications("dyld: objc-mapped: %p %s\n", mhBuffer[i], pathsBuffer[i]); + } + } + } +} + +void AllImages::vmAccountingSetSuspended(bool suspend) +{ +#if __arm__ || __arm64__ + // dyld should tell the kernel when it is doing fix-ups caused by roots + log_fixups("vm.footprint_suspend=%d\n", suspend); + int newValue = suspend ? 1 : 0; + int oldValue = 0; + size_t newlen = sizeof(newValue); + size_t oldlen = sizeof(oldValue); + sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen); +#endif +} + +void AllImages::applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray& initialImages) +{ + launch_cache::Closure mainClosure(closure); + launch_cache::ImageGroup mainGroup = mainClosure.group(); + DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false); + const launch_cache::binary_format::ImageGroup* dylibsGroupData = cacheParser.cachedDylibsGroup(); + launch_cache::ImageGroup dyldCacheDylibGroup(dylibsGroupData); + __block bool suspendedAccounting = false; + mainGroup.forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, const launch_cache::binary_format::Image* imageData, uint32_t imageOffset, bool& stop) { + bool foundInImages = false; + for (int i=0; i < initialImages.count(); ++i) { + if ( initialImages[i].imageData == imageData ) { + foundInImages = true; + uintptr_t replacement = (uintptr_t)(initialImages[i].loadAddress) + imageOffset; + dyldCacheDylibGroup.forEachDyldCachePatchLocation(_dyldCacheAddress, patchTableIndex, ^(uintptr_t* locationToPatch, uintptr_t addend, bool& innerStop) { + if ( !suspendedAccounting ) { + vmAccountingSetSuspended(true); + suspendedAccounting = true; + } + log_fixups("dyld: cache fixup: *%p = %p\n", locationToPatch, (void*)replacement); + *locationToPatch = replacement + addend; + }); + break; + } + } + if ( !foundInImages ) { + launch_cache::Image img(imageData); + log_fixups("did not find loaded image to patch into cache: %s\n", img.path()); + } + }); + if ( suspendedAccounting ) + vmAccountingSetSuspended(false); +} + +void AllImages::runLibSystemInitializer(const mach_header* libSystemAddress, const launch_cache::binary_format::Image* libSystemBinImage) +{ + // run all initializers in image + launch_cache::Image libSystemImage(libSystemBinImage); + libSystemImage.forEachInitializer(libSystemAddress, ^(const void* func) { + Initializer initFunc = (Initializer)func; + dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{ + initFunc(NXArgc, NXArgv, environ, appleParams, _programVars); + }); + log_initializers("called initialzer %p in %s\n", initFunc, libSystemImage.path()); + }); + + // mark libSystem.dylib as being init, so later recursive-init would re-run it + sLoadedImages.forEachWithWriteLock(^(uint32_t anIndex, LoadedImage& loadedImage, bool& stop) { + if ( loadedImage.loadedAddress() == libSystemAddress ) { + loadedImage.setState(LoadedImage::State::inited); + stop = true; + } + }); +} + +void AllImages::runInitialzersBottomUp(const mach_header* imageLoadAddress) +{ + launch_cache::Image topImage = findByLoadAddress(imageLoadAddress); + if ( topImage.isInvalid() ) + return; + + // closure contains list of intializers to run in-order + STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList); + copyCurrentGroups(currentGroupsList); + topImage.forEachInitBefore(currentGroupsList, ^(launch_cache::Image imageToInit) { + // find entry + __block LoadedImage* foundEntry = nullptr; + sLoadedImages.forEachWithReadLock(^(uint32_t index, const LoadedImage& entry, bool& stop) { + if ( entry.image() == imageToInit.binaryData() ) { + foundEntry = (LoadedImage*)&entry; + stop = true; + } + }); + assert(foundEntry != nullptr); + pthread_mutex_lock(&_initializerLock); + // Note, due to the large lock in dlopen, we can't be waiting on another thread + // here, but its possible that we are in a dlopen which is initialising us again + if ( foundEntry->state() == LoadedImage::State::beingInited ) { + log_initializers("dyld: already initializing '%s'\n", imagePath(imageToInit.binaryData())); + } + // at this point, the image is either initialized or not + // if not, initialize it on this thread + if ( foundEntry->state() == LoadedImage::State::uninited ) { + foundEntry->setState(LoadedImage::State::beingInited); + // release initializer lock, so other threads can run initializers + pthread_mutex_unlock(&_initializerLock); + // tell objc to run any +load methods in image + if ( (_objcNotifyInit != nullptr) && imageToInit.mayHavePlusLoads() ) { + log_notifications("dyld: objc-init-notifier called with mh=%p, path=%s\n", foundEntry->loadedAddress(), imagePath(imageToInit.binaryData())); + (*_objcNotifyInit)(imagePath(imageToInit.binaryData()), foundEntry->loadedAddress()); + } + // run all initializers in image + imageToInit.forEachInitializer(foundEntry->loadedAddress(), ^(const void* func) { + Initializer initFunc = (Initializer)func; + dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{ + initFunc(NXArgc, NXArgv, environ, appleParams, _programVars); + }); + log_initializers("dyld: called initialzer %p in %s\n", initFunc, imageToInit.path()); + }); + // reaquire initializer lock to switch state to inited + pthread_mutex_lock(&_initializerLock); + foundEntry->setState(LoadedImage::State::inited); + } + pthread_mutex_unlock(&_initializerLock); + }); +} + + +} // namespace dyld3 + + + + + + diff --git a/dyld/dyld3/AllImages.h b/dyld/dyld3/AllImages.h new file mode 100644 index 0000000..1f644bc --- /dev/null +++ b/dyld/dyld3/AllImages.h @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2017 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 __ALL_IMAGES_H__ +#define __ALL_IMAGES_H__ + +#include +#include + +#include "dyld_priv.h" + +#include "LaunchCache.h" +#include "Loading.h" + + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +// only in macOS and deprecated +struct VIS_HIDDEN __NSObjectFileImage +{ + const char* path = nullptr; + const void* memSource = nullptr; + size_t memLength = 0; + const mach_header* loadAddress = nullptr; + const dyld3::launch_cache::binary_format::Image* binImage = nullptr; +}; +#endif + +namespace dyld3 { + +class VIS_HIDDEN AllImages +{ +public: + typedef launch_cache::binary_format::Closure BinaryClosure; + typedef launch_cache::binary_format::ImageGroup BinaryImageGroup; + typedef launch_cache::binary_format::Image BinaryImage; + typedef launch_cache::ImageGroupList ImageGroupList; + typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide); + + void init(const BinaryClosure* closure, const void* dyldCacheLoadAddress, const char* dyldCachePath, + const dyld3::launch_cache::DynArray& initialImages); + void setMainPath(const char* path); + void applyInitialImages(); + + void addImages(const dyld3::launch_cache::DynArray& newImages); + void removeImages(const launch_cache::DynArray& unloadImages); + void setNeverUnload(const loader::ImageInfo& existingImage); + void applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray& initialImages); + void runInitialzersBottomUp(const mach_header* imageLoadAddress); + void setInitialGroups(); + + uint32_t count() const; + const BinaryImageGroup* cachedDylibsGroup(); + const BinaryImageGroup* otherDylibsGroup(); + const BinaryImageGroup* mainClosureGroup(); + const BinaryClosure* mainClosure() { return _mainClosure; } + uint32_t currentGroupsCount() const; + void copyCurrentGroups(ImageGroupList& groups) const; + + const BinaryImage* messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount); + + launch_cache::Image findByLoadOrder(uint32_t index, const mach_header** loadAddress) const; + launch_cache::Image findByLoadAddress(const mach_header* loadAddress) const; + launch_cache::Image findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions=nullptr) const; + const mach_header* findLoadAddressByImage(const BinaryImage*) const; + bool findIndexForLoadAddress(const mach_header* loadAddress, uint32_t& index); + void forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const; + + const mach_header* mainExecutable() const; + launch_cache::Image mainExecutableImage() const; + const void* cacheLoadAddress() const { return _dyldCacheAddress; } + const char* dyldCachePath() const { return _dyldCachePath; } + const char* imagePath(const BinaryImage*) const; + + const mach_header* alreadyLoaded(const char* path, bool bumpRefCount); + const mach_header* alreadyLoaded(const BinaryImage*, bool bumpRefCount); + const mach_header* alreadyLoaded(uint64_t inode, uint64_t mtime, bool bumpRefCount); + const BinaryImage* findImageInKnownGroups(const char* path); + + bool imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const; + void incRefCount(const mach_header* loadAddress); + void decRefCount(const mach_header* loadAddress); + + void addLoadNotifier(NotifyFunc); + void addUnloadNotifier(NotifyFunc); + void setObjCNotifiers(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped); + void notifyObjCUnmap(const char* path, const struct mach_header* mh); + + void runLibSystemInitializer(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage); + + void setOldAllImageInfo(dyld_all_image_infos* old) { _oldAllImageInfos = old; } + dyld_all_image_infos* oldAllImageInfo() const { return _oldAllImageInfos;} + + void notifyMonitorMain(); + void notifyMonitorLoads(const launch_cache::DynArray& newImages); + void notifyMonitorUnloads(const launch_cache::DynArray& unloadingImages); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + __NSObjectFileImage* addNSObjectFileImage(); + bool hasNSObjectFileImage(__NSObjectFileImage*); + void removeNSObjectFileImage(__NSObjectFileImage*); +#endif + + struct ProgramVars + { + const void* mh; + int* NXArgcPtr; + const char*** NXArgvPtr; + const char*** environPtr; + const char** __prognamePtr; + }; + void setProgramVars(ProgramVars* vars); + +private: + + typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars); + typedef const launch_cache::DynArray StartImageArray; + + void runInitialzersInImage(const mach_header* imageLoadAddress, const launch_cache::binary_format::Image* binImage); + void mirrorToOldAllImageInfos(); + void garbageCollectImages(); + void vmAccountingSetSuspended(bool suspend); + void copyCurrentGroupsNoLock(ImageGroupList& groups) const; + + const BinaryClosure* _mainClosure = nullptr; + const void* _dyldCacheAddress = nullptr; + const char* _dyldCachePath = nullptr; + StartImageArray* _initialImages = nullptr; + const char* _mainExeOverridePath = nullptr; + _dyld_objc_notify_mapped _objcNotifyMapped = nullptr; + _dyld_objc_notify_init _objcNotifyInit = nullptr; + _dyld_objc_notify_unmapped _objcNotifyUnmapped = nullptr; + ProgramVars* _programVars = nullptr; + dyld_all_image_infos* _oldAllImageInfos = nullptr; + dyld_image_info* _oldAllImageArray = nullptr; + uint32_t _oldArrayAllocCount = 0; + pthread_mutex_t _initializerLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + pthread_cond_t _initializerCondition= PTHREAD_COND_INITIALIZER; + int32_t _gcCount = 0; +}; + +extern AllImages gAllImages; + + +} // dyld3 + + +#endif // __ALL_IMAGES_H__ diff --git a/dyld/dyld3/ClosureBuffer.cpp b/dyld/dyld3/ClosureBuffer.cpp new file mode 100644 index 0000000..0231827 --- /dev/null +++ b/dyld/dyld3/ClosureBuffer.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include + +#include "ClosureBuffer.h" +#include "PathOverrides.h" + + +namespace dyld3 { + +TypedContentBuffer::TypedContentBuffer(size_t elementsCount, size_t elementsTotalSize) +{ + _size = elementsTotalSize + (elementsCount+1)*(sizeof(Element)+4); // worst case padding, plus "end" element + vm_address_t bufferAddress = 0; + assert(::vm_allocate(mach_task_self(), &bufferAddress, _size, VM_FLAGS_ANYWHERE) == 0); + _buffer = (Element*)bufferAddress; + _currentEnd = _buffer; + _readOnly = false; +} + +void TypedContentBuffer::free() +{ + if ( _buffer != nullptr ) + vm_deallocate(mach_task_self(), (long)_buffer, _size); + _buffer = nullptr; +} + +void TypedContentBuffer::addItem(uint32_t k, const void* content, size_t len) +{ + assert(!_readOnly); + assert(((char*)_currentEnd + len) < ((char*)_buffer + _size)); + _currentEnd->kind = k; + _currentEnd->contentLength = (uint32_t)len; + if ( len != 0 ) + memmove(&(_currentEnd->content), content, len); + size_t delta = (sizeof(Element) + len + 3) & (-4); + _currentEnd = (Element*)((char*)_currentEnd + delta); +} + +vm_address_t TypedContentBuffer::vmBuffer() const +{ + assert(_readOnly); + return (vm_address_t)_buffer; +} + +uint32_t TypedContentBuffer::vmBufferSize() const +{ + assert(_readOnly); + return (uint32_t)_size; +} + +void TypedContentBuffer::doneBuilding() +{ + _readOnly = true; +} + + +const TypedContentBuffer::Element* TypedContentBuffer::Element::next() const +{ + return (Element*)((char*)this + sizeof(Element) + ((contentLength + 3) & -4)); +} + +TypedContentBuffer::TypedContentBuffer(const void* buff, size_t buffSize) + : _size(buffSize), _buffer((Element*)buff), _currentEnd((Element*)((char*)buff+buffSize)), _readOnly(true) +{ +} + +unsigned TypedContentBuffer::count(uint32_t kind) const +{ + assert(_readOnly); + unsigned count = 0; + for (const Element* e = _buffer; e->kind != 0; e = e->next()) { + if ( e->kind == kind ) + ++count; + } + return count; +} + +void TypedContentBuffer::forEach(uint32_t kind, void (^callback)(const void* content, size_t length)) const +{ + assert(_readOnly); + for (const Element* e = _buffer; e->kind != 0; e = e->next()) { + if ( e->kind == kind ) { + callback(&(e->content), e->contentLength); + } + } +} + +#if !BUILDING_CLOSURED + +ClosureBuffer::ClosureBuffer(const CacheIdent& cacheIdent, const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars) + : TypedContentBuffer(2 + envVars.envVarCount() + groups.count(), computeSize(path, groups, envVars)) +{ + addItem(kindCacheIdent, &cacheIdent, sizeof(CacheIdent)); + addItem(kindTargetPath, path, strlen(path)+1); + envVars.forEachEnvVar(^(const char* envVar) { + addItem(kindEnvVar, envVar, strlen(envVar)+1); + }); + for (size_t i=0; i < groups.count(); ++i) { + launch_cache::ImageGroup group(groups[i]); + addItem(kindImageGroup, group.binaryData(), group.size()); + } + addItem(kindEnd, nullptr, 0); + doneBuilding(); +} + +size_t ClosureBuffer::computeSize(const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars) +{ + __block size_t result = sizeof(CacheIdent); + result += (strlen(path) + 1); + envVars.forEachEnvVar(^(const char* envVar) { + result += (strlen(envVar) + 1); + }); + for (size_t i=0; i < groups.count(); ++i) { + launch_cache::ImageGroup group(groups[i]); + result += group.size(); + } + return result; +} + +#endif + +ClosureBuffer::ClosureBuffer(const char* errorMessage) + : TypedContentBuffer(1, strlen(errorMessage+2)) +{ + addItem(kindErrorMessage, errorMessage, strlen(errorMessage)+1); + doneBuilding(); +} + +ClosureBuffer::ClosureBuffer(const launch_cache::BinaryImageGroupData* imageGroup) + : TypedContentBuffer(1, launch_cache::ImageGroup(imageGroup).size()) +{ + addItem(kindImageGroup, imageGroup, launch_cache::ImageGroup(imageGroup).size()); + doneBuilding(); +} + +ClosureBuffer::ClosureBuffer(const launch_cache::BinaryClosureData* closure) + : TypedContentBuffer(1, launch_cache::Closure(closure).size()) +{ + addItem(kindClosure, closure, launch_cache::Closure(closure).size()); + doneBuilding(); +} + + +ClosureBuffer::ClosureBuffer(const void* buff, size_t buffSize) + : TypedContentBuffer(buff, buffSize) +{ +} + +const ClosureBuffer::CacheIdent& ClosureBuffer::cacheIndent() const +{ + __block CacheIdent* ident = nullptr; + forEach(kindCacheIdent, ^(const void* content, size_t length) { + ident = (CacheIdent*)content; + assert(length == sizeof(CacheIdent)); + }); + assert(ident != nullptr); + return *ident; +} + +const char* ClosureBuffer::targetPath() const +{ + __block char* path = nullptr; + forEach(kindTargetPath, ^(const void* content, size_t length) { + path = (char*)content; + }); + assert(path != nullptr); + return path; +} + +uint32_t ClosureBuffer::envVarCount() const +{ + __block uint32_t count = 0; + forEach(kindEnvVar, ^(const void* content, size_t length) { + ++count; + }); + return count; +} + +void ClosureBuffer::copyImageGroups(const char* envVars[]) const +{ + __block uint32_t index = 0; + forEach(kindEnvVar, ^(const void* content, size_t length) { + envVars[index] = (char*)content; + ++index; + }); +} + +uint32_t ClosureBuffer::imageGroupCount() const +{ + __block uint32_t count = 0; + forEach(kindImageGroup, ^(const void* content, size_t length) { + ++count; + }); + return count; +} + +void ClosureBuffer::copyImageGroups(const launch_cache::BinaryImageGroupData* imageGroups[]) const +{ + __block uint32_t index = 0; + forEach(kindImageGroup, ^(const void* content, size_t length) { + imageGroups[index] = (launch_cache::BinaryImageGroupData*)content; + ++index; + }); +} + +bool ClosureBuffer::isError() const +{ + return ( errorMessage() != nullptr ); +} + +const char* ClosureBuffer::errorMessage() const +{ + __block char* message = nullptr; + forEach(kindErrorMessage, ^(const void* content, size_t length) { + message = (char*)content; + }); + return message; +} + +const launch_cache::BinaryClosureData* ClosureBuffer::closure() const +{ + __block const launch_cache::BinaryClosureData* result = nullptr; + forEach(kindClosure, ^(const void* content, size_t length) { + result = (const launch_cache::BinaryClosureData*)content; + }); + assert(result != nullptr); + return result; +} + + +const launch_cache::BinaryImageGroupData* ClosureBuffer::imageGroup() const +{ + __block const launch_cache::BinaryImageGroupData* result = nullptr; + forEach(kindImageGroup, ^(const void* content, size_t length) { + result = (const launch_cache::BinaryImageGroupData*)content; + }); + assert(result != nullptr); + return result; +} + + + + + + +} // namespace dyld3 + diff --git a/dyld/dyld3/ClosureBuffer.h b/dyld/dyld3/ClosureBuffer.h new file mode 100644 index 0000000..c1ab2c3 --- /dev/null +++ b/dyld/dyld3/ClosureBuffer.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2017 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_CLOSURE_BUFFER_H__ +#define __DYLD_CLOSURE_BUFFER_H__ + +#include "Logging.h" +#include "LaunchCache.h" +#include "PathOverrides.h" + +namespace dyld3 { + + +// simple class for packing typed content into a vm_allocated buffer +class VIS_HIDDEN TypedContentBuffer +{ +public: + // buffer creation + TypedContentBuffer(size_t elementsCount, size_t elementsTotalSize); + void addItem(uint32_t k, const void* content, size_t len); + void doneBuilding(); + vm_address_t vmBuffer() const; + uint32_t vmBufferSize() const; + + // buffer parsing + TypedContentBuffer(const void* buff, size_t buffSize); + unsigned count(uint32_t) const; + void forEach(uint32_t, void (^callback)(const void* content, size_t length)) const; + + void free(); + +private: + struct Element + { + uint32_t kind; + uint32_t contentLength; + uint8_t content[]; + + const Element* next() const; + }; + + size_t _size; + Element* _buffer; + Element* _currentEnd; + bool _readOnly; +}; + + +class VIS_HIDDEN ClosureBuffer : public TypedContentBuffer +{ +public: + + struct CacheIdent + { + uint8_t cacheUUID[16]; + uint64_t cacheAddress; + uint64_t cacheMappedSize; + }; + + // client creation + ClosureBuffer(const CacheIdent&, const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars); + + // closured creation + ClosureBuffer(const char* errorMessage); + ClosureBuffer(const launch_cache::BinaryImageGroupData* imageGroupResult); + ClosureBuffer(const launch_cache::BinaryClosureData* closureResult); + + // client extraction + bool isError() const; + const char* errorMessage() const; + const launch_cache::BinaryClosureData* closure() const; + const launch_cache::BinaryImageGroupData* imageGroup() const; + + // closure builder usage + ClosureBuffer(const void* buff, size_t buffSize); + const CacheIdent& cacheIndent() const; + const char* targetPath() const; + uint32_t envVarCount() const; + void copyImageGroups(const char* envVars[]) const; + uint32_t imageGroupCount() const; + void copyImageGroups(const launch_cache::BinaryImageGroupData* imageGroups[]) const; + +private: + enum { kindEnd=0, kindCacheIdent, kindTargetPath, kindEnvVar, kindImageGroup, kindClosure, kindErrorMessage }; + static size_t computeSize(const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars); + +}; + + + + +} // namespace dyld3 + +#endif // __DYLD_CLOSURE_BUFFER_H__ diff --git a/dyld/dyld3/CodeSigningTypes.h b/dyld/dyld3/CodeSigningTypes.h new file mode 100644 index 0000000..c84a708 --- /dev/null +++ b/dyld/dyld3/CodeSigningTypes.h @@ -0,0 +1,156 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2015 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 _CODE_SIGNING_TYPES_ +#define _CODE_SIGNING_TYPES_ + +#include +#include + + +// +// Magic numbers used by Code Signing +// +enum { + CSMAGIC_REQUIREMENT = 0xfade0c00, // single Requirement blob + CSMAGIC_REQUIREMENTS = 0xfade0c01, // Requirements vector (internal requirements) + CSMAGIC_CODEDIRECTORY = 0xfade0c02, // CodeDirectory blob + CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, // embedded form of signature data + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, // multi-arch collection of embedded signatures + CSMAGIC_BLOBWRAPPER = 0xfade0b01, // used for the cms blob +}; + +enum { + CS_PAGE_SIZE = 4096, + + CS_HASHTYPE_SHA1 = 1, + CS_HASHTYPE_SHA256 = 2, + CS_HASHTYPE_SHA256_TRUNCATED = 3, + + CS_HASH_SIZE_SHA1 = 20, + CS_HASH_SIZE_SHA256 = 32, + CS_HASH_SIZE_SHA256_TRUNCATED = 20, + + CSSLOT_CODEDIRECTORY = 0, + CSSLOT_INFOSLOT = 1, + CSSLOT_REQUIREMENTS = 2, + CSSLOT_RESOURCEDIR = 3, + CSSLOT_APPLICATION = 4, + CSSLOT_ENTITLEMENTS = 5, + CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, + CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = + CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, + CSSLOT_CMS_SIGNATURE = 0x10000, + + kSecCodeSignatureAdhoc = 2 +}; + +enum { + CS_REQUIRE_LV = 0x0002000 // require library validation +}; + +// +// Structure of a SuperBlob +// +struct CS_BlobIndex { + uint32_t type; // type of entry + uint32_t offset; // offset of entry +}; + +struct CS_SuperBlob { + uint32_t magic; // magic number + uint32_t length; // total length of SuperBlob + uint32_t count; // number of index entries following + CS_BlobIndex index[]; // (count) entries + // followed by Blobs in no particular order as indicated by offsets in index +}; + +// +// C form of a CodeDirectory. +// +struct CS_CodeDirectory { + uint32_t magic; // magic number (CSMAGIC_CODEDIRECTORY) */ + uint32_t length; // total length of CodeDirectory blob + uint32_t version; // compatibility version + uint32_t flags; // setup and mode flags + uint32_t hashOffset; // offset of hash slot element at index zero + uint32_t identOffset; // offset of identifier string + uint32_t nSpecialSlots; // number of special hash slots + uint32_t nCodeSlots; // number of ordinary (code) hash slots + uint32_t codeLimit; // limit to main image signature range + uint8_t hashSize; // size of each hash in bytes + uint8_t hashType; // type of hash (cdHashType* constants) + uint8_t platform; // platform identifier; zero if not platform binary + uint8_t pageSize; // log2(page size in bytes); 0 => infinite + uint32_t spare2; // unused (must be zero) + + char end_earliest[0]; + + /* Version 0x20100 */ + uint32_t scatterOffset; /* offset of optional scatter vector */ + char end_withScatter[0]; + + /* Version 0x20200 */ + uint32_t teamOffset; /* offset of optional team identifier */ + char end_withTeam[0]; + + /* Version 0x20300 */ + uint32_t spare3; /* unused (must be zero) */ + uint64_t codeLimit64; /* limit to main image signature range, 64 bits */ + char end_withCodeLimit64[0]; + + /* Version 0x20400 */ + uint64_t execSegBase; /* offset of executable segment */ + uint64_t execSegLimit; /* limit of executable segment */ + uint64_t execSegFlags; /* exec segment flags */ + char end_withExecSeg[0]; + + /* followed by dynamic content as located by offset fields above */ +}; + +struct CS_Blob { + uint32_t magic; // magic number + uint32_t length; // total length of blob +}; + +struct CS_RequirementsBlob { + uint32_t magic; // magic number + uint32_t length; // total length of blob + uint32_t data; // zero for dyld shared cache +}; + + +struct CS_Scatter { + uint32_t count; // number of pages; zero for sentinel (only) + uint32_t base; // first page number + uint64_t targetOffset; // byte offset in target + uint64_t spare; // reserved (must be zero) +}; + + +#endif // _CODE_SIGNING_TYPES_ + + + diff --git a/dyld/dyld3/Diagnostics.cpp b/dyld/dyld3/Diagnostics.cpp new file mode 100644 index 0000000..a8e4bca --- /dev/null +++ b/dyld/dyld3/Diagnostics.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include <_simple.h> +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Diagnostics.h" + +#if BUILDING_CACHE_BUILDER + #include + dispatch_queue_t sWarningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", NULL); +#endif + +Diagnostics::Diagnostics(bool verbose) +#if !DYLD_IN_PROCESS + : _verbose(verbose) + , _prefix("") +#endif +{ +} + +#if !DYLD_IN_PROCESS +Diagnostics::Diagnostics(const std::string& prefix, bool verbose) + : _verbose(verbose) + , _prefix(prefix) +{ +} +#endif + +Diagnostics::~Diagnostics() +{ + clearError(); +} + +void Diagnostics::error(const char* format, ...) +{ + _buffer = _simple_salloc(); + va_list list; + va_start(list, format); + _simple_vsprintf(_buffer, format, list); + va_end(list); + +#if !DYLD_IN_PROCESS + if ( !_verbose ) + return; + + char *output_string; + va_start(list, format); + vasprintf(&output_string, format, list); + va_end(list); + + if (_prefix.empty()) { + fprintf(stderr, "%s", output_string); + } else { + fprintf(stderr, "[%s] %s", _prefix.c_str(), output_string); + } +#endif +} + +bool Diagnostics::hasError() const +{ + return _buffer != nullptr; +} + +bool Diagnostics::noError() const +{ + return _buffer == nullptr; +} + +void Diagnostics::clearError() +{ + if ( _buffer ) + _simple_sfree(_buffer); + _buffer = nullptr; +} + +void Diagnostics::assertNoError() const +{ + if ( _buffer != nullptr ) + abort_report_np("%s", _simple_string(_buffer)); +} + +#if DYLD_IN_PROCESS +const char* Diagnostics::errorMessage() const +{ + return _simple_string(_buffer); +} + +#else +void Diagnostics::warning(const char* format, ...) +{ + _SIMPLE_STRING tmp = _simple_salloc(); + va_list list; + va_start(list, format); + _simple_vsprintf(tmp, format, list); + va_end(list); +#if BUILDING_CACHE_BUILDER + dispatch_sync(sWarningQueue, ^{ + _warnings.insert(_simple_string(tmp)); + }); +#else + _warnings.insert(_simple_string(tmp)); +#endif + _simple_sfree(tmp); +} + +void Diagnostics::verbose(const char* format, ...) +{ + if ( !_verbose ) + return; + + char* output_string; + va_list list; + va_start(list, format); + vasprintf(&output_string, format, list); + va_end(list); + + if (_prefix.empty()) { + fprintf(stderr, "%s", output_string); + } else { + fprintf(stderr, "[%s] %s", _prefix.c_str(), output_string); + } +} + +const std::string Diagnostics::prefix() const +{ + return _prefix; +} + +void Diagnostics::copy(const Diagnostics& other) +{ + if ( other.hasError() ) + error("%s", other.errorMessage().c_str()); + for (const std::string& warn : other.warnings()) + warning("%s", warn.c_str()); +} + +std::string Diagnostics::errorMessage() const +{ + if ( _buffer != nullptr ) + return _simple_string(_buffer); + else + return std::string(); +} + +const std::set Diagnostics::warnings() const +{ +#if BUILDING_CACHE_BUILDER + __block std::set retval; + dispatch_sync(sWarningQueue, ^{ + retval = _warnings; + }); + return retval; +#else + return _warnings; +#endif +} + +void Diagnostics::clearWarnings() +{ +#if BUILDING_CACHE_BUILDER + dispatch_sync(sWarningQueue, ^{ + _warnings.clear(); + }); +#else + _warnings.clear(); +#endif +} + +#endif + diff --git a/dyld/dyld3/Diagnostics.h b/dyld/dyld3/Diagnostics.h new file mode 100644 index 0000000..81fcf82 --- /dev/null +++ b/dyld/dyld3/Diagnostics.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2017 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 Diagnostics_h +#define Diagnostics_h + +#include + +#if !DYLD_IN_PROCESS +#include +#include +#include +#include +#endif + +#include "Logging.h" + + +class VIS_HIDDEN Diagnostics +{ +public: + Diagnostics(bool verbose=false); + ~Diagnostics(); + + void error(const char* format, ...) __attribute__((format(printf, 2, 3))); +#if !DYLD_IN_PROCESS + Diagnostics(const std::string& prefix, bool verbose=false); + + void warning(const char* format, ...) __attribute__((format(printf, 2, 3))); + void verbose(const char* format, ...) __attribute__((format(printf, 2, 3))); + void copy(const Diagnostics&); +#endif + + bool hasError() const; + bool noError() const; + void clearError(); + void assertNoError() const; + +#if DYLD_IN_PROCESS + const char* errorMessage() const; +#else + const std::string prefix() const; + std::string errorMessage() const; + const std::set warnings() const; + void clearWarnings(); +#endif + +private: + void* _buffer = nullptr; +#if !DYLD_IN_PROCESS + std::string _prefix; + std::set _warnings; + bool _verbose = false; +#endif +}; + + + + +#endif // Diagnostics_h diff --git a/dyld/dyld3/DyldCacheParser.cpp b/dyld/dyld3/DyldCacheParser.cpp new file mode 100644 index 0000000..4547027 --- /dev/null +++ b/dyld/dyld3/DyldCacheParser.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "DyldCacheParser.h" +#include "Trie.hpp" + + +namespace dyld3 { + +DyldCacheParser::DyldCacheParser(const DyldSharedCache* cacheHeader, bool rawFile) +{ + _data = (long)cacheHeader; + if ( rawFile ) + _data |= 1; +} + +const dyld_cache_header* DyldCacheParser::header() const +{ + return (dyld_cache_header*)(_data & -2); +} + +const DyldSharedCache* DyldCacheParser::cacheHeader() const +{ + return (DyldSharedCache*)header(); +} + +bool DyldCacheParser::cacheIsMappedRaw() const +{ + return (_data & 1); +} + + +uint64_t DyldCacheParser::dataRegionRuntimeVmOffset() const +{ + const dyld_cache_header* cacheHeader = header(); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset); + return (mappings[1].address - mappings[0].address); +} + +const dyld3::launch_cache::binary_format::ImageGroup* DyldCacheParser::cachedDylibsGroup() const +{ + const dyld_cache_header* cacheHeader = header(); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset); + + if ( cacheIsMappedRaw() ) { + // Whole file is mapped read-only. Use mapping file-offsets to find ImageGroup + uint64_t offsetInLinkEditRegion = (cacheHeader->dylibsImageGroupAddr - mappings[2].address); + return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + mappings[2].fileOffset + offsetInLinkEditRegion); + } + else { + // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find ImageGroup + return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + (cacheHeader->dylibsImageGroupAddr - mappings[0].address)); + } +} + + +const dyld3::launch_cache::binary_format::ImageGroup* DyldCacheParser::otherDylibsGroup() const +{ + const dyld_cache_header* cacheHeader = header(); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset); + + if ( cacheIsMappedRaw() ) { + // Whole file is mapped read-only. Use mapping file-offsets to find ImageGroup + uint64_t offsetInLinkEditRegion = (cacheHeader->otherImageGroupAddr - mappings[2].address); + return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + mappings[2].fileOffset + offsetInLinkEditRegion); + } + else { + // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find ImageGroup + return (dyld3::launch_cache::binary_format::ImageGroup*)((uint8_t*)cacheHeader + (cacheHeader->otherImageGroupAddr - mappings[0].address)); + } +} + +const dyld3::launch_cache::binary_format::Closure* DyldCacheParser::findClosure(const char* path) const +{ + const dyld_cache_header* cacheHeader = header(); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset); + + const uint8_t* executableTrieStart = nullptr; + const uint8_t* executableTrieEnd = nullptr; + const uint8_t* closuresStart = nullptr; + + if ( cacheIsMappedRaw() ) { + // Whole file is mapped read-only. Use mapping file-offsets to find trie and closures + executableTrieStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresTrieAddr - mappings[2].address + mappings[2].fileOffset; + executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize; + closuresStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresAddr - mappings[2].address + mappings[2].fileOffset; + } + else { + // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find trie and closures + uintptr_t slide = (uintptr_t)cacheHeader - (uintptr_t)(mappings[0].address); + executableTrieStart = (uint8_t*)(cacheHeader->progClosuresTrieAddr + slide); + executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize; + closuresStart = (uint8_t*)(cacheHeader->progClosuresAddr + slide); + } + Diagnostics diag; + const uint8_t* imageNode = dyld3::MachOParser::trieWalk(diag, executableTrieStart, executableTrieEnd, path); + if ( imageNode != NULL ) { + uint32_t closureOffset = (uint32_t)dyld3::MachOParser::read_uleb128(diag, imageNode, executableTrieEnd); + return (const dyld3::launch_cache::BinaryClosureData*)((uint8_t*)closuresStart + closureOffset); + } + return nullptr; +} + + +#if !DYLD_IN_PROCESS +void DyldCacheParser::forEachClosure(void (^handler)(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* cls)) const +{ + const dyld_cache_header* cacheHeader = header(); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset); + + const uint8_t* executableTrieStart = nullptr; + const uint8_t* executableTrieEnd = nullptr; + const uint8_t* closuresStart = nullptr; + + if ( cacheIsMappedRaw() ) { + // Whole file is mapped read-only. Use mapping file-offsets to find trie and closures + executableTrieStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresTrieAddr - mappings[2].address + mappings[2].fileOffset; + executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize; + closuresStart = (uint8_t*)cacheHeader + cacheHeader->progClosuresAddr - mappings[2].address + mappings[2].fileOffset; + } + else { + // Cache file is mapped in three non-contiguous ranges. Use mapping addresses to find trie and closures + uintptr_t slide = (uintptr_t)cacheHeader - (uintptr_t)(mappings[0].address); + executableTrieStart = (uint8_t*)(cacheHeader->progClosuresTrieAddr + slide); + executableTrieEnd = executableTrieStart + cacheHeader->progClosuresTrieSize; + closuresStart = (uint8_t*)(cacheHeader->progClosuresAddr + slide); + } + + std::vector closureEntries; + if ( Trie::parseTrie(executableTrieStart, executableTrieEnd, closureEntries) ) { + for (DylibIndexTrie::Entry& entry : closureEntries ) { + uint32_t offset = entry.info.index; + if ( offset < cacheHeader->progClosuresSize ) + handler(entry.name.c_str(), (const dyld3::launch_cache::binary_format::Closure*)(closuresStart+offset)); + } + } +} +#endif + + + + +} // namespace dyld3 + diff --git a/dyld/dyld3/DyldCacheParser.h b/dyld/dyld3/DyldCacheParser.h new file mode 100644 index 0000000..34deee4 --- /dev/null +++ b/dyld/dyld3/DyldCacheParser.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2017 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 DyldCacheParser_h +#define DyldCacheParser_h + +#include +#include +#include + +#include "Diagnostics.h" +#include "DyldSharedCache.h" +#include "LaunchCacheFormat.h" + +namespace dyld3 { + +class VIS_HIDDEN DyldCacheParser +{ +public: +#if !DYLD_IN_PROCESS + static bool isValidDyldCache(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables); +#endif + + DyldCacheParser(const DyldSharedCache* cacheHeader, bool rawFile); + const DyldSharedCache* cacheHeader() const; + bool cacheIsMappedRaw() const; + + + + // + // Get ImageGroup for cached dylibs built into this cache files + // + const dyld3::launch_cache::binary_format::ImageGroup* cachedDylibsGroup() const; + + + // + // Get ImageGroup for other OS dylibs and bundles built into this cache files + // + const dyld3::launch_cache::binary_format::ImageGroup* otherDylibsGroup() const; + + + // + // returns closure for given path, or nullptr if no closure found + // + const dyld3::launch_cache::binary_format::Closure* findClosure(const char* path) const; + + // + // returns what vmOffset of data (r/w) region from cache header will be when cache is used in a process + // + uint64_t dataRegionRuntimeVmOffset() const; + +#if !DYLD_IN_PROCESS + // + // Iterates over closure of OS programs built into shared cache + // + void forEachClosure(void (^handler)(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure*)) const; +#endif + +private: + const dyld_cache_header* header() const; + + long _data; // low bit means rawFile +}; + +} // namespace dyld3 + +#endif // DyldCacheParser_h diff --git a/dyld/dyld3/LaunchCache.h b/dyld/dyld3/LaunchCache.h new file mode 100644 index 0000000..447a2c7 --- /dev/null +++ b/dyld/dyld3/LaunchCache.h @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2017 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 LaunchCache_h +#define LaunchCache_h + + +#include +#include +#include +#include +#include +#include + +#if !DYLD_IN_PROCESS + #include + #include + #include + #include "shared-cache/DyldSharedCache.h" +#endif + +#include "Diagnostics.h" + +#define VIS_HIDDEN __attribute__((visibility("hidden"))) + + +namespace dyld3 { + +class DyldCacheParser; + +namespace launch_cache { + + +namespace binary_format { + struct Image; + struct ImageGroup; + union ImageRef; + struct Closure; + struct DiskImage; + struct CachedImage; + struct AllFixupsBySegment; + struct SegmentFixupsByPage; +} + +typedef binary_format::Image BinaryImageData; +typedef binary_format::ImageGroup BinaryImageGroupData; +typedef binary_format::Closure BinaryClosureData; + + +struct VIS_HIDDEN MemoryRange +{ + bool contains(const MemoryRange& other) const; + bool intersects(const MemoryRange& other) const; + + const void* address; + uint64_t size; +}; + + +class VIS_HIDDEN SlowLoadSet +{ +public: + SlowLoadSet(const BinaryImageData** start, const BinaryImageData** end) : _start(start), _end(end), _current(start) { } + bool contains(const BinaryImageData*); + bool add(const BinaryImageData*); + void forEach(void (^handler)(const BinaryImageData*)); + void forEach(void (^handler)(const BinaryImageData*, bool& stop)); + long count() const; +private: + const BinaryImageData** const _start; + const BinaryImageData** const _end; + const BinaryImageData** _current; +}; + +struct ImageGroup; + + +template +class VIS_HIDDEN DynArray +{ +public: + DynArray(uintptr_t count, T* storage) : _count(count), _elements(storage) { } +#if !DYLD_IN_PROCESS + DynArray(const std::vector& vec) : _count(vec.size()), _elements((T*)&vec[0]) { } +#endif + + T& operator[](size_t idx) { assert(idx < _count); return _elements[idx]; } + const T& operator[](size_t idx) const { assert(idx < _count); return _elements[idx]; } + uintptr_t count() const { return _count; } +private: + uintptr_t _count; + T* _elements; +}; + + +// STACK_ALLOC_DYNARRAY(foo, 10, myarray); +#define STACK_ALLOC_DYNARRAY(_type, _count, _name) \ + uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \ + dyld3::launch_cache::DynArray<_type> _name(_count, (_type*)__##_name##_array_alloc); + + +typedef DynArray ImageGroupList; + + +// In the pre-computed fixups for an Image, each fixup location is set to a TargetSymbolValue +// which is an abstract encoding of a resolved symbol in an image that can be turned into a +// real address once all ASLR slides are known. +struct VIS_HIDDEN TargetSymbolValue +{ +#if DYLD_IN_PROCESS + class LoadedImages + { + public: + virtual const uint8_t* dyldCacheLoadAddressForImage() = 0; + virtual const mach_header* loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup) = 0; + virtual void forEachImage(void (^handler)(uint32_t anIndex, const BinaryImageData*, const mach_header*, bool& stop)) = 0; + virtual void setAsNeverUnload(uint32_t anIndex) = 0; + }; + + uintptr_t resolveTarget(Diagnostics& diag, const ImageGroup& inGroup, LoadedImages& images) const; +#else + static TargetSymbolValue makeInvalid(); + static TargetSymbolValue makeAbsolute(uint64_t value); + static TargetSymbolValue makeSharedCacheOffset(uint32_t offset); + static TargetSymbolValue makeGroupValue(uint32_t groupIndex, uint32_t imageIndexInGroup, uint64_t offsetInImage, bool isIndirectGroupNum); + static TargetSymbolValue makeDynamicGroupValue(uint32_t imagePathPoolOffset, uint32_t imageSymbolPoolOffset, bool weakImport); + std::string asString(ImageGroup group) const; + bool operator==(const TargetSymbolValue& other) const { return (_data.raw == other._data.raw); } + bool isSharedCacheTarget(uint64_t& offsetInCache) const; + bool isGroupImageTarget(uint32_t& groupNum, uint32_t& indexInGroup, uint64_t& offsetInImage) const; + bool isInvalid() const; +#endif +private: + TargetSymbolValue(); + + enum Kinds { kindSharedCache, kindAbsolute, kindGroup, kindDynamicGroup }; + + + struct SharedCacheOffsetTarget { + uint64_t kind : 2, // kindSharedCache + offsetIntoCache : 62; + }; + struct AbsoluteTarget { + uint64_t kind : 2, // kindAbsolute + value : 62; + }; + struct GroupImageTarget { + uint64_t kind : 2, // kindGroup + isIndirectGroup : 1, // 0 => use groupNum directly. 1 => index indirect side table + groupNum : 7, // 0 not used, 1 => other dylibs, 2 => main closure, 3 => first dlopen group + indexInGroup : 12, + offsetInImage : 42; + }; + struct DynamicGroupImageTarget { + uint64_t kind : 2, // kindDynamicGroup + weakImport : 1, + imagePathOffset : 30, + symbolNameOffset: 31; + }; + union { + SharedCacheOffsetTarget sharedCache; + AbsoluteTarget absolute; + GroupImageTarget group; + DynamicGroupImageTarget dynamicGroup; + uint64_t raw; + } _data; + + static_assert(sizeof(_data) == 8, "Overflow in size of TargetSymbolValue"); +}; + + +struct VIS_HIDDEN Image +{ + enum class LinkKind { regular=0, weak=1, upward=2, reExport=3 }; + enum class FixupKind { rebase32, rebase64, bind32, bind64, rebaseText32, bindText32, bindTextRel32, bindImportJmp32 }; + + Image(const BinaryImageData* binaryData) : _binaryData(binaryData) { } + + bool valid() const { return (_binaryData != nullptr); } + const BinaryImageData* binaryData() const { return _binaryData; } + const ImageGroup group() const; + uint32_t maxLoadCount() const; + const char* path() const; + const char* leafName() const; + uint32_t pathHash() const; + const uuid_t* uuid() const; + bool isInvalid() const; + bool hasObjC() const; + bool isBundle() const; + bool hasWeakDefs() const; + bool mayHavePlusLoads() const; + bool hasTextRelocs() const; + bool neverUnload() const; + bool cwdMustBeThisDir() const; + bool isPlatformBinary() const; + bool overridableDylib() const; + bool validateUsingModTimeAndInode() const; + bool validateUsingCdHash() const; + uint64_t fileModTime() const; + uint64_t fileINode() const; + const uint8_t* cdHash16() const; + void forEachDependentImage(const ImageGroupList& groups, void (^handler)(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop)) const; +#if !DYLD_IN_PROCESS + bool recurseAllDependentImages(const ImageGroupList& groups, std::unordered_set& allDependents) const; +#endif + bool recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, + void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const; + bool containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions) const; + bool segmentHasFixups(uint32_t segIndex) const; + void forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const; + void forEachInitBefore(const ImageGroupList& groups, void (^handler)(Image imageToInit)) const; + void forEachInitBefore(void (^handler)(binary_format::ImageRef imageToInit)) const; + void forEachDOF(const void* imageLoadAddress, void (^handler)(const void* initializer)) const; + + bool isDiskImage() const; + + // the following are only valid if isDiskImage() returns false + const binary_format::CachedImage* asCachedImage() const; + uint32_t cacheOffset() const; + uint32_t patchStartIndex() const; + uint32_t patchCount() const; + + + // the following are only valid if isDiskImage() returns true + const binary_format::DiskImage* asDiskImage() const; + uint64_t sliceOffsetInFile() const; + bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const; + bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const; + uint64_t vmSizeToMap() const; + void forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const; + void forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const; + void forEachFixup(uint32_t segIndex, MemoryRange segContent, + void (^handler)(uint64_t segOffset, FixupKind kind, TargetSymbolValue value, bool& stop)) const; + +#if !DYLD_IN_PROCESS + void printAsJSON(const ImageGroupList& groupList, bool printFixups=false, bool printDependentsDetails=false, FILE* out=stdout) const; +#endif + +// todo: fairPlayTextPages + +private: + friend struct ImageGroup; + friend struct Closure; + + bool recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, bool& stopped, + void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const; + uint32_t pageSize() const; + const binary_format::SegmentFixupsByPage* segmentFixups(uint32_t segIndex) const; + static void forEachFixup(const uint8_t* pageFixups, const void* segContent, uint32_t& offset, uint32_t& ordinal, + void (^handler)(uint32_t pageOffset, FixupKind kind, uint32_t targetOrdinal, bool& stop)); + static Image resolveImageRef(const ImageGroupList& groups, binary_format::ImageRef ref, bool applyOverrides=true); + + + const BinaryImageData* _binaryData; +}; + + +struct VIS_HIDDEN ImageGroup +{ + ImageGroup(const BinaryImageGroupData* binaryData) : _binaryData(binaryData) { } + + size_t size() const; + uint32_t imageCount() const; + uint32_t groupNum() const; + bool dylibsExpectedOnDisk() const; + const Image image(uint32_t index) const; + uint32_t indexInGroup(const BinaryImageData* image) const; + const BinaryImageData* findImageByPath(const char* path, uint32_t& foundIndex) const; + const BinaryImageData* findImageByCacheOffset(size_t cacheVmOffset, uint32_t& mhCacheOffset, uint8_t& foundPermissions) const; + const BinaryImageData* imageBinary(uint32_t index) const; + binary_format::ImageRef dependentPool(uint32_t index) const; + const BinaryImageGroupData* binaryData() const { return _binaryData; } + const char* stringFromPool(uint32_t offset) const; + uint32_t indirectGroupNum(uint32_t index) const; + void forEachImageRefOverride(void (^handler)(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDyilbRef, bool& stop)) const; + void forEachImageRefOverride(const ImageGroupList& groupList, void (^handler)(Image standardDylib, Image overrideDyilb, bool& stop)) const; + void forEachAliasOf(uint32_t imageIndex, void (^handler)(const char* aliasPath, uint32_t aliasPathHash, bool& stop)) const; +#if DYLD_IN_PROCESS + void forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, const BinaryImageData* image, uint32_t imageOffset, bool& stop)) const; + void forEachDyldCachePatchLocation(const void* dyldCacheLoadAddress, uint32_t patchTargetIndex, + void (^handler)(uintptr_t* locationToPatch, uintptr_t addend, bool& stop)) const; +#else + void forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop)) const; + void forEachDyldCachePatchLocation(const DyldCacheParser& cacheParser, void (^handler)(uint32_t targetCacheOffset, const std::vector& usesPointersCacheOffsets, bool& stop)) const; + bool hasPatchTableIndex(uint32_t targetCacheOffset, uint32_t& index) const; +#endif + + static uint32_t hashFunction(const char* s); +#if !DYLD_IN_PROCESS + void printAsJSON(const ImageGroupList& groupList, bool printFixups=false, bool printDependentsDetails=false, FILE* out=stdout) const; + void printStatistics(FILE* out=stderr) const; +#endif + +private: + friend struct Image; + + const char* stringPool() const; + uint32_t stringPoolSize() const; + const uint64_t* segmentPool(uint32_t index) const; + const binary_format::AllFixupsBySegment* fixUps(uint32_t offset) const; + const TargetSymbolValue* targetValuesArray() const; + uint32_t targetValuesCount() const; + uint32_t initializersPoolCount() const; + const uint32_t* initializerOffsetsPool() const; + const uint32_t initializerOffsetsCount() const; + const binary_format::ImageRef* intializerListPool() const; + const uint32_t intializerListPoolCount() const; + const uint32_t* dofOffsetsPool() const; + const uint32_t dofOffsetsCount() const; + const uint32_t* indirectGroupNumsPool() const; + const uint32_t indirectGroupNumsCount() const; + void forEachDyldCachePatch(uint32_t patchTargetIndex, uint32_t cacheDataVmOffset, + void (^handler)(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop)) const; + + const BinaryImageGroupData* _binaryData; +}; + + + +struct VIS_HIDDEN Closure +{ + Closure(const BinaryClosureData* binaryData); + + size_t size() const; + const uuid_t* dyldCacheUUID() const; + const uint8_t* cdHash() const; + uint32_t initialImageCount() const; + uint32_t mainExecutableImageIndex() const; + uint32_t mainExecutableEntryOffset() const; + bool mainExecutableUsesCRT() const; + bool isRestricted() const; + bool usesLibraryValidation() const; + const BinaryImageData* libSystem(const ImageGroupList& groups); + const BinaryImageData* libDyld(const ImageGroupList& groups); + uint32_t libdyldVectorOffset() const; + const ImageGroup group() const; + const BinaryClosureData* binaryData() const { return _binaryData; } + void forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const; + void forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const; + +#if !DYLD_IN_PROCESS + void printAsJSON(const ImageGroupList& groupList, bool printFixups=true, bool printDependentsDetails=false, FILE* out=stdout) const; + void printStatistics(FILE* out=stderr) const; +#endif + +private: + const BinaryClosureData* _binaryData; +}; + + + + + + +} // namespace launch_cache +} // namespace dyld3 + + +#endif // LaunchCache_h + + diff --git a/dyld/dyld3/LaunchCacheFormat.h b/dyld/dyld3/LaunchCacheFormat.h new file mode 100644 index 0000000..bea67ad --- /dev/null +++ b/dyld/dyld3/LaunchCacheFormat.h @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2017 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 LaunchCacheFormat_h +#define LaunchCacheFormat_h + + +#include +#include +#include +#include + +#include "LaunchCache.h" + + +namespace dyld3 { +namespace launch_cache { +namespace binary_format { + + +// bump this number each time binary format changes +enum { kFormatVersion = 8 }; + +union VIS_HIDDEN ImageRef { + ImageRef() : val(0xFFFFFFFF) { } + ImageRef(uint8_t kind, uint32_t groupNum, uint32_t indexInGroup) : _linkKind(kind), _groupNum(groupNum), _indexInGroup(indexInGroup) { + assert(groupNum < (1 << 18)); + assert(indexInGroup < (1 << 12)); + } + uint8_t kind() const { return _linkKind; } + uint32_t groupNum() const { return _groupNum; } + uint32_t indexInGroup() const { return _indexInGroup; } + uint16_t value() const { return val; } + void clearKind() { _linkKind = 0; } + + bool operator==(const ImageRef& rhs) const { + return (val == rhs.val); + } + bool operator!=(const ImageRef& rhs) const { + return (val != rhs.val); + } + static ImageRef weakImportMissing(); + static ImageRef makeEmptyImageRef() { return ImageRef(); } + +private: + ImageRef(uint32_t v) : val(v) { } + + uint32_t val; + struct { + uint32_t _linkKind : 2, // Image::LinkKind + _groupNum : 18, // 0 => cached dylib group, 1 => other dylib group, 2 => main closure, etc + _indexInGroup : 12; // max 64K images in group + }; +}; + + + + +// In disk based images, all segments are multiples of page size +// This struct just tracks the size (disk and vm) of each segment. +// This is compact for most every image which have contiguous segments. +// If the image does not have contiguous segments (rare), an extra +// DiskSegment is inserted with the paddingNotSeg bit set. +struct DiskSegment +{ + uint64_t filePageCount : 30, + vmPageCount : 30, + permissions : 3, + paddingNotSeg : 1; +}; + + +// In cache DATA_DIRTY is not page aligned or sized +// This struct allows segments with any alignment and up to 256MB in size +struct DyldCacheSegment +{ + uint64_t cacheOffset : 32, + size : 28, + permissions : 4; +}; + +// When an Image is built on the device, the mtime and inode are recorded. +// When built off device, the first 16 bytes of SHA1 of CodeDirectory is recorded. +union FileInfo +{ + struct { + uint64_t mtime; + uint64_t inode; + } statInfo; + struct { + uint8_t bytes[16]; + } cdHash16; +}; + +struct Image +{ + uint32_t isDiskImage : 1, // images are DiskImage - not Image + isInvalid : 1, // an error occurred creating the info for this image + has16KBpages : 1, + hasTextRelocs : 1, + hasObjC : 1, + mayHavePlusLoads : 1, + isEncrypted : 1, // image is DSMOS or FairPlay encrypted + hasWeakDefs : 1, + neverUnload : 1, + cwdSameAsThis : 1, // dylibs use file system relative paths, cwd must be main's dir + isPlatformBinary : 1, // part of OS - can be loaded into LV process + isBundle : 1, + overridableDylib : 1, // only applicable to group 0 + padding : 7, + maxLoadCount : 12; + int32_t groupOffset; // back pointer to containing ImageGroup (from start of Image) + uint32_t pathPoolOffset; + uint32_t pathHash; + FileInfo fileInfo; + uuid_t uuid; + uint16_t dependentsArrayStartIndex; + uint16_t dependentsArrayCount; + uint16_t segmentsArrayStartIndex; + uint16_t segmentsArrayCount; + uint16_t initBeforeArrayStartIndex; + uint16_t initBeforeArrayCount; + uint16_t initOffsetsArrayStartIndex; + uint16_t initOffsetsArrayCount; + uint16_t dofOffsetsArrayStartIndex; + uint16_t dofOffsetsArrayCount; +}; + +// an image in the dyld shared cache +struct CachedImage : public Image +{ + uint32_t patchStartIndex; + uint32_t patchCount; +}; + +// an image not in the dyld shared cache (loaded from disk at runtime) +struct DiskImage : public Image +{ + uint32_t totalVmPages; + uint32_t sliceOffsetIn4K; + uint32_t codeSignFileOffset; + uint32_t codeSignFileSize; + uint32_t fixupsPoolOffset : 28, // offset in ImageGroup's pool for AllFixupsBySegment + fixupsPoolSegCount : 4; // count of segments in AllFixupsBySegment for this image + uint32_t fairPlayTextPageCount : 28, + fairPlayTextStartPage : 4; + uint32_t targetsArrayStartIndex; // index in ImageGroup's pool of OrdinalEntry + uint32_t targetsArrayCount; +}; + + +// if an Image has an alias (symlink to it), the Image does not record the alias, but the ImageGroup does +struct AliasEntry +{ + uint32_t aliasHash; + uint32_t imageIndexInGroup; + uint32_t aliasOffsetInStringPool; +}; + +// each DiskImage points to an array of these, one per segment with fixups +struct AllFixupsBySegment +{ + uint32_t segIndex : 4, + offset : 28; // from start of AllFixupsBySegment to this seg's SegmentFixupsByPage +}; + + +// This struct is suitable for passing into kernel when kernel supports fixups on page-in. +struct SegmentFixupsByPage +{ + uint32_t size; // of this struct, including fixup opcodes + uint32_t pageSize; // 0x1000 or 0x4000 + uint32_t pageCount; + uint32_t pageInfoOffsets[1]; // array size is pageCount + // each page info is a FixUpOpcode[] +}; + +enum class FixUpOpcode : uint8_t { + done = 0x00, +// apply = 0x10, + rebase32 = 0x10, // add32 slide at current pageOffset, increment pageOffset by 4 + rebase64 = 0x11, // add64 slide at current pageOffset, increment pageOffset by 8 + bind32 = 0x12, // set 32-bit ordinal value at current pageOffset, increment pageOffset by 4 + bind64 = 0x13, // set 64-bit ordinal value at current pageOffset, increment pageOffset by 8 + rebaseText32 = 0x14, // add32 slide at current text pageOffset, increment pageOffset by 4 + bindText32 = 0x15, // set 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 + bindTextRel32 = 0x16, // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 CALL to dylib) + bindImportJmp32 = 0x17, // set delta to 32-bit ordinal value at current text pageOffset, increment pageOffset by 4 (i386 JMP to dylib) +// fixupChain64 = 0x18, // current page offset is start of a chain of locations to fix up +// adjPageOffset = 0x20, + setPageOffset = 0x20, // low 4-bits is amount to increment (1 to 15). If zero, then add next ULEB (note: can set offset for unaligned pointer) + incPageOffset = 0x30, // low 4-bits *4 is amount to increment (4 to 60). If zero, then add next ULEB * 4 +// adjOrdinal = 0x40, + setOrdinal = 0x40, // low 4-bits is ordinal (1-15). If zero, then ordinal is next ULEB + incOrdinal = 0x50, // low 4-bits is ordinal inc amount (1-15). If zero, then ordinal is next ULEB + repeat = 0x60 // low 5-bits is how many next bytes to repeat. next ULEB is repeat count +}; + +// If a closure uses DYLD_LIBRARY_PATH to override an OS dylib, there is an +// ImageRefOverride entry to redirect uses of the OS dylib. +struct ImageRefOverride +{ + ImageRef standardDylib; + ImageRef overrideDylib; +}; + +// If a closure interposes on, or has a dylib that overrides, something in the dyld shared cache, +// then closure's ImageGroup contains an array of these +struct DyldCacheOverride +{ + uint64_t patchTableIndex : 24, // index into PatchTable array of group 0 + imageIndex : 8, // index in this group (2) of what to replace with + imageOffset : 32; // offset within image to override something in cache +}; + + +// The ImageGroup for the dyld shared cache dylibs contains and array of these +// with one entry for each symbol in a cached dylib that is used by some other cached dylib. +struct PatchTable +{ + uint32_t targetCacheOffset; // delta from the base address of the cache to the address of the symbol to patch + uint32_t offsetsStartIndex; // index into the PatchOffset array of first location to patch, last offset has low bit set +}; + +struct PatchOffset +{ + uint32_t last : 1, + hasAddend : 1, + dataRegionOffset : 30; +}; + +struct ImageGroup +{ + uint32_t imagesEntrySize : 8, + dylibsExpectedOnDisk : 1, + imageFileInfoIsCdHash : 1, + padding : 14; + uint32_t groupNum; + uint32_t imagesPoolCount; + uint32_t imagesPoolOffset; // offset to array of Image or DiskImage + uint32_t imageAliasCount; + uint32_t imageAliasOffset; // offset to array of AliasEntry + uint32_t segmentsPoolCount; + uint32_t segmentsPoolOffset; // offset to array of Segment or DyldCacheSegment + uint32_t dependentsPoolCount; + uint32_t dependentsPoolOffset; // offset to array of ImageRef + uint32_t intializerOffsetPoolCount; + uint32_t intializerOffsetPoolOffset; // offset to array of uint32_t + uint32_t intializerListPoolCount; + uint32_t intializerListPoolOffset; // offset to array of ImageRef + uint32_t targetsPoolCount; + uint32_t targetsOffset; // offset to array of TargetSymbolValue + uint32_t fixupsPoolSize; + uint32_t fixupsOffset; // offset to list of AllFixupsBySegment + uint32_t cachePatchTableCount; + uint32_t cachePatchTableOffset; // offset to array of PatchTable (used only in group 0) + uint32_t cachePatchOffsetsCount; + uint32_t cachePatchOffsetsOffset; // offset to array of PatchOffset cache offsets (used only in group 0) + uint32_t symbolOverrideTableCount; + uint32_t symbolOverrideTableOffset; // offset to array of DyldCacheOverride (used only in group 2) + uint32_t imageOverrideTableCount; + uint32_t imageOverrideTableOffset; // offset to array of ImageRefOverride (used only in group 2) + uint32_t dofOffsetPoolCount; + uint32_t dofOffsetPoolOffset; // offset to array of uint32_t + uint32_t indirectGroupNumPoolCount; + uint32_t indirectGroupNumPoolOffset; // offset to array of uint32_t + uint32_t stringsPoolSize; + uint32_t stringsPoolOffset; + // Image array + // Alias array + // Segment array + // ImageRef array + // Initializer offsets array + // Initializer ImageRef array + // TargetSymbolValue array + // AllFixupsBySegment pool + // PatchTable array + // PatchOffset array + // DyldCacheOverride array + // ImageRefOverride array + // string pool + // DOF offsets array +}; + + +struct Closure +{ + enum { magicV1 = 0x31646c6e }; + + uint32_t magic; + uint32_t usesCRT : 1, + isRestricted : 1, + usesLibraryValidation : 1, + padding : 29; + uint32_t missingFileComponentsOffset; // offset to array of 16-bit string pool offset of path components + uint32_t dyldEnvVarsOffset; + uint32_t dyldEnvVarsCount; + uint32_t stringPoolOffset; + uint32_t stringPoolSize; + ImageRef libSystemRef; + ImageRef libDyldRef; + uint32_t libdyldVectorOffset; + uint32_t mainExecutableIndexInGroup; + uint32_t mainExecutableEntryOffset; + uint32_t initialImageCount; + uuid_t dyldCacheUUID; // all zero if this closure is embedded in a dyld cache + uint8_t mainExecutableCdHash[20]; // or UUID if not code signed + ImageGroup group; + // MissingFile array + // env vars array + // string pool +}; + + + +} // namespace binary_format + +} // namespace launch_cache +} // namespace dyld + + +#endif // LaunchCacheFormat_h + + diff --git a/dyld/dyld3/LaunchCachePrinter.cpp b/dyld/dyld3/LaunchCachePrinter.cpp new file mode 100644 index 0000000..f5efd16 --- /dev/null +++ b/dyld/dyld3/LaunchCachePrinter.cpp @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include + +#include +#include +#include + +#include "LaunchCache.h" +#include "LaunchCacheFormat.h" + +#if !DYLD_IN_PROCESS + +namespace dyld3 { +namespace launch_cache { + +struct Node +{ + std::string value; + std::map map; + std::vector array; +}; + +static std::string hex(uint64_t value) { + char buff[64]; + sprintf(buff, "0x%llX", value); + return buff; +} + +static std::string hex5(uint64_t value) { + char buff[64]; + sprintf(buff, "0x%05llX", value); + return buff; +} + +static std::string decimal(uint64_t value) { + char buff[64]; + sprintf(buff, "%llu", value); + return buff; +} + +static Node buildImageNode(const Image& image, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails) +{ + __block Node imageNode; + + if ( image.isInvalid() ) + return imageNode; + + const ImageGroup group = image.group(); + imageNode.map["path"].value = image.path(); + __block Node imageAliases; + group.forEachAliasOf(group.indexInGroup(image.binaryData()), ^(const char* aliasPath, uint32_t aliasPathHash, bool& stop) { + Node anAlias; + anAlias.value = aliasPath; + imageAliases.array.push_back(anAlias); + }); + if ( !imageAliases.array.empty() ) + imageNode.map["aliases"] = imageAliases; + uuid_string_t uuidStr; + uuid_unparse(*image.uuid(), uuidStr); + imageNode.map["uuid"].value = uuidStr; + imageNode.map["has-objc"].value = (image.hasObjC() ? "true" : "false"); + imageNode.map["has-weak-defs"].value = (image.hasWeakDefs() ? "true" : "false"); + imageNode.map["never-unload"].value = (image.neverUnload() ? "true" : "false"); + imageNode.map["platform-binary"].value = (image.isPlatformBinary() ? "true" : "false"); + if ( group.groupNum() == 0 ) + imageNode.map["overridable-dylib"].value = (image.overridableDylib() ? "true" : "false"); + if ( image.cwdMustBeThisDir() ) + imageNode.map["cwd-must-be-this-dir"].value = "true"; + if ( image.isDiskImage() ) { + uint32_t csFileOffset; + uint32_t csSize; + if ( image.hasCodeSignature(csFileOffset, csSize) ) { + imageNode.map["code-sign-location"].map["offset"].value = hex(csFileOffset); + imageNode.map["code-sign-location"].map["size"].value = hex(csSize); + } + uint32_t fpTextOffset; + uint32_t fpSize; + if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) { + imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset); + imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize); + } + if ( image.validateUsingModTimeAndInode() ) { + imageNode.map["file-mod-time"].value = hex(image.fileModTime()); + imageNode.map["file-inode"].value = hex(image.fileINode()); + } + else { + const uint8_t* cdHash = image.cdHash16(); + std::string cdHashStr; + cdHashStr.reserve(32); + for (int j=0; j < 16; ++j) { + uint8_t byte = cdHash[j]; + uint8_t nibbleL = byte & 0x0F; + uint8_t nibbleH = byte >> 4; + if ( nibbleH < 10 ) + cdHashStr += '0' + nibbleH; + else + cdHashStr += 'a' + (nibbleH-10); + if ( nibbleL < 10 ) + cdHashStr += '0' + nibbleL; + else + cdHashStr += 'a' + (nibbleL-10); + } + imageNode.map["file-cd-hash-16"].value = cdHashStr; + } + imageNode.map["total-vm-size"].value = hex(image.vmSizeToMap()); + uint64_t sliceOffset = image.sliceOffsetInFile(); + if ( sliceOffset != 0 ) + imageNode.map["file-offset-of-slice"].value = hex(sliceOffset); + if ( image.hasTextRelocs() ) + imageNode.map["has-text-relocs"].value = "true"; + image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) { + Node segInfoNode; + segInfoNode.map["file-offset"].value = hex(fileOffset); + segInfoNode.map["file-size"].value = hex(fileSize); + segInfoNode.map["vm-size"].value = hex(vmSize); + segInfoNode.map["permissions"].value = hex(permissions); + imageNode.map["mappings"].array.push_back(segInfoNode); + }); + if ( printFixups ) { + image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) { + MemoryRange segContent = { nullptr, vmSize }; + std::string segName = "segment-" + decimal(segIndex); + __block Node segmentFixupsNode; + image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) { + switch ( kind ) { + case Image::FixupKind::rebase32: + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit rebase"; + break; + case Image::FixupKind::rebase64: + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "64-bit rebase"; + break; + case Image::FixupKind::rebaseText32 : + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit text rebase"; + break; + case Image::FixupKind::bind32: + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit bind, target=") + value.asString(group); + break; + case Image::FixupKind::bind64: + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("64-bit bind, target=") + value.asString(group); + break; + case Image::FixupKind::bindText32 : + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text abs bind, target=") + value.asString(group); + break; + case Image::FixupKind::bindTextRel32 : + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text rel bind, target=") + value.asString(group); + break; + case Image::FixupKind::bindImportJmp32 : + segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit IMPORT JMP rel bind, target=") + value.asString(group); + break; + } + }); + if ( segmentFixupsNode.map[segName].map.size() != 0 ) { + imageNode.map["fixups"].array.push_back(segmentFixupsNode); + } + }); + } + } + else { + imageNode.map["patch-start-index"].value = decimal(image.patchStartIndex()); + imageNode.map["patch-count"].value = decimal(image.patchCount()); + } + + // add dependents + image.forEachDependentImage(groupList, ^(uint32_t depIndex, Image depImage, Image::LinkKind kind, bool& stop) { + Node depMapNode; + depMapNode.map["path"].value = depImage.path(); + if ( printDependentsDetails ) { + ImageGroup depGroup = depImage.group(); + uint32_t indexInGroup = depGroup.indexInGroup(depImage.binaryData()); + depMapNode.map["group-index"].value = decimal(depGroup.groupNum()); + depMapNode.map["index-in-group"].value = decimal(indexInGroup); + } + switch ( kind ) { + case Image::LinkKind::regular: + depMapNode.map["link"].value = "regular"; + break; + case Image::LinkKind::reExport: + depMapNode.map["link"].value = "re-export"; + break; + case Image::LinkKind::upward: + depMapNode.map["link"].value = "upward"; + break; + case Image::LinkKind::weak: + depMapNode.map["link"].value = "weak"; + break; + } + imageNode.map["dependents"].array.push_back(depMapNode); + }); + // add things to init before this image + __block Node initBeforeNode; + image.forEachInitBefore(groupList, ^(Image beforeImage) { + Node beforeNode; + beforeNode.value = beforeImage.path(); + imageNode.map["initializer-order"].array.push_back(beforeNode); + }); + + // add initializers + image.forEachInitializer(nullptr, ^(const void* initializer) { + Node initNode; + initNode.value = hex((long)initializer); + imageNode.map["initializer-offsets"].array.push_back(initNode); + }); + + // add override info if relevant + group.forEachImageRefOverride(groupList, ^(Image standardDylib, Image overrideDylib, bool& stop) { + if ( overrideDylib.binaryData() == image.binaryData() ) { + imageNode.map["override-of-cached-dylib"].value = standardDylib.path(); + } + }); + + // add dtrace info + image.forEachDOF(nullptr, ^(const void* section) { + Node initNode; + initNode.value = hex((long)section); + imageNode.map["dof-offsets"].array.push_back(initNode); + }); + + return imageNode; +} + + +static Node buildImageGroupNode(const ImageGroup& group, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails) +{ + Node images; + uint32_t imageCount = group.imageCount(); + images.array.reserve(imageCount); + for (uint32_t i=0; i < imageCount; ++i) { + images.array.push_back(buildImageNode(group.image(i), groupList, printFixups, printDependentsDetails)); + } + return images; +} + +static Node buildClosureNode(const Closure& closure, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails) +{ + __block Node root; + + // add env-vars if they exist + closure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) { + const char* equ = strchr(keyEqualValue, '='); + if ( equ != nullptr ) { + char key[512]; + strncpy(key, keyEqualValue, equ-keyEqualValue); + key[equ-keyEqualValue] = '\0'; + root.map["env-vars"].map[key].value = equ+1; + } + }); + + // add missing files array if they exist + closure.forEachMustBeMissingFile(^(const char* path, bool& stop) { + Node fileNode; + fileNode.value = path; + root.map["must-be-missing-files"].array.push_back(fileNode); + }); + + const uint8_t* cdHash = closure.cdHash(); + std::string cdHashStr; + cdHashStr.reserve(24); + for (int i=0; i < 20; ++i) { + uint8_t byte = cdHash[i]; + uint8_t nibbleL = byte & 0x0F; + uint8_t nibbleH = byte >> 4; + if ( nibbleH < 10 ) + cdHashStr += '0' + nibbleH; + else + cdHashStr += 'a' + (nibbleH-10); + if ( nibbleL < 10 ) + cdHashStr += '0' + nibbleL; + else + cdHashStr += 'a' + (nibbleL-10); + } + if ( cdHashStr != "0000000000000000000000000000000000000000" ) + root.map["cd-hash"].value = cdHashStr; + + // add uuid of dyld cache this closure requires + closure.dyldCacheUUID(); + uuid_string_t cacheUuidStr; + uuid_unparse(*closure.dyldCacheUUID(), cacheUuidStr); + root.map["dyld-cache-uuid"].value = cacheUuidStr; + + // add top level images + Node& rootImages = root.map["root-images"]; + uint32_t initImageCount = closure.mainExecutableImageIndex(); + rootImages.array.resize(initImageCount+1); + for (uint32_t i=0; i <= initImageCount; ++i) { + const Image image = closure.group().image(i); + uuid_string_t uuidStr; + uuid_unparse(*image.uuid(), uuidStr); + rootImages.array[i].value = uuidStr; + } + root.map["initial-image-count"].value = decimal(closure.initialImageCount()); + + // add images + root.map["images"] = buildImageGroupNode(closure.group(), groupList, printFixups, printDependentsDetails); + root.map["group-num"].value = decimal(closure.group().groupNum()); + + if ( closure.mainExecutableUsesCRT() ) + root.map["main-offset"].value = hex(closure.mainExecutableEntryOffset()); + else + root.map["start-offset"].value = hex(closure.mainExecutableEntryOffset()); + + root.map["libdyld-entry-offset"].value = hex(closure.libdyldVectorOffset()); + + root.map["restricted"].value = (closure.isRestricted() ? "true" : "false"); + + root.map["library-validation"].value = (closure.usesLibraryValidation() ? "true" : "false"); + + __block Node cacheOverrides; + closure.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop) { + Node patch; + patch.map["patch-index"].value = decimal(patchTableIndex); + patch.map["replacement"].value = "{closure[" + decimal(imageIndexInClosure) + "]+" + hex(imageOffset) + "}"; + cacheOverrides.array.push_back(patch); + }); + if ( !cacheOverrides.array.empty() ) + root.map["dyld-cache-overrides"].array = cacheOverrides.array; + + return root; +} + +static void indentBy(uint32_t spaces, FILE* out) { + for (int i=0; i < spaces; ++i) { + fprintf(out, " "); + } +} + +static void printJSON(const Node& node, uint32_t indent, FILE* out) +{ + if ( !node.map.empty() ) { + fprintf(out, "{"); + bool needComma = false; + for (const auto& entry : node.map) { + if ( needComma ) + fprintf(out, ","); + fprintf(out, "\n"); + indentBy(indent+2, out); + fprintf(out, "\"%s\": ", entry.first.c_str()); + printJSON(entry.second, indent+2, out); + needComma = true; + } + fprintf(out, "\n"); + indentBy(indent, out); + fprintf(out, "}"); + } + else if ( !node.array.empty() ) { + fprintf(out, "["); + bool needComma = false; + for (const auto& entry : node.array) { + if ( needComma ) + fprintf(out, ","); + fprintf(out, "\n"); + indentBy(indent+2, out); + printJSON(entry, indent+2, out); + needComma = true; + } + fprintf(out, "\n"); + indentBy(indent, out); + fprintf(out, "]"); + } + else { + fprintf(out, "\"%s\"", node.value.c_str()); + } + if ( indent == 0 ) + fprintf(out, "\n"); +} + + +void Image::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const +{ + Node image = buildImageNode(*this, groupList, printFixups, printDependentsDetails); + printJSON(image, 0, out); +} + +void ImageGroup::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const +{ + Node root; + root.map["images"] = buildImageGroupNode(*this, groupList, printFixups, printDependentsDetails); + root.map["group-num"].value = decimal(groupNum()); + root.map["dylibs-expected-on-disk"].value = (dylibsExpectedOnDisk() ? "true" : "false"); + printJSON(root, 0, out); +} + +void ImageGroup::printStatistics(FILE* out) const +{ + __block uint32_t totalRebases = 0; + __block uint32_t totalBinds = 0; + for (uint32_t i=0; i < imageCount(); ++i) { + Image img(image(i)); + img.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) { + MemoryRange segContent = { nullptr, vmSize }; + img.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) { + if ( kind == Image::FixupKind::rebase64 ) + ++totalRebases; + else + ++totalBinds; + }); + }); + } + + fprintf(out, "ImageGroup:\n"); + fprintf(out, " image-count: % 5d\n", _binaryData->imagesPoolCount); + fprintf(out, " alias-count: % 5d\n", _binaryData->imageAliasCount); + fprintf(out, " segments-count: % 5d\n", _binaryData->segmentsPoolCount); + fprintf(out, " dependents-count: % 5d\n", _binaryData->dependentsPoolCount); + fprintf(out, " targets-count: % 5d\n", _binaryData->targetsPoolCount); + fprintf(out, " rebase-count: % 5d\n", totalRebases); + fprintf(out, " bind-count: % 5d\n", totalBinds); + fprintf(out, " fixups-size: % 8d bytes\n", _binaryData->fixupsPoolSize); + fprintf(out, " targets-size: % 8ld bytes\n", _binaryData->targetsPoolCount * sizeof(uint64_t)); + fprintf(out, " strings-size: % 8d bytes\n", _binaryData->stringsPoolSize); + fprintf(out, " dofs-size: % 8ld bytes\n", _binaryData->dofOffsetPoolCount * sizeof(uint32_t)); + fprintf(out, " indirect-groups-size: % 8ld bytes\n", _binaryData->indirectGroupNumPoolCount * sizeof(uint32_t)); +} + + +void Closure::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const +{ + Node root = buildClosureNode(*this, groupList, printFixups, printDependentsDetails); + printJSON(root, 0, out); +} + +void Closure::printStatistics(FILE* out) const +{ + fprintf(out, "closure size: %lu\n", size()); + group().printStatistics(out); +} + + + +} // namespace launch_cache +} // namespace dyld3 + +#endif + + diff --git a/dyld/dyld3/LaunchCacheReader.cpp b/dyld/dyld3/LaunchCacheReader.cpp new file mode 100644 index 0000000..06c73e1 --- /dev/null +++ b/dyld/dyld3/LaunchCacheReader.cpp @@ -0,0 +1,1471 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + +#include "LaunchCacheFormat.h" +#include "LaunchCache.h" +#include "MachOParser.h" +#include "DyldCacheParser.h" + +namespace dyld { + extern void log(const char* format, ...) __attribute__((format(printf, 1, 2))); +} + +namespace dyld3 { +namespace launch_cache { + +static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) { + assert("malformed uleb128"); + break; + } + uint64_t slice = *p & 0x7f; + + if (bit > 63) { + assert("uleb128 too big for uint64"); + break; + } + else { + result |= (slice << bit); + bit += 7; + } + } while (*p++ & 0x80); + return (uintptr_t)result; +} + + +bool MemoryRange::contains(const MemoryRange& other) const +{ + if ( this->address > other.address ) + return false; + const uint8_t* thisEnd = (uint8_t*)address + size; + const uint8_t* otherEnd = (uint8_t*)other.address + other.size; + return (thisEnd >= otherEnd); +} + +bool MemoryRange::intersects(const MemoryRange& other) const +{ + const uint8_t* thisEnd = (uint8_t*)address + size; + const uint8_t* otherEnd = (uint8_t*)other.address + other.size; + if ( otherEnd < this->address ) + return false; + return ( other.address < thisEnd ); +} + + +//////////////////////////// SlowLoadSet //////////////////////////////////////// + +bool SlowLoadSet::contains(const BinaryImageData* image) +{ + for (const BinaryImageData** p=_start; p < _current; ++p) { + if ( *p == image ) + return true; + } + return false; +} + +bool SlowLoadSet::add(const BinaryImageData* image) +{ + if ( _current < _end ) { + *_current++ = image; + return true; + } + return false; +} + +void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*)) +{ + for (const BinaryImageData** p=_start; p < _current; ++p) { + handler(*p); + } +} + +void SlowLoadSet::forEach(void (^handler)(const BinaryImageData*, bool& stop)) +{ + bool stop = false; + for (const BinaryImageData** p=_start; p < _current; ++p) { + handler(*p, stop); + if ( stop ) + break; + } +} + + +long SlowLoadSet::count() const +{ + return (_current - _start); +} + + +//////////////////////////// TargetSymbolValue //////////////////////////////////////// + + +#if DYLD_IN_PROCESS + +uintptr_t TargetSymbolValue::resolveTarget(Diagnostics& diag, const ImageGroup& inGroup, LoadedImages& images) const +{ + // this block is only used if findExportedSymbol() needs to trace re-exported dylibs to find a symbol + MachOParser::DependentFinder reExportFollower = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) { + *foundMH = nullptr; + images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) { + Image anImage(binImage); + if ( strcmp(depLoadPath, anImage.path()) == 0 ) { + *foundMH = mh; + stop = true; + } + }); + return (*foundMH != nullptr); + }; + + uintptr_t offset; + switch ( _data.sharedCache.kind ) { + + case TargetSymbolValue::kindSharedCache: + assert(_data.sharedCache.offsetIntoCache != 0); + return (uintptr_t)(images.dyldCacheLoadAddressForImage() + _data.sharedCache.offsetIntoCache); + + case TargetSymbolValue::kindAbsolute: + offset = (uintptr_t)_data.absolute.value; + // sign extend 42 bit value + if ( offset & 0x2000000000000000ULL ) + offset |= 0xC000000000000000ULL; + return offset; + + case TargetSymbolValue::kindGroup: { + uint32_t groupNum = _data.group.isIndirectGroup ? inGroup.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum; + uintptr_t targetImageLoadAddress = (uintptr_t)(images.loadAddressFromGroupAndIndex(groupNum, _data.group.indexInGroup)); + if ( targetImageLoadAddress == 0 ) + diag.error("image for groupNum=%d, indexInGroup=%d not found", groupNum, _data.group.indexInGroup); + offset = (uintptr_t)_data.group.offsetInImage; + // sign extend 42 bit offset + if ( offset & 0x0000020000000000ULL ) + offset |= 0xFFFFFC0000000000ULL; + return targetImageLoadAddress + offset; + } + + case TargetSymbolValue::kindDynamicGroup: { + const char* imagePath = inGroup.stringFromPool(_data.dynamicGroup.imagePathOffset); + const char* symbolName = inGroup.stringFromPool(_data.dynamicGroup.symbolNameOffset); + __block uintptr_t result = 0; + __block bool found = false; + if ( strcmp(imagePath, "@flat") == 0 ) { + // search all images in load order + images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) { + Diagnostics findSymbolDiag; + dyld3::MachOParser parser(mh); + dyld3::MachOParser::FoundSymbol foundInfo; + if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, ^(uint32_t, const char* depLoadPath, void*, const mach_header** foundMH, void**) { + // need to follow re-exported symbols to support libc renamed and reexported symbols + *foundMH = nullptr; + images.forEachImage(^(uint32_t innerIndex, const BinaryImageData* innerBinImage, const mach_header* innerMH, bool& innerStop) { + Image innerImage(innerBinImage); + if ( strcmp(depLoadPath, innerImage.path()) == 0 ) { + *foundMH = innerMH; + innerStop = true; + } + }); + return (*foundMH != nullptr); + }) ) { + result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value); + images.setAsNeverUnload(idx); + found = true; + stop = true; + } + }); + // bind unfound flat symbols to NULL to support lazy binding semantics + if ( !found ) { + result = 0; + found = true; + } + } + else if ( strcmp(imagePath, "@main") == 0 ) { + // search only main executable + images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) { + if ( mh->filetype == MH_EXECUTE ) { + Diagnostics findSymbolDiag; + dyld3::MachOParser parser(mh); + dyld3::MachOParser::FoundSymbol foundInfo; + if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) { + result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value); + found = true; + stop = true; + } + } + }); + } + else if ( strcmp(imagePath, "@weak_def") == 0 ) { + // search images with weak definitions in load order + images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) { + Image anImage(binImage); + if ( anImage.hasWeakDefs() ) { + Diagnostics findSymbolDiag; + dyld3::MachOParser parser(mh); + dyld3::MachOParser::FoundSymbol foundInfo; + if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, nullptr) ) { + result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value); + found = true; + images.setAsNeverUnload(idx); + stop = true; + } + } + }); + } + else { + // search only image the matches supplied path + images.forEachImage(^(uint32_t idx, const BinaryImageData* binImage, const mach_header* mh, bool& stop) { + Image anImage(binImage); + if ( strcmp(anImage.path(), imagePath) == 0 ) { + Diagnostics findSymbolDiag; + dyld3::MachOParser parser(mh); + dyld3::MachOParser::FoundSymbol foundInfo; + if ( parser.findExportedSymbol(findSymbolDiag, symbolName, nullptr, foundInfo, reExportFollower) ) { + result = ((uintptr_t)(foundInfo.foundInDylib) + (uintptr_t)foundInfo.value); + found = true; + stop = true; + } + } + }); + } + if ( found ) + return result; + if ( _data.dynamicGroup.weakImport ) + return 0; + diag.error("dynamic symbol '%s' not found for %s", symbolName, imagePath); + return 0; + } + } + assert(0 && "resolveTarget() not reachable"); +} + +#else + +TargetSymbolValue::TargetSymbolValue() +{ + _data.raw = 0; +} + +TargetSymbolValue TargetSymbolValue::makeInvalid() +{ + return TargetSymbolValue(); +} + +TargetSymbolValue TargetSymbolValue::makeSharedCacheOffset(uint32_t offset) +{ + TargetSymbolValue t; + t._data.sharedCache.kind = kindSharedCache; + t._data.sharedCache.offsetIntoCache = offset; + return t; +} + +TargetSymbolValue TargetSymbolValue::makeAbsolute(uint64_t value) +{ + TargetSymbolValue t; + t._data.absolute.kind = kindAbsolute; + t._data.absolute.value = value; + return t; +} + +TargetSymbolValue TargetSymbolValue::makeGroupValue(uint32_t groupIndex, uint32_t imageIndexInGroup, uint64_t offsetInImage, bool isIndirectGroupNum) +{ + assert(groupIndex != 0 || isIndirectGroupNum); + assert(groupIndex < 128); + assert(imageIndexInGroup < 4096); + TargetSymbolValue t; + t._data.group.kind = kindGroup; + t._data.group.isIndirectGroup = isIndirectGroupNum; + t._data.group.groupNum = groupIndex; + t._data.group.indexInGroup = imageIndexInGroup; + t._data.group.offsetInImage = offsetInImage; + return t; +} + +TargetSymbolValue TargetSymbolValue::makeDynamicGroupValue(uint32_t imagePathPoolOffset, uint32_t imageSymbolPoolOffset, bool weakImport) +{ + TargetSymbolValue t; + t._data.dynamicGroup.kind = kindDynamicGroup; + t._data.dynamicGroup.weakImport = weakImport; + t._data.dynamicGroup.imagePathOffset = imagePathPoolOffset; + t._data.dynamicGroup.symbolNameOffset = imageSymbolPoolOffset; + return t; +} + +bool TargetSymbolValue::isSharedCacheTarget(uint64_t& offsetInCache) const +{ + if ( _data.sharedCache.kind != kindSharedCache ) + return false; + offsetInCache = _data.sharedCache.offsetIntoCache; + return true; +} + +bool TargetSymbolValue::isGroupImageTarget(uint32_t& groupNum, uint32_t& indexInGroup, uint64_t& offsetInImage) const +{ + if ( _data.sharedCache.kind != kindGroup ) + return false; + // This is only used for interposing, so refuse to allow indirect for group 2 + assert(!_data.group.isIndirectGroup); + groupNum = _data.group.groupNum; + indexInGroup = _data.group.indexInGroup; + offsetInImage = _data.group.offsetInImage; + return true; +} + +bool TargetSymbolValue::isInvalid() const +{ + return (_data.raw == 0); +} + +static std::string hex8(uint64_t value) { + char buff[64]; + sprintf(buff, "0x%08llX", value); + return buff; +} + +static std::string decimal(uint64_t value) { + char buff[64]; + sprintf(buff, "%llu", value); + return buff; +} + +std::string TargetSymbolValue::asString(ImageGroup group) const +{ + int64_t offset; + switch ( _data.sharedCache.kind ) { + case kindSharedCache: + if ( _data.sharedCache.offsetIntoCache == 0 ) + return "{invalid target}"; + else + return "{cache+" + hex8(_data.sharedCache.offsetIntoCache) + "}"; + case kindAbsolute: + offset = (uintptr_t)_data.absolute.value; + // sign extend 42 bit value + if ( offset & 0x2000000000000000ULL ) + offset |= 0xC000000000000000ULL; + return "{absolute:" + hex8(offset) + "}"; + case kindGroup: + offset = _data.group.offsetInImage; + // sign extend 42 bit offset + if ( offset & 0x0000020000000000ULL ) + offset |= 0xFFFFFC0000000000ULL; + if ( _data.group.groupNum == 1 ) + return "{otherDylib[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}"; + if ( _data.group.groupNum == 2 ) + return "{closure[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}"; + else { + uint32_t groupNum = _data.group.isIndirectGroup ? group.indirectGroupNum(_data.group.groupNum) : _data.group.groupNum; + return "{dlopen-group-" + decimal(groupNum-2) + "[" + decimal(_data.group.indexInGroup) +"]+" + hex8(offset) + "}"; + } + case kindDynamicGroup: + return "{dynamic image='" + std::string(group.stringFromPool(_data.dynamicGroup.imagePathOffset)) + + "' symbol='" + std::string(group.stringFromPool(_data.dynamicGroup.symbolNameOffset)) + "'}"; + } + assert(0 && "unreachable"); + return "xx"; +} + +#endif + +//////////////////////////// ImageRef //////////////////////////////////////// + +binary_format::ImageRef binary_format::ImageRef::weakImportMissing() +{ + ImageRef missing(0xFFFFFFFF); + return missing; +} + + + +//////////////////////////// Closure //////////////////////////////////////// + +Closure::Closure(const binary_format::Closure* closure) + : _binaryData(closure) +{ + assert(closure->magic == binary_format::Closure::magicV1); +} + +size_t Closure::size() const +{ + return _binaryData->stringPoolOffset + _binaryData->stringPoolSize; +} + +const ImageGroup Closure::group() const +{ + return ImageGroup(&_binaryData->group); +} + +void Closure::forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const +{ + const uint32_t* envVarStringOffsets = (uint32_t*)((uint8_t*)_binaryData + _binaryData->dyldEnvVarsOffset); + const char* stringPool = (char*)_binaryData + _binaryData->stringPoolOffset; + bool stop = false; + for (uint32_t i=0; i < _binaryData->dyldEnvVarsCount; ++i) { + handler(&stringPool[envVarStringOffsets[i]], stop); + if ( stop ) + break; + } +} + +void Closure::forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const +{ + const uint16_t* offsets = (uint16_t*)((uint8_t*)_binaryData + _binaryData->missingFileComponentsOffset); + if ( *offsets == 0 ) + return; + const char* stringPool = (char*)_binaryData + _binaryData->stringPoolOffset; + bool stop = false; + while ( !stop ) { + char path[PATH_MAX]; + path[0] = '\0'; + while ( *offsets != 0 ) { + const char* component = &stringPool[*offsets++]; + strlcat(path, "/", PATH_MAX); + strlcat(path, component, PATH_MAX); + } + handler(path, stop); + ++offsets; // move to next path + if ( *offsets == 0 ) // if no next path, then end of list of strings + stop = true; + } +} + +const uuid_t* Closure::dyldCacheUUID() const +{ + return &(_binaryData->dyldCacheUUID); +} + + +const uint8_t* Closure::cdHash() const +{ + return _binaryData->mainExecutableCdHash; +} + + +uint32_t Closure::initialImageCount() const +{ + return _binaryData->initialImageCount; +} + + +uint32_t Closure::mainExecutableImageIndex() const +{ + return _binaryData->mainExecutableIndexInGroup; +} + + +uint32_t Closure::mainExecutableEntryOffset() const +{ + return _binaryData->mainExecutableEntryOffset; +} + +bool Closure::mainExecutableUsesCRT() const +{ + return _binaryData->usesCRT; +} + +bool Closure::isRestricted() const +{ + return _binaryData->isRestricted; +} + +bool Closure::usesLibraryValidation() const +{ + return _binaryData->usesLibraryValidation; +} + +uint32_t Closure::libdyldVectorOffset() const +{ + return _binaryData->libdyldVectorOffset; +} + +const BinaryImageData* Closure::libSystem(const ImageGroupList& groups) +{ + return Image::resolveImageRef(groups, _binaryData->libSystemRef).binaryData(); +} + +const BinaryImageData* Closure::libDyld(const ImageGroupList& groups) +{ + return Image::resolveImageRef(groups, _binaryData->libDyldRef).binaryData(); +} + + +//////////////////////////// ImageGroup //////////////////////////////////////// + +size_t ImageGroup::size() const +{ + return (_binaryData->stringsPoolOffset + _binaryData->stringsPoolSize + 3) & (-4); +} + +uint32_t ImageGroup::groupNum() const +{ + return _binaryData->groupNum; +} + +bool ImageGroup::dylibsExpectedOnDisk() const +{ + return _binaryData->dylibsExpectedOnDisk; +} + +uint32_t ImageGroup::imageCount() const +{ + return _binaryData->imagesPoolCount; +} + +const binary_format::Image* ImageGroup::imageBinary(uint32_t index) const +{ + assert(index <_binaryData->imagesPoolCount); + return (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset + (index * _binaryData->imagesEntrySize)); +} + + +const Image ImageGroup::image(uint32_t index) const +{ + return Image(imageBinary(index)); +} + +uint32_t ImageGroup::indexInGroup(const binary_format::Image* img) const +{ + long delta = (char*)img - ((char*)_binaryData + _binaryData->imagesPoolOffset); + uint32_t index = (uint32_t)(delta /_binaryData->imagesEntrySize); + assert(image(index)._binaryData == img); + return index; +} + +const binary_format::Image* ImageGroup::findImageByPath(const char* path, uint32_t& foundIndex) const +{ + // check path of each image in group + uint32_t targetHash = hashFunction(path); + const uint8_t* p = (uint8_t*)_binaryData + _binaryData->imagesPoolOffset; + for (uint32_t i=0; i < _binaryData->imagesPoolCount; ++i) { + const binary_format::Image* binImage = (binary_format::Image*)p; + if ( binImage->pathHash == targetHash ) { + Image img(binImage); + if ( !img.isInvalid() && (strcmp(img.path(), path) == 0) ) { + foundIndex = i; + return binImage; + } + } + p += _binaryData->imagesEntrySize; + } + // check each alias + const binary_format::AliasEntry* aliasEntries = (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset); + for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) { + const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool); + if ( aliasEntries[i].aliasHash == targetHash ) { + if ( strcmp(aliasPath, path) == 0 ) { + Image img = image(aliasEntries[i].imageIndexInGroup); + if ( !img.isInvalid() ) { + foundIndex = aliasEntries[i].imageIndexInGroup; + return img.binaryData(); + } + } + } + } + return nullptr; +} + +const binary_format::Image* ImageGroup::findImageByCacheOffset(size_t cacheVmOffset, uint32_t& mhCacheOffset, uint8_t& foundPermissions) const +{ + assert(groupNum() == 0); + + const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)segmentPool(0); + const binary_format::Image* image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset); + // most address lookups are in TEXT, so just search first segment in first pass + for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) { + const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex]; + if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) { + mhCacheOffset = segInfo->cacheOffset; + foundPermissions = segInfo->permissions; + return image; + } + image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize); + } + // second pass, skip TEXT segment + image = (binary_format::Image*)((char*)_binaryData + _binaryData->imagesPoolOffset); + for (uint32_t imageIndex=0; imageIndex < _binaryData->imagesPoolCount; ++imageIndex) { + for (uint32_t segIndex=1; segIndex < image->segmentsArrayCount; ++segIndex) { + const binary_format::DyldCacheSegment* segInfo = &cacheSegs[image->segmentsArrayStartIndex+segIndex]; + if ( (cacheVmOffset >= segInfo->cacheOffset) && (cacheVmOffset < (segInfo->cacheOffset + segInfo->size)) ) { + mhCacheOffset = cacheSegs[image->segmentsArrayStartIndex].cacheOffset; + foundPermissions = segInfo->permissions; + return image; + } + } + image = (binary_format::Image*)((char*)image + _binaryData->imagesEntrySize); + } + return nullptr; +} + +void ImageGroup::forEachAliasOf(uint32_t imageIndex, void (^handler)(const char* aliasPath, uint32_t aliasPathHash, bool& stop)) const +{ + bool stop = false; + const binary_format::AliasEntry* aliasEntries = (binary_format::AliasEntry*)((uint8_t*)_binaryData + _binaryData->imageAliasOffset); + for (uint32_t i=0; i < _binaryData->imageAliasCount; ++i) { + if ( aliasEntries[i].imageIndexInGroup == imageIndex ) { + const char* aliasPath = stringFromPool(aliasEntries[i].aliasOffsetInStringPool); + handler(aliasPath, aliasEntries[i].aliasHash, stop); + if ( stop ) + break; + } + } +} + +const char* ImageGroup::stringPool() const +{ + return (char*)_binaryData + _binaryData->stringsPoolOffset; +} + +const char* ImageGroup::stringFromPool(uint32_t offset) const +{ + assert(offset < _binaryData->stringsPoolSize); + return (char*)_binaryData + _binaryData->stringsPoolOffset + offset; +} + +uint32_t ImageGroup::stringPoolSize() const +{ + return _binaryData->stringsPoolSize;; +} + +binary_format::ImageRef ImageGroup::dependentPool(uint32_t index) const +{ + assert(index < _binaryData->dependentsPoolCount); + const binary_format::ImageRef* depArray = (binary_format::ImageRef*)((char*)_binaryData + _binaryData->dependentsPoolOffset); + return depArray[index]; +} + +const uint64_t* ImageGroup::segmentPool(uint32_t index) const +{ + assert(index < _binaryData->segmentsPoolCount); + const uint64_t* segArray = (uint64_t*)((char*)_binaryData + _binaryData->segmentsPoolOffset); + return &segArray[index]; +} + + +const uint32_t* ImageGroup::initializerOffsetsPool() const +{ + return (uint32_t*)((char*)_binaryData + _binaryData->intializerOffsetPoolOffset); +} + +const uint32_t ImageGroup::initializerOffsetsCount() const +{ + return _binaryData->intializerOffsetPoolCount; +} + +const binary_format::ImageRef* ImageGroup::intializerListPool() const +{ + return (binary_format::ImageRef*)((char*)_binaryData + _binaryData->intializerListPoolOffset); +} + +const uint32_t ImageGroup::intializerListPoolCount() const +{ + return _binaryData->intializerListPoolCount; +} + +const binary_format::AllFixupsBySegment* ImageGroup::fixUps(uint32_t offset) const +{ + return (binary_format::AllFixupsBySegment*)((char*)_binaryData + _binaryData->fixupsOffset + offset); +} + +const TargetSymbolValue* ImageGroup::targetValuesArray() const +{ + return (TargetSymbolValue*)((char*)_binaryData + _binaryData->targetsOffset); +} + +uint32_t ImageGroup::targetValuesCount() const +{ + return _binaryData->targetsPoolCount; +} + + +const uint32_t* ImageGroup::dofOffsetsPool() const +{ + return (uint32_t*)((char*)_binaryData + _binaryData->dofOffsetPoolOffset); +} + +const uint32_t ImageGroup::dofOffsetsCount() const +{ + return _binaryData->dofOffsetPoolCount; +} + + +const uint32_t* ImageGroup::indirectGroupNumsPool() const +{ + return (uint32_t*)((char*)_binaryData + _binaryData->indirectGroupNumPoolOffset); +} + +const uint32_t ImageGroup::indirectGroupNumsCount() const +{ + return _binaryData->indirectGroupNumPoolCount; +} + +uint32_t ImageGroup::indirectGroupNum(uint32_t offset) const +{ + assert(offset < _binaryData->indirectGroupNumPoolCount); + return indirectGroupNumsPool()[offset]; +} + +uint32_t ImageGroup::hashFunction(const char* str) +{ + uint32_t h = 0; + for (const char* s=str; *s != '\0'; ++s) + h = h*5 + *s; + return h; +} + + +void ImageGroup::forEachDyldCachePatch(uint32_t patchTargetIndex, uint32_t cacheDataVmOffset, void (^handler)(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop)) const +{ + assert(_binaryData->imagesEntrySize == sizeof(binary_format::CachedImage) && "only callable on group-0 in shared cache"); + assert(patchTargetIndex < _binaryData->cachePatchTableCount); + const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset); + uint32_t offsetsIndex = patches[patchTargetIndex].offsetsStartIndex; + uint32_t targetCacheOffset = patches[patchTargetIndex].targetCacheOffset; + const binary_format::PatchOffset* patchLocationOffsets = (binary_format::PatchOffset*)((char*)_binaryData + _binaryData->cachePatchOffsetsOffset); + bool stop = false; + while ( !stop ) { + assert(offsetsIndex < _binaryData->cachePatchOffsetsCount); + binary_format::PatchOffset entry = patchLocationOffsets[offsetsIndex]; + ++offsetsIndex; + handler(targetCacheOffset, cacheDataVmOffset+entry.dataRegionOffset, entry.hasAddend, stop); + if ( entry.last ) + stop = true; + } +} + +void ImageGroup::forEachImageRefOverride(void (^handler)(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop)) const +{ + bool stop = false; + const binary_format::ImageRefOverride* entries = (binary_format::ImageRefOverride*)((char*)_binaryData + _binaryData->imageOverrideTableOffset); + for (uint32_t i=0; (i < _binaryData->imageOverrideTableCount) && !stop; ++i) { + handler(entries[i].standardDylib, entries[i].overrideDylib, stop); + } +} + +void ImageGroup::forEachImageRefOverride(const ImageGroupList& groupList, void (^handler)(Image standardDylib, Image overrideDylib, bool& stop)) const +{ + forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool& stop) { + Image standardDylib = Image::resolveImageRef(groupList, standardDylibRef, false); + Image overrideDylib = Image::resolveImageRef(groupList, overrideDylibRef, false); + handler(standardDylib, overrideDylib, stop); + }); +} + + +#if DYLD_IN_PROCESS + +void ImageGroup::forEachDyldCachePatchLocation(const void* dyldCacheLoadAddress, uint32_t patchTargetIndex, void (^handler)(uintptr_t* locationToPatch, uintptr_t addend, bool&)) const +{ + DyldCacheParser cacheParser((DyldSharedCache*)dyldCacheLoadAddress, false); + uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset(); + forEachDyldCachePatch(patchTargetIndex, cacheDataVmOffset, ^(uint32_t targetCacheOffset, uint32_t usePointersCacheOffset, bool hasAddend, bool& stop) { + uintptr_t addend = 0; + uintptr_t* fixupLoc = (uintptr_t*)((char*)dyldCacheLoadAddress + usePointersCacheOffset); + if ( hasAddend ) { + uintptr_t currentValue = *fixupLoc; + uintptr_t expectedValue = (uintptr_t)dyldCacheLoadAddress + targetCacheOffset; + uintptr_t delta = currentValue - expectedValue; + assert(delta < 32); + addend = delta; + } + handler(fixupLoc, addend, stop); + }); +} + +void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, const BinaryImageData* image, uint32_t imageOffset, bool& stop)) const +{ + bool stop = false; + const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset); + for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) { + handler(entries[i].patchTableIndex, imageBinary(entries[i].imageIndex), entries[i].imageOffset, stop); + } +} + +#else + +void ImageGroup::forEachDyldCacheSymbolOverride(void (^handler)(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop)) const +{ + bool stop = false; + const binary_format::DyldCacheOverride* entries = (binary_format::DyldCacheOverride*)((char*)_binaryData + _binaryData->symbolOverrideTableOffset); + for (uint32_t i=0; (i < _binaryData->symbolOverrideTableCount) && !stop; ++i) { + handler(entries[i].patchTableIndex, entries[i].imageIndex, entries[i].imageOffset, stop); + } +} + +void ImageGroup::forEachDyldCachePatchLocation(const DyldCacheParser& cacheParser, void (^handler)(uint32_t targetCacheOffset, const std::vector& usesPointersCacheOffsets, bool& stop)) const +{ + uint32_t cacheDataVmOffset = (uint32_t)cacheParser.dataRegionRuntimeVmOffset(); + __block std::vector pointerCacheOffsets; + bool stop = false; + for (uint32_t patchIndex=0; patchIndex < _binaryData->cachePatchTableCount; ++patchIndex) { + pointerCacheOffsets.clear(); + __block uint32_t targetCacheOffset = 0; + forEachDyldCachePatch(patchIndex, cacheDataVmOffset, ^(uint32_t targetCacheOff, uint32_t usePointersCacheOffset, bool hasAddend, bool&) { + targetCacheOffset = targetCacheOff; + pointerCacheOffsets.push_back(usePointersCacheOffset); + }); + std::sort(pointerCacheOffsets.begin(), pointerCacheOffsets.end(), [&](uint32_t a, uint32_t b) { return a < b; }); + handler(targetCacheOffset, pointerCacheOffsets, stop); + if ( stop ) + break; + } +} + +bool ImageGroup::hasPatchTableIndex(uint32_t targetCacheOffset, uint32_t& foundIndex) const +{ + const binary_format::PatchTable* patches = (binary_format::PatchTable*)((char*)_binaryData + _binaryData->cachePatchTableOffset); + for (uint32_t i=0; i < _binaryData->cachePatchTableCount; ++i) { + if ( patches[i].targetCacheOffset == targetCacheOffset ) { + foundIndex = i; + return true; + } + } + return false; +} + +#endif + + +//////////////////////////// Image //////////////////////////////////////// + + + +const ImageGroup Image::group() const +{ + return ImageGroup((binary_format::ImageGroup*)(((char*)_binaryData) + (_binaryData->groupOffset))); +} + +uint32_t Image::maxLoadCount() const +{ + return _binaryData->maxLoadCount; +} + +const char* Image::path() const +{ + return group().stringFromPool(_binaryData->pathPoolOffset); +} + +uint32_t Image::pathHash() const +{ + return _binaryData->pathHash; +} + +const char* Image::leafName() const +{ + const char* path = group().stringFromPool(_binaryData->pathPoolOffset); + const char* lastSlash = strrchr(path, '/'); + if ( lastSlash != nullptr ) + return lastSlash+1; + else + return path; +} + +const uuid_t* Image::uuid() const +{ + return &(_binaryData->uuid); +} + +bool Image::isInvalid() const +{ + return (_binaryData == nullptr) || _binaryData->isInvalid; +} + +bool Image::hasObjC() const +{ + return _binaryData->hasObjC; +} + +bool Image::isBundle() const +{ + return _binaryData->isBundle; +} + +bool Image::hasWeakDefs() const +{ + return _binaryData->hasWeakDefs; +} + +bool Image::mayHavePlusLoads() const +{ + return _binaryData->mayHavePlusLoads; +} + +bool Image::hasTextRelocs() const +{ + return _binaryData->hasTextRelocs; +} + +bool Image::neverUnload() const +{ + return _binaryData->neverUnload; +} + +bool Image::cwdMustBeThisDir() const +{ + return _binaryData->cwdSameAsThis; +} + +bool Image::isPlatformBinary() const +{ + return _binaryData->isPlatformBinary; +} + +bool Image::overridableDylib() const +{ + return _binaryData->overridableDylib; +} + +void Image::forEachDependentImage(const ImageGroupList& groups, void (^handler)(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop)) const +{ + assert(!_binaryData->isInvalid); + binary_format::ImageRef missingRef = binary_format::ImageRef::weakImportMissing(); + __block bool stop = false; + for (uint32_t depIndex=0; (depIndex < _binaryData->dependentsArrayCount) && !stop; ++depIndex) { + binary_format::ImageRef ref = group().dependentPool(_binaryData->dependentsArrayStartIndex + depIndex); + if ( ref != missingRef ) { + Image depImage(resolveImageRef(groups, ref)); + handler(depIndex, depImage, (LinkKind)ref.kind(), stop); + } + } +} + + +#if !DYLD_IN_PROCESS +bool Image::recurseAllDependentImages(const ImageGroupList& groups, std::unordered_set& allDependents) const +{ + if ( isInvalid() ) + return false; + __block bool result = true; + forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) { + if ( allDependents.count(depImage.binaryData()) == 0 ) { + allDependents.insert(depImage.binaryData()); + if ( !depImage.recurseAllDependentImages(groups, allDependents) ) { + result = false; + stop = true; + } + } + }); + return result; +} +#endif + +bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, bool& stopped, + void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const +{ + __block bool result = true; + // breadth first, add all directly dependent images + const dyld3::launch_cache::binary_format::Image* needToProcessArray[_binaryData->dependentsArrayCount]; + memset((void*)needToProcessArray, 0, _binaryData->dependentsArrayCount * sizeof(*needToProcessArray)); + const dyld3::launch_cache::binary_format::Image** const needToProcess = needToProcessArray; + forEachDependentImage(groups, ^(uint32_t depIndex, Image depImage, LinkKind kind, bool& stop) { + const dyld3::launch_cache::binary_format::Image* depImageData = depImage.binaryData(); + if ( allDependents.contains(depImageData) ) { + needToProcess[depIndex] = nullptr; + } + else { + needToProcess[depIndex] = depImageData; + if ( !allDependents.add(depImageData) ) { + result = false; + stop = true; + return; + } + if (handler) { + handler(depImageData, stop); + if ( stop ) + stopped = true; + } + } + }); + + // recurse on each dependent image + for (int i=0; !stopped && (i < _binaryData->dependentsArrayCount); ++i) { + if ( const dyld3::launch_cache::binary_format::Image* depImageData = needToProcess[i] ) { + Image depImage(depImageData); + if ( !depImage.recurseAllDependentImages(groups, allDependents, stopped, handler) ) { + return false; + } + } + } + + return result; +} + +bool Image::recurseAllDependentImages(const ImageGroupList& groups, SlowLoadSet& allDependents, + void (^handler)(const dyld3::launch_cache::binary_format::Image* aBinImage, bool& stop)) const +{ + bool stopped = false; + return recurseAllDependentImages(groups, allDependents, stopped, handler); +} + +void Image::forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const +{ + assert(isDiskImage()); + const uint32_t pageSize = (_binaryData->has16KBpages ? 0x4000 : 0x1000); + const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex); + const binary_format::DiskSegment* diskSegs = (binary_format::DiskSegment*)rawSegs; + uint32_t segIndex = 0; + uint32_t fileOffset = 0; + int64_t vmOffset = 0; + // decrement vmOffset by all segments before TEXT (e.g. PAGEZERO) + for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) { + const binary_format::DiskSegment* seg = &diskSegs[i]; + if ( seg->filePageCount != 0 ) { + break; + } + vmOffset -= (uint64_t)seg->vmPageCount * pageSize; + } + // walk each segment and call handler + for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) { + const binary_format::DiskSegment* seg = &diskSegs[i]; + uint64_t vmSize = (uint64_t)seg->vmPageCount * pageSize; + uint32_t fileSize = seg->filePageCount * pageSize; + if ( !seg->paddingNotSeg ) { + bool stop = false; + handler(segIndex, ( fileSize == 0) ? 0 : fileOffset, fileSize, vmOffset, vmSize, seg->permissions, stop); + ++segIndex; + if ( stop ) + break; + } + vmOffset += vmSize; + fileOffset += fileSize; + } +} + +void Image::forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const +{ + assert(!isDiskImage()); + const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex); + const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs; + bool stop = false; + for (uint32_t i=0; i < _binaryData->segmentsArrayCount; ++i) { + uint64_t vmOffset = cacheSegs[i].cacheOffset - cacheSegs[0].cacheOffset; + uint64_t vmSize = cacheSegs[i].size; + uint8_t permissions = cacheSegs[i].permissions; + handler(i, vmOffset, vmSize, permissions, stop); + if ( stop ) + break; + } +} + +bool Image::segmentHasFixups(uint32_t segIndex) const +{ + return (segmentFixups(segIndex) != nullptr); +} + +bool Image::containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions) const +{ + if ( addr < imageLoadAddress ) + return false; + + __block bool found = false; + uint64_t offsetInImage = (char*)addr - (char*)imageLoadAddress; + if ( _binaryData->isDiskImage ) { + forEachDiskSegment(^(uint32_t segIterIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) { + if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) { + if ( permissions != nullptr ) + *permissions = segPerms; + found = true; + stop = true; + } + }); + } + else { + forEachCacheSegment(^(uint32_t segIterIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t segPerms, bool& stop) { + if ( (offsetInImage >= vmOffset) && (offsetInImage < vmOffset+vmSize) ) { + if ( permissions != nullptr ) + *permissions = segPerms; + found = true; + stop = true; + } + }); + } + return found; +} + +void Image::forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const +{ + const uint32_t initCount = _binaryData->initOffsetsArrayCount; + const uint32_t startIndex = _binaryData->initOffsetsArrayStartIndex; + const uint32_t* initOffsets = group().initializerOffsetsPool(); + assert(startIndex + initCount <= group().initializerOffsetsCount()); + for (uint32_t i=0; i < initCount; ++i) { + uint32_t anOffset = initOffsets[startIndex+i]; + const void* func = (char*)imageLoadAddress + anOffset; + handler(func); + } +} + +void Image::forEachInitBefore(void (^handler)(binary_format::ImageRef imageToInit)) const +{ + const uint32_t initCount = _binaryData->initBeforeArrayCount; + const uint32_t startIndex = _binaryData->initBeforeArrayStartIndex; + const uint32_t endIndex = group().intializerListPoolCount(); + const binary_format::ImageRef* initRefs = group().intializerListPool(); + assert(startIndex + initCount <= endIndex); + for (uint32_t i=0; i < initCount; ++i) { + binary_format::ImageRef ref = initRefs[startIndex+i]; + handler(ref); + } +} + +void Image::forEachDOF(const void* imageLoadAddress, void (^handler)(const void* section)) const +{ + const uint32_t dofCount = _binaryData->dofOffsetsArrayCount; + const uint32_t startIndex = _binaryData->dofOffsetsArrayStartIndex; + const uint32_t* dofOffsets = group().dofOffsetsPool(); + assert(startIndex + dofCount <= group().dofOffsetsCount()); + for (uint32_t i=0; i < dofCount; ++i) { + uint32_t anOffset = dofOffsets[startIndex+i]; + const void* section = (char*)imageLoadAddress + anOffset; + handler(section); + } +} + +Image Image::resolveImageRef(const ImageGroupList& groups, binary_format::ImageRef ref, bool applyOverrides) +{ + // first look if ref image is overridden in closure + __block binary_format::ImageRef targetRef = ref; + if ( applyOverrides ) { + binary_format::ImageRef refToMatch = ref; + refToMatch.clearKind(); + for (int i=0; i < groups.count(); ++i) { + ImageGroup aGroup(groups[i]); + if ( aGroup.groupNum() >= 2 ) { + aGroup.forEachImageRefOverride(^(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef, bool &stop) { + if ( refToMatch == standardDylibRef ) { + targetRef = overrideDylibRef; + stop = true; + } + }); + } + } + } + // create Image object from targetRef + for (int i=0; i < groups.count(); ++i) { + ImageGroup aGroup(groups[i]); + if ( aGroup.groupNum() == targetRef.groupNum() ) { + return aGroup.image(targetRef.indexInGroup()); + } + } + //assert(0 && "invalid ImageRef"); + return Image(nullptr); +} + +void Image::forEachInitBefore(const ImageGroupList& groups, void (^handler)(Image imageToInit)) const +{ + forEachInitBefore(^(binary_format::ImageRef ref) { + handler(resolveImageRef(groups, ref)); + }); +} + +bool Image::validateUsingModTimeAndInode() const +{ + return !group().binaryData()->imageFileInfoIsCdHash; +} + +bool Image::validateUsingCdHash() const +{ + // don't have cdHash info if union has modtime info in it + if ( !group().binaryData()->imageFileInfoIsCdHash ) + return false; + + // don't have codesign blob in dyld cache + if ( !_binaryData->isDiskImage ) + return false; + + // return true if image is code signed and cdHash16 is non-zero + const binary_format::DiskImage* diskImage = asDiskImage(); + if ( diskImage->codeSignFileOffset == 0 ) + return false; + + uint8_t zeros[16]; + bzero(zeros, 16); + return (memcmp(cdHash16(), zeros, 16) != 0); +} + +const uint8_t* Image::cdHash16() const +{ + return _binaryData->fileInfo.cdHash16.bytes; +} + +uint64_t Image::fileModTime() const +{ + return _binaryData->fileInfo.statInfo.mtime; +} + +uint64_t Image::fileINode() const +{ + return _binaryData->fileInfo.statInfo.inode; +} + + +bool Image::isDiskImage() const +{ + return _binaryData->isDiskImage; +} + +const binary_format::DiskImage* Image::asDiskImage() const +{ + assert(_binaryData->isDiskImage); + return (binary_format::DiskImage*)_binaryData; +} + +const binary_format::CachedImage* Image::asCachedImage() const +{ + assert(!_binaryData->isDiskImage); + return (binary_format::CachedImage*)_binaryData; +} + +uint32_t Image::pageSize() const +{ + return (_binaryData->has16KBpages ? 0x4000 : 0x1000); +} + +uint32_t Image::cacheOffset() const +{ + assert(!_binaryData->isDiskImage); + const uint64_t* rawSegs = group().segmentPool(_binaryData->segmentsArrayStartIndex); + const binary_format::DyldCacheSegment* cacheSegs = (binary_format::DyldCacheSegment*)rawSegs; + return cacheSegs[0].cacheOffset; +} + +uint32_t Image::patchStartIndex() const +{ + return asCachedImage()->patchStartIndex; +} + +uint32_t Image::patchCount() const +{ + return asCachedImage()->patchCount; +} + +uint64_t Image::sliceOffsetInFile() const +{ + return asDiskImage()->sliceOffsetIn4K * 4096; +} + +bool Image::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const +{ + const binary_format::DiskImage* diskImage = asDiskImage(); + if ( diskImage->codeSignFileOffset != 0 ) { + fileOffset = diskImage->codeSignFileOffset; + size = diskImage->codeSignFileSize; + return true; + } + return false; +} + +bool Image::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const +{ + const binary_format::DiskImage* diskImage = asDiskImage(); + if ( diskImage->fairPlayTextPageCount != 0 ) { + textOffset = diskImage->fairPlayTextStartPage * pageSize(); + size = diskImage->fairPlayTextPageCount * pageSize(); + return true; + } + return false; +} + +uint64_t Image::vmSizeToMap() const +{ + return asDiskImage()->totalVmPages * pageSize(); +} + +void Image::forEachFixup(const uint8_t* pageFixups, const void* segContent, uint32_t& offset, uint32_t& ordinal, + void (^handler)(uint32_t pageOffset, FixupKind kind, uint32_t ordinal, bool& stop)) +{ + bool stop = false; + for (const uint8_t* p = pageFixups; (*p != 0) && !stop;) { + binary_format::FixUpOpcode fullOp = (binary_format::FixUpOpcode)(*p); + binary_format::FixUpOpcode majorOp = (binary_format::FixUpOpcode)(*p & 0xF0); + uint8_t low4 = (*p & 0x0F); + switch ( majorOp ) { + case binary_format::FixUpOpcode::done: + return; + case binary_format::FixUpOpcode::rebase32: // apply + switch ( fullOp ) { + case binary_format::FixUpOpcode::bind64: + handler(offset, FixupKind::bind64, ordinal, stop); + offset += 8; + ++p; + break; + case binary_format::FixUpOpcode::bind32: + handler(offset, FixupKind::bind32, ordinal, stop); + offset += 4; + ++p; + break; + case binary_format::FixUpOpcode::rebase64: + handler(offset, FixupKind::rebase64, 0, stop); + offset += 8; + ++p; + break; + case binary_format::FixUpOpcode::rebase32: + handler(offset, FixupKind::rebase32, 0, stop); + offset += 4; + ++p; + break; + case binary_format::FixUpOpcode::rebaseText32: + handler(offset, FixupKind::rebaseText32, 0, stop); + offset += 4; + ++p; + break; + case binary_format::FixUpOpcode::bindText32: + handler(offset, FixupKind::bindText32, ordinal, stop); + offset += 4; + ++p; + break; + case binary_format::FixUpOpcode::bindTextRel32: + handler(offset, FixupKind::bindTextRel32, ordinal, stop); + offset += 4; + ++p; + break; + case binary_format::FixUpOpcode::bindImportJmp32: + handler(offset, FixupKind::bindImportJmp32, ordinal, stop); + offset += 5; + ++p; + break; + //case binary_format::FixUpOpcode::fixupChain64: + // assert(0 && "rebase/bind chain support not implemented yet"); + // break; + default: + assert(0 && "bad opcode"); + break; + } + break; + case binary_format::FixUpOpcode::incPageOffset: + if ( low4 == 0 ) { + ++p; + offset += read_uleb128(p, p+8)*4; + } + else { + offset += (low4*4); + ++p; + } + break; + case binary_format::FixUpOpcode::setPageOffset: + if ( low4 == 0 ) { + ++p; + offset = (uint32_t)read_uleb128(p, p+8); + } + else { + offset = low4; + ++p; + } + break; + case binary_format::FixUpOpcode::incOrdinal: + if ( low4 == 0 ) { + ++p; + ordinal += read_uleb128(p, p+8); + } + else { + ordinal += low4; + ++p; + } + break; + case binary_format::FixUpOpcode::setOrdinal: + if ( low4 == 0 ) { + ++p; + ordinal = (uint32_t)read_uleb128(p, p+8); + } + else { + ordinal = low4; + ++p; + } + break; + case binary_format::FixUpOpcode::repeat: { + ++p; + uint32_t count = (uint32_t)read_uleb128(p, p+8); + uint8_t pattern[32]; + for (int j=0; j < low4; ++j) { + pattern[j] = *p++; + } + pattern[low4] = (uint8_t)binary_format::FixUpOpcode::done; + for (int j=0; j < count; ++j) { + forEachFixup(&pattern[0], segContent, offset, ordinal, handler); + if ( stop ) + break; + } + } + break; + default: + assert(0 && "bad opcode"); + break; + } + } +} + +const binary_format::SegmentFixupsByPage* Image::segmentFixups(uint32_t segIndex) const +{ + const binary_format::DiskImage* diskImage = asDiskImage(); + //const BinaryImageGroupData* g = group().binaryData(); + uint32_t segCountWithFixups = diskImage->fixupsPoolSegCount; + //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d), group=%p, segCountWithFixup=%d\n", _binaryData, segIndex, g, segCountWithFixups); + const binary_format::AllFixupsBySegment* allFixups = group().fixUps(diskImage->fixupsPoolOffset); + for (uint32_t i=0; i < segCountWithFixups; ++i) { + if ( allFixups[i].segIndex == segIndex ) { + //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d) allFixups=%p, allFixups[%d].segIndex=%d, allFixups[%d].offset=%d\n", _binaryData, segIndex, allFixups, i, allFixups[i].segIndex, i, allFixups[i].offset); + return (binary_format::SegmentFixupsByPage*)((char*)allFixups + allFixups[i].offset); + } + } + //fprintf(stderr,"segmentFixups(binImage=%p, segIndex=%d) => nullptr\n", _binaryData, segIndex); + return nullptr; +} + +void Image::forEachFixup(uint32_t segIndex, MemoryRange segContent, void (^handler)(uint64_t segOffset, FixupKind, TargetSymbolValue, bool& stop)) const +{ + const binary_format::SegmentFixupsByPage* segFixups = segmentFixups(segIndex); + if ( segFixups == nullptr ) + return; + + assert(segFixups->pageCount*segFixups->pageSize <= segContent.size); + + const uint32_t ordinalsIndexInGroupPool = asDiskImage()->targetsArrayStartIndex; + const uint32_t maxOrdinal = asDiskImage()->targetsArrayCount; + const TargetSymbolValue* groupArray = group().targetValuesArray(); + assert(ordinalsIndexInGroupPool < group().targetValuesCount()); + const TargetSymbolValue* targetOrdinalArray = &groupArray[ordinalsIndexInGroupPool]; + + for (uint32_t pageIndex=0; pageIndex < segFixups->pageCount; ++pageIndex) { + const uint8_t* opcodes = (uint8_t*)(segFixups) + segFixups->pageInfoOffsets[pageIndex]; + uint64_t pageStartOffet = pageIndex * segFixups->pageSize; + uint32_t curOffset = 0; + uint32_t curOrdinal = 0; + forEachFixup(opcodes, segContent.address, curOffset, curOrdinal, ^(uint32_t pageOffset, FixupKind kind, uint32_t targetOrdinal, bool& stop) { + assert(targetOrdinal < maxOrdinal); + handler(pageStartOffet + pageOffset, kind, targetOrdinalArray[targetOrdinal], stop); + }); + } +} + + +} // namespace launch_cache +} // namespace dyld3 + + + diff --git a/dyld/dyld3/LaunchCacheWriter.cpp b/dyld/dyld3/LaunchCacheWriter.cpp new file mode 100644 index 0000000..e51fbdf --- /dev/null +++ b/dyld/dyld3/LaunchCacheWriter.cpp @@ -0,0 +1,1285 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "LaunchCacheFormat.h" +#include "LaunchCacheWriter.h" +#include "shared-cache/dyld_cache_format.h" +#include "shared-cache/DyldSharedCache.h" +#include "shared-cache/FileUtils.h" + +namespace std +{ + template <> + struct hash + { + std::size_t operator()(const dyld3::launch_cache::binary_format::ImageRef& value) const { + return std::hash()(value.value()); + } + }; +} + + +namespace dyld3 { +namespace launch_cache { + + +static uintptr_t align(uintptr_t value, uintptr_t align) +{ + return (value+align-1) & (-align); +} + +//////////////////////////// ImageGroupWriter //////////////////////////////////////// + +ImageGroupWriter::ImageGroupWriter(uint32_t groupNum, bool pages16KB, bool is64, bool dylibsExpectedOnDisk, bool mtimeAndInodeAreValid) + : _isDiskImage(groupNum != 0), _is64(is64), _groupNum(groupNum), _pageSize(pages16KB ? 0x4000 : 0x1000), + _dylibsExpectedOnDisk(dylibsExpectedOnDisk), _imageFileInfoIsCdHash(!mtimeAndInodeAreValid) +{ +} + + +uint32_t ImageGroupWriter::size() const +{ + binary_format::ImageGroup tempGroup; + layoutBinary(&tempGroup); + return tempGroup.stringsPoolOffset + tempGroup.stringsPoolSize; +} + +void ImageGroupWriter::layoutBinary(binary_format::ImageGroup* grp) const +{ + grp->imagesEntrySize = _isDiskImage ? sizeof(binary_format::DiskImage) : sizeof(binary_format::CachedImage); + grp->groupNum = _groupNum; + grp->dylibsExpectedOnDisk = _dylibsExpectedOnDisk; + grp->imageFileInfoIsCdHash = _imageFileInfoIsCdHash; + grp->padding = 0; + + grp->imagesPoolCount = imageCount(); + grp->imagesPoolOffset = sizeof(binary_format::ImageGroup); + uint32_t imagesPoolSize = grp->imagesEntrySize * grp->imagesPoolCount; + + grp->imageAliasCount = (uint32_t)_aliases.size(); + grp->imageAliasOffset = grp->imagesPoolOffset + imagesPoolSize; + uint32_t imageAliasSize = grp->imageAliasCount * sizeof(binary_format::AliasEntry); + + grp->segmentsPoolCount = (uint32_t)_segmentPool.size(); + grp->segmentsPoolOffset = (uint32_t)align(grp->imageAliasOffset + imageAliasSize, 8); + uint32_t segmentsPoolSize = grp->segmentsPoolCount * sizeof(uint64_t); + + grp->dependentsPoolCount = (uint32_t)_dependentsPool.size(); + grp->dependentsPoolOffset = grp->segmentsPoolOffset + segmentsPoolSize; + uint32_t dependentsPoolSize = grp->dependentsPoolCount * sizeof(binary_format::ImageRef); + + grp->intializerOffsetPoolCount = (uint32_t)_initializerOffsets.size(); + grp->intializerOffsetPoolOffset = (uint32_t)align(grp->dependentsPoolOffset + dependentsPoolSize, 4); + uint32_t intializerOffsetSize = grp->intializerOffsetPoolCount * sizeof(uint32_t); + + grp->intializerListPoolCount = (uint32_t)_initializerBeforeLists.size(); + grp->intializerListPoolOffset = grp->intializerOffsetPoolOffset + intializerOffsetSize; + uint32_t intializerListPoolSize = grp->intializerListPoolCount * sizeof(binary_format::ImageRef); + + grp->targetsPoolCount = (uint32_t)_targetsPool.size(); + grp->targetsOffset = (uint32_t)align(grp->intializerListPoolOffset + intializerListPoolSize, 8); + uint32_t targetsSize = grp->targetsPoolCount * sizeof(TargetSymbolValue); + + grp->fixupsPoolSize = (uint32_t)_fixupsPool.size(); + grp->fixupsOffset = (uint32_t)align(grp->targetsOffset + targetsSize, 4); + + grp->cachePatchTableCount = (uint32_t)_patchPool.size(); + grp->cachePatchTableOffset = (uint32_t)align(grp->fixupsOffset + grp->fixupsPoolSize, 4); + uint32_t patchTableSize = grp->cachePatchTableCount * sizeof(binary_format::PatchTable); + + grp->cachePatchOffsetsCount = (uint32_t)_patchLocationPool.size(); + grp->cachePatchOffsetsOffset = grp->cachePatchTableOffset + patchTableSize; + uint32_t patchOffsetsSize = grp->cachePatchOffsetsCount * sizeof(binary_format::PatchOffset); + + grp->symbolOverrideTableCount = (uint32_t)_dyldCacheSymbolOverridePool.size(); + grp->symbolOverrideTableOffset = grp->cachePatchOffsetsOffset + patchOffsetsSize; + uint32_t symbolOverrideSize = grp->symbolOverrideTableCount * sizeof(binary_format::DyldCacheOverride); + + grp->imageOverrideTableCount = (uint32_t)_imageOverridePool.size(); + grp->imageOverrideTableOffset = grp->symbolOverrideTableOffset + symbolOverrideSize; + uint32_t imageOverrideSize = grp->imageOverrideTableCount * sizeof(binary_format::ImageRefOverride); + + grp->dofOffsetPoolCount = (uint32_t)_dofOffsets.size(); + grp->dofOffsetPoolOffset = grp->imageOverrideTableOffset + imageOverrideSize; + uint32_t dofOffsetSize = grp->dofOffsetPoolCount * sizeof(uint32_t); + + grp->indirectGroupNumPoolCount = (uint32_t)_indirectGroupNumPool.size(); + grp->indirectGroupNumPoolOffset = grp->dofOffsetPoolOffset + dofOffsetSize; + uint32_t indirectGroupNumSize = grp->indirectGroupNumPoolCount * sizeof(uint32_t); + + grp->stringsPoolSize = (uint32_t)_stringPool.size(); + grp->stringsPoolOffset = grp->indirectGroupNumPoolOffset + indirectGroupNumSize; +} + + +void ImageGroupWriter::finalizeTo(Diagnostics& diag, const std::vector& curGroups, binary_format::ImageGroup* grp) const +{ + layoutBinary(grp); + uint8_t* buffer = (uint8_t*)grp; + if ( imageCount() > 0 ) { + uint32_t pad1Size = grp->segmentsPoolOffset - (grp->imageAliasOffset + grp->imageAliasCount * sizeof(binary_format::AliasEntry)); + uint32_t pad2Size = grp->targetsOffset - (grp->intializerListPoolOffset + grp->intializerListPoolCount * sizeof(binary_format::ImageRef)); + memcpy(&buffer[grp->imagesPoolOffset], &imageByIndex(0), grp->imagesEntrySize * grp->imagesPoolCount); + memcpy(&buffer[grp->imageAliasOffset], &_aliases[0], grp->imageAliasCount * sizeof(binary_format::AliasEntry)); + bzero( &buffer[grp->segmentsPoolOffset-pad1Size], pad1Size); + memcpy(&buffer[grp->segmentsPoolOffset], &_segmentPool[0], grp->segmentsPoolCount * sizeof(uint64_t)); + memcpy(&buffer[grp->dependentsPoolOffset], &_dependentsPool[0], grp->dependentsPoolCount * sizeof(binary_format::ImageRef)); + memcpy(&buffer[grp->intializerListPoolOffset], &_initializerBeforeLists[0], grp->intializerListPoolCount * sizeof(binary_format::ImageRef)); + memcpy(&buffer[grp->intializerOffsetPoolOffset],&_initializerOffsets[0], grp->intializerOffsetPoolCount * sizeof(uint32_t)); + bzero( &buffer[grp->targetsOffset-pad2Size], pad2Size); + memcpy(&buffer[grp->targetsOffset], &_targetsPool[0], grp->targetsPoolCount * sizeof(TargetSymbolValue)); + memcpy(&buffer[grp->fixupsOffset], _fixupsPool.start(), grp->fixupsPoolSize); + memcpy(&buffer[grp->cachePatchTableOffset], &_patchPool[0], grp->cachePatchTableCount * sizeof(binary_format::PatchTable)); + memcpy(&buffer[grp->cachePatchOffsetsOffset], &_patchLocationPool[0], grp->cachePatchOffsetsCount * sizeof(binary_format::PatchOffset)); + memcpy(&buffer[grp->symbolOverrideTableOffset], &_dyldCacheSymbolOverridePool[0], grp->symbolOverrideTableCount * sizeof(binary_format::DyldCacheOverride)); + memcpy(&buffer[grp->imageOverrideTableOffset], &_imageOverridePool[0], grp->imageOverrideTableCount * sizeof(binary_format::ImageRefOverride)); + memcpy(&buffer[grp->dofOffsetPoolOffset], &_dofOffsets[0], grp->dofOffsetPoolCount * sizeof(uint32_t)); + memcpy(&buffer[grp->indirectGroupNumPoolOffset], &_indirectGroupNumPool[0], grp->indirectGroupNumPoolCount * sizeof(uint32_t)); + memcpy(&buffer[grp->stringsPoolOffset], &_stringPool[0], grp->stringsPoolSize); + } + + // now that we have a real ImageGroup, we can analyze it to find max load counts for each image + ImageGroup imGroup(grp); + std::unordered_set allDependents; + STACK_ALLOC_DYNARRAY(const binary_format::ImageGroup*, curGroups.size()+1, newGroupList); + for (int i=0; i < curGroups.size(); ++i) + newGroupList[i] = curGroups[i]; + newGroupList[newGroupList.count()-1] = grp; + for (uint32_t i=0; i < grp->imagesPoolCount; ++i) { + Image image = imGroup.image(i); + if ( image.isInvalid() ) + continue; + allDependents.clear(); + allDependents.insert(image.binaryData()); + BinaryImageData* imageData = (BinaryImageData*)(buffer + grp->imagesPoolOffset + (i * grp->imagesEntrySize)); + if ( !image.recurseAllDependentImages(newGroupList, allDependents) ) { + //diag.warning("%s dependents on an invalid dylib", image.path()); + imageData->isInvalid = true; + } + imageData->maxLoadCount = (uint32_t)allDependents.size(); + } +} + +uint32_t ImageGroupWriter::maxLoadCount(Diagnostics& diag, const std::vector& curGroups, binary_format::ImageGroup* grp) const +{ + ImageGroup imGroup(grp); + std::unordered_set allDependents; + std::vector allGroups = curGroups; + if ( grp->groupNum == 2 ) + allGroups.push_back(grp); + DynArray groupList(allGroups); + for (uint32_t i=0; i < grp->imagesPoolCount; ++i) { + Image image = imGroup.image(i); + if ( image.isInvalid() ) + continue; + allDependents.insert(image.binaryData()); + BinaryImageData* imageData = (BinaryImageData*)((char*)grp + grp->imagesPoolOffset + (i * grp->imagesEntrySize)); + if ( !image.recurseAllDependentImages(groupList, allDependents) ) { + //diag.warning("%s dependents on an invalid dylib", image.path()); + imageData->isInvalid = true; + } + } + return (uint32_t)allDependents.size(); +} + +void ImageGroupWriter::setImageCount(uint32_t count) +{ + if ( _isDiskImage ) { + _diskImages.resize(count); + bzero(&_diskImages[0], count*sizeof(binary_format::DiskImage)); + } + else { + _images.resize(count); + bzero(&_images[0], count*sizeof(binary_format::CachedImage)); + } + + int32_t offset = 0 - (int32_t)sizeof(binary_format::ImageGroup); + for (uint32_t i=0; i < count; ++i) { + binary_format::Image& img = imageByIndex(i); + img.isDiskImage = _isDiskImage; + img.has16KBpages = (_pageSize == 0x4000); + img.groupOffset = offset; + if ( _isDiskImage ) + offset -= sizeof(binary_format::DiskImage); + else + offset -= sizeof(binary_format::CachedImage); + } +} + +uint32_t ImageGroupWriter::imageCount() const +{ + if ( _isDiskImage ) + return (uint32_t)_diskImages.size(); + else + return (uint32_t)_images.size(); +} + +binary_format::Image& ImageGroupWriter::imageByIndex(uint32_t imageIndex) +{ + assert(imageIndex < imageCount()); + if ( _isDiskImage ) + return _diskImages[imageIndex]; + else + return _images[imageIndex]; +} + +const binary_format::Image& ImageGroupWriter::imageByIndex(uint32_t imageIndex) const +{ + assert(imageIndex < imageCount()); + if ( _isDiskImage ) + return _diskImages[imageIndex]; + else + return _images[imageIndex]; +} + +bool ImageGroupWriter::isInvalid(uint32_t imageIndex) const +{ + return imageByIndex(imageIndex).isInvalid; +} + +void ImageGroupWriter::setImageInvalid(uint32_t imageIndex) +{ + imageByIndex(imageIndex).isInvalid = true; +} + +uint32_t ImageGroupWriter::addIndirectGroupNum(uint32_t groupNum) +{ + auto pos = _indirectGroupNumPoolExisting.find(groupNum); + if ( pos != _indirectGroupNumPoolExisting.end() ) + return pos->second; + uint32_t startOffset = (uint32_t)_indirectGroupNumPool.size(); + _indirectGroupNumPool.push_back(groupNum); + _indirectGroupNumPoolExisting[startOffset] = groupNum; + return startOffset; +} + +uint32_t ImageGroupWriter::addString(const char* str) +{ + auto pos = _stringPoolExisting.find(str); + if ( pos != _stringPoolExisting.end() ) + return pos->second; + uint32_t startOffset = (uint32_t)_stringPool.size(); + size_t size = strlen(str) + 1; + _stringPool.insert(_stringPool.end(), str, &str[size]); + _stringPoolExisting[str] = startOffset; + return startOffset; +} + +void ImageGroupWriter::alignStringPool() +{ + while ( (_stringPool.size() % 4) != 0 ) + _stringPool.push_back('\0'); +} + +void ImageGroupWriter::setImagePath(uint32_t imageIndex, const char* path) +{ + binary_format::Image& image = imageByIndex(imageIndex); + image.pathPoolOffset = addString(path); + image.pathHash = ImageGroup::hashFunction(path); +} + +void ImageGroupWriter::addImageAliasPath(uint32_t imageIndex, const char* anAlias) +{ + binary_format::AliasEntry entry; + entry.aliasHash = ImageGroup::hashFunction(anAlias); + entry.imageIndexInGroup = imageIndex; + entry.aliasOffsetInStringPool = addString(anAlias); + _aliases.push_back(entry); +} + +void ImageGroupWriter::ImageGroupWriter::setImageUUID(uint32_t imageIndex, const uuid_t uuid) +{ + memcpy(imageByIndex(imageIndex).uuid, uuid, sizeof(uuid_t)); +} + +void ImageGroupWriter::setImageHasObjC(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).hasObjC = value; +} + +void ImageGroupWriter::setImageIsBundle(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).isBundle = value; +} + +void ImageGroupWriter::setImageHasWeakDefs(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).hasWeakDefs = value; +} + +void ImageGroupWriter::setImageMayHavePlusLoads(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).mayHavePlusLoads = value; +} + +void ImageGroupWriter::setImageNeverUnload(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).neverUnload = value; +} + +void ImageGroupWriter::setImageMustBeThisDir(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).cwdSameAsThis = value; +} + +void ImageGroupWriter::setImageIsPlatformBinary(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).isPlatformBinary = value; +} + +void ImageGroupWriter::setImageOverridableDylib(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).overridableDylib = value; +} + +void ImageGroupWriter::setImageFileMtimeAndInode(uint32_t imageIndex, uint64_t mTime, uint64_t inode) +{ + imageByIndex(imageIndex).fileInfo.statInfo.mtime = mTime; + imageByIndex(imageIndex).fileInfo.statInfo.inode = inode; + assert(!_imageFileInfoIsCdHash); +} + +void ImageGroupWriter::setImageCdHash(uint32_t imageIndex, uint8_t cdHash[20]) +{ + memcpy(imageByIndex(imageIndex).fileInfo.cdHash16.bytes, cdHash, 16); + assert(_imageFileInfoIsCdHash); +} + +void ImageGroupWriter::setImageIsEncrypted(uint32_t imageIndex, bool value) +{ + imageByIndex(imageIndex).isEncrypted = value; +} + +void ImageGroupWriter::setImageMaxLoadCount(uint32_t imageIndex, uint32_t count) +{ + imageByIndex(imageIndex).maxLoadCount = count; +} + +void ImageGroupWriter::setImageFairPlayRange(uint32_t imageIndex, uint32_t offset, uint32_t size) +{ + assert(imageIndex < imageCount()); + assert(_isDiskImage); + binary_format::DiskImage& image = _diskImages[imageIndex]; + if ( image.has16KBpages ) { + assert((offset & 0x3FFF) == 0); + assert((size & 0x3FFF) == 0); + } + else { + assert((offset & 0xFFF) == 0); + assert((size & 0xFFF) == 0); + } + assert(offset < (_pageSize*16)); + image.fairPlayTextStartPage = offset / _pageSize; + image.fairPlayTextPageCount = size / _pageSize; +} + +void ImageGroupWriter::setImageInitializerOffsets(uint32_t imageIndex, const std::vector& offsets) +{ + binary_format::Image& image = imageByIndex(imageIndex); + image.initOffsetsArrayStartIndex = _initializerOffsets.size(); + image.initOffsetsArrayCount = offsets.size(); + _initializerOffsets.insert(_initializerOffsets.end(), offsets.begin(), offsets.end()); +} + +void ImageGroupWriter::setImageDOFOffsets(uint32_t imageIndex, const std::vector& offsets) +{ + binary_format::Image& image = imageByIndex(imageIndex); + image.dofOffsetsArrayStartIndex = _dofOffsets.size(); + image.dofOffsetsArrayCount = offsets.size(); + _dofOffsets.insert(_dofOffsets.end(), offsets.begin(), offsets.end()); +} + +uint32_t ImageGroupWriter::addUniqueInitList(const std::vector& initBefore) +{ + // see if this initBefore list already exists in pool + if ( _initializerBeforeLists.size() > initBefore.size() ) { + size_t cmpLen = initBefore.size()*sizeof(binary_format::ImageRef); + size_t end = _initializerBeforeLists.size() - initBefore.size(); + for (uint32_t i=0; i < end; ++i) { + if ( memcmp(&initBefore[0], &_initializerBeforeLists[i], cmpLen) == 0 ) { + return i; + } + } + } + uint32_t result = (uint32_t)_initializerBeforeLists.size(); + _initializerBeforeLists.insert(_initializerBeforeLists.end(), initBefore.begin(), initBefore.end()); + return result; +} + +void ImageGroupWriter::setImageInitBefore(uint32_t imageIndex, const std::vector& initBefore) +{ + binary_format::Image& image = imageByIndex(imageIndex); + image.initBeforeArrayStartIndex = addUniqueInitList(initBefore); + image.initBeforeArrayCount = initBefore.size(); +} + +void ImageGroupWriter::setImageSliceOffset(uint32_t imageIndex, uint64_t fileOffset) +{ + assert(imageIndex < imageCount()); + assert(_isDiskImage); + binary_format::DiskImage& image = _diskImages[imageIndex]; + image.sliceOffsetIn4K = (uint32_t)(fileOffset / 4096); +} + +void ImageGroupWriter::setImageCodeSignatureLocation(uint32_t imageIndex, uint32_t fileOffset, uint32_t size) +{ + assert(imageIndex < imageCount()); + assert(_isDiskImage); + binary_format::DiskImage& image = _diskImages[imageIndex]; + image.codeSignFileOffset = fileOffset; + image.codeSignFileSize = size; +} + +void ImageGroupWriter::setImageDependentsCount(uint32_t imageIndex, uint32_t count) +{ + binary_format::Image& image = imageByIndex(imageIndex); + image.dependentsArrayStartIndex = _dependentsPool.size(); + image.dependentsArrayCount = count; + _dependentsPool.resize(_dependentsPool.size() + count); +} + +void ImageGroupWriter::setImageDependent(uint32_t imageIndex, uint32_t depIndex, binary_format::ImageRef dependent) +{ + binary_format::Image& image = imageByIndex(imageIndex); + assert(depIndex < image.dependentsArrayCount); + _dependentsPool[image.dependentsArrayStartIndex + depIndex] = dependent; +} + +uint32_t ImageGroupWriter::imageDependentsCount(uint32_t imageIndex) const +{ + return imageByIndex(imageIndex).dependentsArrayCount; +} + +binary_format::ImageRef ImageGroupWriter::imageDependent(uint32_t imageIndex, uint32_t depIndex) const +{ + const binary_format::Image& image = imageByIndex(imageIndex); + assert(depIndex < image.dependentsArrayCount); + return _dependentsPool[image.dependentsArrayStartIndex + depIndex]; +} + +void ImageGroupWriter::setImageSegments(uint32_t imageIndex, MachOParser& imageParser, uint64_t cacheUnslideBaseAddress) +{ + if ( _isDiskImage ) { + __block uint32_t totalPageCount = 0; + __block uint32_t lastFileOffsetEnd = 0; + __block uint64_t lastVmAddrEnd = 0; + __block std::vector diskSegments; + diskSegments.reserve(8); + imageParser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( (fileOffset != 0) && (fileOffset != lastFileOffsetEnd) ) { + binary_format::DiskSegment filePadding; + filePadding.filePageCount = (fileOffset - lastFileOffsetEnd)/_pageSize; + filePadding.vmPageCount = 0; + filePadding.permissions = 0; + filePadding.paddingNotSeg = 1; + diskSegments.push_back(filePadding); + } + if ( (lastVmAddrEnd != 0) && (vmAddr != lastVmAddrEnd) ) { + binary_format::DiskSegment vmPadding; + vmPadding.filePageCount = 0; + vmPadding.vmPageCount = (vmAddr - lastVmAddrEnd)/_pageSize; + vmPadding.permissions = 0; + vmPadding.paddingNotSeg = 1; + diskSegments.push_back(vmPadding); + totalPageCount += vmPadding.vmPageCount; + } + { + binary_format::DiskSegment segInfo; + segInfo.filePageCount = (fileSize+_pageSize-1)/_pageSize; + segInfo.vmPageCount = (vmSize+_pageSize-1)/_pageSize; + segInfo.permissions = protections & 7; + segInfo.paddingNotSeg = 0; + diskSegments.push_back(segInfo); + totalPageCount += segInfo.vmPageCount; + if ( fileSize != 0 ) + lastFileOffsetEnd = fileOffset + fileSize; + if ( vmSize != 0 ) + lastVmAddrEnd = vmAddr + vmSize; + } + }); + binary_format::Image& image = imageByIndex(imageIndex); + image.segmentsArrayStartIndex = _segmentPool.size(); + image.segmentsArrayCount = diskSegments.size(); + _segmentPool.insert(_segmentPool.end(), (uint64_t*)&diskSegments[0], (uint64_t*)&diskSegments[image.segmentsArrayCount]); + _diskImages[imageIndex].totalVmPages = totalPageCount; + } + else { + binary_format::Image& image = imageByIndex(imageIndex); + image.segmentsArrayStartIndex = _segmentPool.size(); + image.segmentsArrayCount = imageParser.segmentCount(); + _segmentPool.resize(_segmentPool.size() + image.segmentsArrayCount); + __block uint32_t segIndex = 0; + imageParser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + binary_format::DyldCacheSegment seg = { (uint32_t)(vmAddr-cacheUnslideBaseAddress), (uint32_t)vmSize, protections }; + _segmentPool[image.segmentsArrayStartIndex + segIndex] = *((uint64_t*)&seg); + ++segIndex; + }); + } +} + +void ImageGroupWriter::setImagePatchLocations(uint32_t imageIndex, uint32_t funcVmOffset, const std::unordered_set& patchLocations) +{ + assert(imageIndex < imageCount()); + binary_format::CachedImage& image = _images[imageIndex]; + if ( image.patchStartIndex == 0 ) { + image.patchStartIndex = (uint32_t)_patchPool.size(); + image.patchCount = 0; + } + else { + assert(image.patchStartIndex + image.patchCount == _patchPool.size()); + } + + binary_format::PatchTable entry = { funcVmOffset, (uint32_t)_patchLocationPool.size() }; + for (uint32_t loc : patchLocations) { + _patchLocationPool.push_back(*((binary_format::PatchOffset*)&loc)); + } + _patchLocationPool.back().last = true; + _patchPool.push_back(entry); + _images[imageIndex].patchCount++; +} + +void ImageGroupWriter::setGroupCacheOverrides(const std::vector& cacheOverrides) +{ + _dyldCacheSymbolOverridePool = cacheOverrides; +} + +void ImageGroupWriter::addImageIsOverride(binary_format::ImageRef standardDylibRef, binary_format::ImageRef overrideDylibRef) +{ + _imageOverridePool.push_back({standardDylibRef, overrideDylibRef}); +} + + +class SegmentFixUpBuilder +{ +public: + SegmentFixUpBuilder(uint32_t segIndex, uint32_t dataSegPageCount, uint32_t pageSize, bool is64, + const std::vector& fixups, + std::vector& targetsForImage, bool log); + + bool hasFixups() { return _hasFixups; } + uint32_t segIndex() { return _segIndex; } + void appendSegmentFixUpMap(ContentBuffer&); + +private: + struct TmpOpcode { + binary_format::FixUpOpcode op; + uint8_t repeatOpcodeCount; + uint16_t count; + + bool operator!=(const TmpOpcode& rhs) const { + return ((op != rhs.op) || (count != rhs.count) || (repeatOpcodeCount != rhs.repeatOpcodeCount)); + } + }; + + + ContentBuffer makeFixupOpcodesForPage(uint32_t pageStartSegmentOffset, const ImageGroupWriter::FixUp* start, + const ImageGroupWriter::FixUp* end); + uint32_t getOrdinalForTarget(TargetSymbolValue); + void expandOpcodes(const std::vector& opcodes, uint8_t page[0x4000], uint32_t& offset, uint32_t& ordinal); + void expandOpcodes(const std::vector& opcodes, uint8_t page[0x4000]); + bool samePageContent(const uint8_t page1[], const uint8_t page2[]); + void printOpcodes(const char* prefix, const std::vector opcodes); + void printOpcodes(const char* prefix, bool printOffset, const TmpOpcode opcodes[], size_t opcodesLen, uint32_t& offset); + uint32_t opcodeEncodingSize(const std::vector& opcodes); + + const bool _is64; + const bool _log; + bool _hasFixups; + const uint32_t _segIndex; + const uint32_t _dataSegPageCount; + const uint32_t _pageSize; + std::vector& _targets; + std::vector _opcodesByPage; +}; + + + + +SegmentFixUpBuilder::SegmentFixUpBuilder(uint32_t segIndex, uint32_t segPageCount, uint32_t pageSize, bool is64, + const std::vector& fixups, + std::vector& targetsForImage, bool log) + : _is64(is64), _log(log), _hasFixups(false), _segIndex(segIndex), _dataSegPageCount(segPageCount), _pageSize(pageSize), _targets(targetsForImage) +{ + //fprintf(stderr, "SegmentFixUpBuilder(segIndex=%d, segPageCount=%d)\n", segIndex, segPageCount); + _targets.push_back(TargetSymbolValue::makeInvalid()); // ordinal zero reserved to mean "add slide" + _opcodesByPage.resize(segPageCount); + size_t startFixupIndex = 0; + for (uint32_t pageIndex=0; pageIndex < segPageCount; ++pageIndex) { + uint32_t pageStartOffset = pageIndex*_pageSize; + uint32_t pageEndOffset = pageStartOffset+_pageSize; + // find first index in this page + while ( (startFixupIndex < fixups.size()) && ((fixups[startFixupIndex].segIndex != segIndex) || (fixups[startFixupIndex].segOffset < pageStartOffset)) ) + ++startFixupIndex; + // find first index beyond this page + size_t endFixupIndex = startFixupIndex; + while ( (endFixupIndex < fixups.size()) && (fixups[endFixupIndex].segIndex == segIndex) && (fixups[endFixupIndex].segOffset < pageEndOffset) ) + ++endFixupIndex; + // create opcodes for fixups on this pageb + _opcodesByPage[pageIndex] = makeFixupOpcodesForPage(pageStartOffset, &fixups[startFixupIndex], &fixups[endFixupIndex]); + startFixupIndex = endFixupIndex; + } +} + + +uint32_t SegmentFixUpBuilder::getOrdinalForTarget(TargetSymbolValue target) +{ + uint32_t ordinal = 0; + for (const TargetSymbolValue& entry : _targets) { + if ( entry == target ) + return ordinal; + ++ordinal; + } + _targets.push_back(target); + return ordinal; +} + +void SegmentFixUpBuilder::appendSegmentFixUpMap(ContentBuffer& buffer) +{ + std::vector offsets; + uint32_t curOffset = sizeof(binary_format::SegmentFixupsByPage)-4 + _dataSegPageCount*4; + for (auto& opcodes : _opcodesByPage) { + if ( opcodes.size() == 0 ) + offsets.push_back(0); + else + offsets.push_back(curOffset); + curOffset += opcodes.size(); + } + uint32_t totalSize = curOffset; + + // write header + buffer.append_uint32(totalSize); // SegmentFixupsByPage.size + buffer.append_uint32(_pageSize); // SegmentFixupsByPage.pageSize + buffer.append_uint32(_dataSegPageCount); // SegmentFixupsByPage.pageCount + for (uint32_t i=0; i < _dataSegPageCount; ++i) { + buffer.append_uint32(offsets[i]); // SegmentFixupsByPage.pageInfoOffsets[i] + } + // write each page's opcode stream + for (uint32_t i=0; i < offsets.size(); ++i) { + buffer.append_buffer(_opcodesByPage[i]); + } +} + +void SegmentFixUpBuilder::expandOpcodes(const std::vector& opcodes, uint8_t page[]) +{ + uint32_t offset = 0; + uint32_t ordinal = 0; + bzero(page, _pageSize); + expandOpcodes(opcodes, page, offset, ordinal); +} + +void SegmentFixUpBuilder::expandOpcodes(const std::vector& opcodes, uint8_t page[], uint32_t& offset, uint32_t& ordinal) +{ + for (int i=0; i < opcodes.size(); ++i) { + assert(offset < _pageSize); + TmpOpcode tmp = opcodes[i]; + switch ( tmp.op ) { + case binary_format::FixUpOpcode::bind64: + *(uint64_t*)(&page[offset]) = ordinal; + offset += 8; + break; + case binary_format::FixUpOpcode::bind32: + *(uint32_t*)(&page[offset]) = ordinal; + offset += 4; + break; + case binary_format::FixUpOpcode::rebase64: + *(uint64_t*)(&page[offset]) = 0x1122334455667788; + offset += 8; + break; + case binary_format::FixUpOpcode::rebase32: + *(uint32_t*)(&page[offset]) = 0x23452345; + offset += 4; + break; + case binary_format::FixUpOpcode::rebaseText32: + *(uint32_t*)(&page[offset]) = 0x56785678; + offset += 4; + break; + case binary_format::FixUpOpcode::bindText32: + *(uint32_t*)(&page[offset]) = 0x98769876; + offset += 4; + break; + case binary_format::FixUpOpcode::bindTextRel32: + *(uint32_t*)(&page[offset]) = 0x34563456; + offset += 4; + break; + case binary_format::FixUpOpcode::bindImportJmp32: + *(uint32_t*)(&page[offset]) = 0x44556677; + offset += 4; + break; + case binary_format::FixUpOpcode::done: + break; + case binary_format::FixUpOpcode::setPageOffset: + offset = tmp.count; + break; + case binary_format::FixUpOpcode::incPageOffset: + offset += (tmp.count*4); + break; + case binary_format::FixUpOpcode::setOrdinal: + ordinal = tmp.count; + break; + case binary_format::FixUpOpcode::incOrdinal: + ++ordinal; + break; + case binary_format::FixUpOpcode::repeat: { + std::vector pattern; + for (int j=0; j < tmp.repeatOpcodeCount; ++j) { + pattern.push_back(opcodes[i+j+1]); + } + for (int j=0; j < tmp.count; ++j) { + expandOpcodes(pattern, page, offset, ordinal); + } + i += tmp.repeatOpcodeCount; + } + break; + } + } +} + + + +uint32_t SegmentFixUpBuilder::opcodeEncodingSize(const std::vector& opcodes) +{ + uint32_t size = 0; + for (int i=0; i < opcodes.size(); ++i) { + switch ( opcodes[i].op ) { + case binary_format::FixUpOpcode::bind64: + case binary_format::FixUpOpcode::bind32: + case binary_format::FixUpOpcode::rebase64: + case binary_format::FixUpOpcode::rebase32: + case binary_format::FixUpOpcode::rebaseText32: + case binary_format::FixUpOpcode::bindText32: + case binary_format::FixUpOpcode::bindTextRel32: + case binary_format::FixUpOpcode::bindImportJmp32: + case binary_format::FixUpOpcode::done: + ++size; + break; + case binary_format::FixUpOpcode::setPageOffset: + case binary_format::FixUpOpcode::incPageOffset: + case binary_format::FixUpOpcode::setOrdinal: + case binary_format::FixUpOpcode::incOrdinal: + ++size; + if ( opcodes[i].count >= 16 ) + size += ContentBuffer::uleb128_size(opcodes[i].count); + break; + case binary_format::FixUpOpcode::repeat: { + ++size; + size += ContentBuffer::uleb128_size(opcodes[i].count); + std::vector pattern; + for (int j=0; j < opcodes[i].repeatOpcodeCount; ++j) { + pattern.push_back(opcodes[++i]); + } + size += opcodeEncodingSize(pattern); + } + break; + } + } + return size; +} + + +bool SegmentFixUpBuilder::samePageContent(const uint8_t page1[], const uint8_t page2[]) +{ + bool result = true; + if (memcmp(page1, page2, _pageSize) != 0) { + if ( _is64 ) { + const uint64_t* p1 = (uint64_t* )page1; + const uint64_t* p2 = (uint64_t* )page2; + for (int i=0; i < _pageSize/8; ++i) { + if ( p1[i] != p2[i] ) { + fprintf(stderr, "page1[0x%03X] = 0x%016llX, page2[0x%03X] = 0x%016llX\n", i*8, p1[i], i*8, p2[i]); + result = false; + } + } + } + else { + const uint32_t* p1 = (uint32_t* )page1; + const uint32_t* p2 = (uint32_t* )page2; + for (int i=0; i < _pageSize/4; ++i) { + if ( p1[i] != p2[i] ) { + fprintf(stderr, "page1[0x%03X] = 0x%016X, page2[0x%03X] = 0x%016X\n", i*4, p1[i], i*4, p2[i]); + result = false; + } + } + } + } + return result; +} + +void SegmentFixUpBuilder::printOpcodes(const char* prefix, const std::vector opcodes) +{ + uint32_t offset = 0; + printOpcodes(prefix, true, &opcodes[0], opcodes.size(), offset); +} + +void SegmentFixUpBuilder::printOpcodes(const char* prefix, bool printOffset, const TmpOpcode opcodes[], size_t opcodesLen, uint32_t& offset) +{ + for (int i=0; i < opcodesLen; ++i) { + TmpOpcode tmp = opcodes[i]; + if ( printOffset ) + fprintf(stderr, "%s offset=0x%04X: ", prefix, offset); + else + fprintf(stderr, "%s ", prefix); + switch ( tmp.op ) { + case binary_format::FixUpOpcode::bind64: + fprintf(stderr, "bind64\n"); + offset += 8; + break; + case binary_format::FixUpOpcode::bind32: + fprintf(stderr, "bind32\n"); + offset += 4; + break; + case binary_format::FixUpOpcode::rebase64: + fprintf(stderr, "rebase64\n"); + offset += 8; + break; + case binary_format::FixUpOpcode::rebase32: + fprintf(stderr, "rebase32\n"); + offset += 4; + break; + case binary_format::FixUpOpcode::rebaseText32: + fprintf(stderr, "rebaseText32\n"); + offset += 4; + break; + case binary_format::FixUpOpcode::bindText32: + fprintf(stderr, "bindText32\n"); + offset += 4; + break; + case binary_format::FixUpOpcode::bindTextRel32: + fprintf(stderr, "bindTextRel32\n"); + offset += 4; + break; + case binary_format::FixUpOpcode::bindImportJmp32: + fprintf(stderr, "bindJmpRel32\n"); + offset += 4; + break; + case binary_format::FixUpOpcode::done: + fprintf(stderr, "done\n"); + break; + case binary_format::FixUpOpcode::setPageOffset: + fprintf(stderr, "setPageOffset(%d)\n", tmp.count); + offset = tmp.count; + break; + case binary_format::FixUpOpcode::incPageOffset: + fprintf(stderr, "incPageOffset(%d)\n", tmp.count); + offset += (tmp.count*4); + break; + case binary_format::FixUpOpcode::setOrdinal: + fprintf(stderr, "setOrdinal(%d)\n", tmp.count); + break; + case binary_format::FixUpOpcode::incOrdinal: + fprintf(stderr, "incOrdinal(%d)\n", tmp.count); + break; + case binary_format::FixUpOpcode::repeat: { + char morePrefix[128]; + strcpy(morePrefix, prefix); + strcat(morePrefix, " "); + uint32_t prevOffset = offset; + fprintf(stderr, "repeat(%d times, next %d opcodes)\n", tmp.count, tmp.repeatOpcodeCount); + printOpcodes(morePrefix, false, &opcodes[i+1], tmp.repeatOpcodeCount, offset); + i += tmp.repeatOpcodeCount; + uint32_t repeatDelta = (offset-prevOffset)*(tmp.count-1); + offset += repeatDelta; + } + break; + } + } +} + +ContentBuffer SegmentFixUpBuilder::makeFixupOpcodesForPage(uint32_t pageStartSegmentOffset, const ImageGroupWriter::FixUp* start, const ImageGroupWriter::FixUp* end) +{ + //fprintf(stderr, " makeFixupOpcodesForPage(segOffset=0x%06X, startFixup=%p, endFixup=%p)\n", pageStartSegmentOffset, start, end); + std::vector tmpOpcodes; + const uint32_t pointerSize = (_is64 ? 8 : 4); + uint32_t offset = pageStartSegmentOffset; + uint32_t ordinal = 0; + const ImageGroupWriter::FixUp* lastFixup = nullptr; + for (const ImageGroupWriter::FixUp* f=start; f < end; ++f) { + // ignore double bind at same address (ld64 bug) + if ( lastFixup && (lastFixup->segOffset == f->segOffset) ) + continue; + // add opcode to adjust current offset if needed + if ( f->segOffset != offset ) { + if ( ((f->segOffset % 4) != 0) || ((offset % 4) != 0) ) { + // mis aligned pointers use bigger set opcode + tmpOpcodes.push_back({binary_format::FixUpOpcode::setPageOffset, 0, (uint16_t)(f->segOffset-pageStartSegmentOffset)}); + } + else { + uint32_t delta4 = (uint32_t)(f->segOffset - offset)/4; + assert(delta4*4 < _pageSize); + tmpOpcodes.push_back({binary_format::FixUpOpcode::incPageOffset, 0, (uint16_t)delta4}); + } + offset = (uint32_t)f->segOffset; + } + uint32_t nextOrd = 0; + switch ( f->type ) { + case ImageGroupWriter::FixupType::rebase: + tmpOpcodes.push_back({_is64 ? binary_format::FixUpOpcode::rebase64 : binary_format::FixUpOpcode::rebase32, 0, 0}); + offset += pointerSize; + _hasFixups = true; + break; + case ImageGroupWriter::FixupType::pointerLazyBind: + case ImageGroupWriter::FixupType::pointerBind: + //assert(f->target.imageIndex == binary_format::OrdinalEntry::kImageIndexDyldSharedCache); + nextOrd = getOrdinalForTarget(f->target); + if ( nextOrd != ordinal ) { + if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) { + tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)}); + } + else { + tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd}); + } + ordinal = nextOrd; + } + tmpOpcodes.push_back({_is64 ? binary_format::FixUpOpcode::bind64 : binary_format::FixUpOpcode::bind32, 0, 0}); + offset += pointerSize; + _hasFixups = true; + break; + case ImageGroupWriter::FixupType::rebaseText: + assert(!_is64); + tmpOpcodes.push_back({binary_format::FixUpOpcode::rebaseText32, 0, 0}); + offset += pointerSize; + _hasFixups = true; + break; + case ImageGroupWriter::FixupType::bindText: + assert(!_is64); + nextOrd = getOrdinalForTarget(f->target); + if ( nextOrd != ordinal ) { + if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) { + tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)}); + } + else { + tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd}); + } + ordinal = nextOrd; + } + tmpOpcodes.push_back({binary_format::FixUpOpcode::bindText32, 0, 0}); + offset += pointerSize; + _hasFixups = true; + break; + case ImageGroupWriter::FixupType::bindTextRel: + assert(!_is64); + nextOrd = getOrdinalForTarget(f->target); + if ( nextOrd != ordinal ) { + if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) { + tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)}); + } + else { + tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd}); + } + ordinal = nextOrd; + } + tmpOpcodes.push_back({binary_format::FixUpOpcode::bindTextRel32, 0, 0}); + offset += pointerSize; + _hasFixups = true; + break; + case ImageGroupWriter::FixupType::bindImportJmpRel: + assert(!_is64); + nextOrd = getOrdinalForTarget(f->target); + if ( nextOrd != ordinal ) { + if ( (nextOrd > ordinal) && (nextOrd < (ordinal+31)) ) { + tmpOpcodes.push_back({binary_format::FixUpOpcode::incOrdinal, 0, (uint16_t)(nextOrd-ordinal)}); + } + else { + tmpOpcodes.push_back({binary_format::FixUpOpcode::setOrdinal, 0, (uint16_t)nextOrd}); + } + ordinal = nextOrd; + } + tmpOpcodes.push_back({binary_format::FixUpOpcode::bindImportJmp32, 0, 0}); + offset += pointerSize; + _hasFixups = true; + break; + case ImageGroupWriter::FixupType::ignore: + assert(0 && "ignore fixup types should have been removed"); + break; + } + lastFixup = f; + } + + uint8_t firstExpansion[0x4010]; // larger than 16KB to handle unaligned pointers + expandOpcodes(tmpOpcodes, firstExpansion); + + if (_log) printOpcodes("start", tmpOpcodes); + + + for (int stride=1; stride < 6; ++stride) { + for (int i=0; i < tmpOpcodes.size(); ++i) { + int j; + for (j=i+stride; j < tmpOpcodes.size(); j += stride) { + bool strideMatch = true; + for (int k=0; k < stride; ++k) { + if ( (j+k >= tmpOpcodes.size()) || (tmpOpcodes[j+k] != tmpOpcodes[i+k]) ) { + strideMatch = false; + break; + } + if ( (tmpOpcodes[j+k].op == binary_format::FixUpOpcode::repeat) && (tmpOpcodes[j+k].repeatOpcodeCount+k >= stride) ) { + strideMatch = false; + break; + } + } + if ( !strideMatch ) + break; + } + // see if same opcode repeated three or more times + int repeats = (j-i)/stride; + if ( repeats > 3 ) { + // replace run with repeat opcode + tmpOpcodes[i].op = binary_format::FixUpOpcode::repeat; + tmpOpcodes[i].repeatOpcodeCount = stride; + tmpOpcodes[i].count = repeats; + tmpOpcodes.erase(tmpOpcodes.begin()+i+1, tmpOpcodes.begin()+j-stride); + i += stride; + } + else { + // don't look for matches inside a repeat loop + if ( tmpOpcodes[i].op == binary_format::FixUpOpcode::repeat ) + i += tmpOpcodes[i].repeatOpcodeCount; + } + } + if (_log) { + char tmp[32]; + sprintf(tmp, "stride %d", stride); + printOpcodes(tmp, tmpOpcodes); + } + uint8_t secondExpansion[0x4010]; + expandOpcodes(tmpOpcodes, secondExpansion); + if ( !samePageContent(firstExpansion, secondExpansion) ) + printOpcodes("opt", tmpOpcodes); + } + + // convert temp opcodes to real opcodes + bool wroteDone = false; + ContentBuffer opcodes; + for (const TmpOpcode& tmp : tmpOpcodes) { + switch ( tmp.op ) { + case binary_format::FixUpOpcode::bind64: + case binary_format::FixUpOpcode::bind32: + case binary_format::FixUpOpcode::rebase64: + case binary_format::FixUpOpcode::rebase32: + case binary_format::FixUpOpcode::rebaseText32: + case binary_format::FixUpOpcode::bindText32: + case binary_format::FixUpOpcode::bindTextRel32: + case binary_format::FixUpOpcode::bindImportJmp32: + opcodes.append_byte((uint8_t)tmp.op); + break; + case binary_format::FixUpOpcode::done: + opcodes.append_byte((uint8_t)tmp.op); + wroteDone = true; + break; + case binary_format::FixUpOpcode::setPageOffset: + case binary_format::FixUpOpcode::incPageOffset: + case binary_format::FixUpOpcode::setOrdinal: + case binary_format::FixUpOpcode::incOrdinal: + if ( (tmp.count > 0) && (tmp.count < 16) ) { + opcodes.append_byte((uint8_t)tmp.op | tmp.count); + } + else { + opcodes.append_byte((uint8_t)tmp.op); + opcodes.append_uleb128(tmp.count); + } + break; + case binary_format::FixUpOpcode::repeat: { + const TmpOpcode* nextOpcodes = &tmp; + ++nextOpcodes; + std::vector pattern; + for (int i=0; i < tmp.repeatOpcodeCount; ++i) { + pattern.push_back(nextOpcodes[i]); + } + uint32_t repeatBytes = opcodeEncodingSize(pattern); + assert(repeatBytes < 15); + opcodes.append_byte((uint8_t)tmp.op | repeatBytes); + opcodes.append_uleb128(tmp.count); + } + break; + } + } + + if ( (opcodes.size() == 0) || !wroteDone ) + opcodes.append_byte((uint8_t)binary_format::FixUpOpcode::done); + + // make opcodes streams 4-byte aligned + opcodes.pad_to_size(4); + + //fprintf(stderr, " makeFixupOpcodesForPage(pageStartSegmentOffset=0x%0X) result=%lu bytes\n", pageStartSegmentOffset, opcodes.size()); + + return opcodes; +} + + + + +void ImageGroupWriter::setImageFixups(Diagnostics& diag, uint32_t imageIndex, std::vector& fixups, bool hasTextRelocs) +{ + // only applicable for ImageGroup in a closure (not group of images in dyld cache) + assert(_isDiskImage); + + // sort all rebases and binds by address + std::sort(fixups.begin(), fixups.end(), [](FixUp& lhs, FixUp& rhs) -> bool { + if ( &lhs == &rhs ) + return false; + // sort by segIndex + if ( lhs.segIndex < rhs.segIndex ) + return true; + if ( lhs.segIndex > rhs.segIndex ) + return false; + // then sort by segOffset + if ( lhs.segOffset < rhs.segOffset ) + return true; + if ( lhs.segOffset > rhs.segOffset ) + return false; + // two fixups at same location + + // if the same (linker bug), ignore one + if ( lhs.type == rhs.type ) { + rhs.type = FixupType::ignore; + } + // if one is rebase for lazy pointer, ignore rebase because dyld3 does not lazy bind + else if ( (lhs.type == FixupType::pointerLazyBind) && (rhs.type == FixupType::rebase) ) { + // lazy pointers have rebase and (lazy) bind at same location. since dyld3 does not do lazy binding, we mark the rebase to be ignored later + rhs.type = FixupType::ignore; + } + else if ( (rhs.type == FixupType::pointerLazyBind) && (lhs.type == FixupType::rebase) ) { + // lazy pointers have rebase and (lazy) bind at same location. since dyld3 does not do lazy binding, we mark the rebase to be ignored later + lhs.type = FixupType::ignore; + } + return (lhs.type < rhs.type); + }); + + // remove ignoreable fixups + fixups.erase(std::remove_if(fixups.begin(), fixups.end(), + [&](const FixUp& a) { + return (a.type == FixupType::ignore); + }), fixups.end()); + + // look for overlapping fixups + const uint32_t pointerSize = (_is64 ? 8 : 4); + const FixUp* lastFixup = nullptr; + for (const FixUp& fixup : fixups) { + if ( lastFixup != nullptr ) { + if ( lastFixup->segIndex == fixup.segIndex ) { + uint64_t increment = fixup.segOffset - lastFixup->segOffset; + if ( increment < pointerSize ) { + if ( (increment == 0) && ((lastFixup->type == FixupType::ignore) || (fixup.type == FixupType::ignore)) ) { + // allow rebase to local lazy helper and lazy bind to same location + } + else { + diag.error("segment %d has overlapping fixups at offset 0x%0llX and 0x%0llX", fixup.segIndex, lastFixup->segOffset, fixup.segOffset); + setImageInvalid(imageIndex); + return; + } + } + } + } + lastFixup = &fixup; + } + + if ( hasTextRelocs ) + _diskImages[imageIndex].hasTextRelocs = true; + + // there is one ordinal table per image, shared by all segments with fixups in that image + std::vector targetsForImage; + + const bool opcodeLogging = false; + // calculate SegmentFixupsByPage for each segment + std::vector builders; + for (uint32_t segIndex=0, onDiskSegIndex=0; segIndex < _diskImages[imageIndex].segmentsArrayCount; ++segIndex) { + const binary_format::DiskSegment* diskSeg = (const binary_format::DiskSegment*)&(_segmentPool[_diskImages[imageIndex].segmentsArrayStartIndex+segIndex]); + SegmentFixUpBuilder* builder = nullptr; + if ( diskSeg->paddingNotSeg ) + continue; + if ( diskSeg->filePageCount == 0 ) { + ++onDiskSegIndex; + continue; + } + if ( diskSeg->permissions & VM_PROT_WRITE ) { + builder = new SegmentFixUpBuilder(onDiskSegIndex, diskSeg->filePageCount, _pageSize, _is64, fixups, targetsForImage, opcodeLogging); + } + else if ( hasTextRelocs && (diskSeg->permissions == (VM_PROT_READ|VM_PROT_EXECUTE)) ) { + builder = new SegmentFixUpBuilder(onDiskSegIndex, diskSeg->filePageCount, _pageSize, _is64, fixups, targetsForImage, opcodeLogging); + } + if ( builder != nullptr ) { + if ( builder->hasFixups() ) + builders.push_back(builder); + else + delete builder; + } + ++onDiskSegIndex; + } + + // build AllFixupsBySegment for image + _fixupsPool.pad_to_size(4); + uint32_t startOfFixupsOffset = (uint32_t)_fixupsPool.size(); + size_t headerSize = builders.size() * sizeof(binary_format::AllFixupsBySegment); + size_t offsetOfSegmentHeaderInBuffer = _fixupsPool.size(); + for (int i=0; i < headerSize; ++i) { + _fixupsPool.append_byte(0); + } + uint32_t entryIndex = 0; + for (SegmentFixUpBuilder* builder : builders) { + binary_format::AllFixupsBySegment* entries = (binary_format::AllFixupsBySegment*)(_fixupsPool.start()+offsetOfSegmentHeaderInBuffer); + entries[entryIndex].segIndex = builder->segIndex(); + entries[entryIndex].offset = (uint32_t)_fixupsPool.size() - startOfFixupsOffset; + builder->appendSegmentFixUpMap(_fixupsPool); + delete builder; + ++entryIndex; + } + _diskImages[imageIndex].fixupsPoolOffset = (uint32_t)offsetOfSegmentHeaderInBuffer; + _diskImages[imageIndex].fixupsPoolSegCount = entryIndex; + + // append targetsForImage into group + size_t start = _targetsPool.size(); + size_t count = targetsForImage.size(); + _diskImages[imageIndex].targetsArrayStartIndex = (uint32_t)start; + _diskImages[imageIndex].targetsArrayCount = (uint32_t)count; + assert(_diskImages[imageIndex].targetsArrayStartIndex == start); + assert(_diskImages[imageIndex].targetsArrayCount == count); + _targetsPool.insert(_targetsPool.end(), targetsForImage.begin(), targetsForImage.end()); +} + + +} +} + + diff --git a/dyld/dyld3/LaunchCacheWriter.h b/dyld/dyld3/LaunchCacheWriter.h new file mode 100644 index 0000000..a00ea93 --- /dev/null +++ b/dyld/dyld3/LaunchCacheWriter.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2017 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 LaunchCacheWriter_h +#define LaunchCacheWriter_h + + +#include + +#include +#include +#include +#include + +#include "LaunchCacheFormat.h" +#include "LaunchCache.h" +#include "MachOParser.h" +#include "shared-cache/DyldSharedCache.h" + + +namespace dyld3 { +namespace launch_cache { + + + +class ContentBuffer { +private: + std::vector _data; +public: + std::vector& bytes() { return _data; } + unsigned long size() const { return _data.size(); } + void reserve(unsigned long l) { _data.reserve(l); } + const uint8_t* start() const { return &_data[0]; } + const uint8_t* end() const { return &_data[_data.size()]; } + + void append_uleb128(uint64_t value) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + _data.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + void append_byte(uint8_t byte) { + _data.push_back(byte); + } + + void append_uint32(uint32_t value) { + for (int i=0; i < 4; ++i) { + _data.push_back(value & 0xFF); + value = (value >> 8); + } + } + + void append_uint64(uint64_t value) { + for (int i=0; i < 8; ++i) { + _data.push_back(value & 0xFF); + value = (value >> 8); + } + } + + void append_buffer(const ContentBuffer& value) { + _data.insert(_data.end(), value.start(), value.end()); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + void pad_to_size(unsigned int alignment) { + while ( (_data.size() % alignment) != 0 ) + _data.push_back(0); + } +}; + +class ImageGroupWriter +{ +public: + ImageGroupWriter(uint32_t groupNum, bool pages16KB, bool is64, bool dylibsExpectedOnDisk, bool mtimeAndInodeAreValid); + + enum class FixupType { rebase, pointerBind, pointerLazyBind, bindText, bindTextRel, rebaseText, bindImportJmpRel, ignore }; + struct FixUp { + uint32_t segIndex; + uint64_t segOffset; + FixupType type; + TargetSymbolValue target; + }; + + uint32_t size() const; + void finalizeTo(Diagnostics& diag, const std::vector&, binary_format::ImageGroup* buffer) const; + uint32_t maxLoadCount(Diagnostics& diag, const std::vector&, binary_format::ImageGroup* buffer) const; + + bool isInvalid(uint32_t imageIndex) const; + + void setImageCount(uint32_t); + void setImageInvalid(uint32_t imageIndex); + void setImagePath(uint32_t imageIndex, const char* path); + void setImageUUID(uint32_t imageIndex, const uuid_t uuid); + void setImageHasObjC(uint32_t imageIndex, bool value); + void setImageIsBundle(uint32_t imageIndex, bool value); + void setImageHasWeakDefs(uint32_t imageIndex, bool value); + void setImageMayHavePlusLoads(uint32_t imageIndex, bool value); + void setImageNeverUnload(uint32_t imageIndex, bool); + void setImageMustBeThisDir(uint32_t imageIndex, bool value); + void setImageIsPlatformBinary(uint32_t imageIndex, bool value); + void setImageOverridableDylib(uint32_t imageIndex, bool value); + void setImageIsEncrypted(uint32_t imageIndex, bool value); + void setImageMaxLoadCount(uint32_t imageIndex, uint32_t count); + void setImageFairPlayRange(uint32_t imageIndex, uint32_t offset, uint32_t size); + void setImageInitializerOffsets(uint32_t imageIndex, const std::vector& offsets); + void setImageDOFOffsets(uint32_t imageIndex, const std::vector& offsets); + void setImageInitBefore(uint32_t imageIndex, const std::vector&); + void setImageSliceOffset(uint32_t imageIndex, uint64_t fileOffset); + void setImageFileMtimeAndInode(uint32_t imageIndex, uint64_t mTime, uint64_t inode); + void setImageCdHash(uint32_t imageIndex, uint8_t cdHash[20]); + void setImageCodeSignatureLocation(uint32_t imageIndex, uint32_t fileOffset, uint32_t size); + void setImageDependentsCount(uint32_t imageIndex, uint32_t count); + void setImageDependent(uint32_t imageIndex, uint32_t depIndex, binary_format::ImageRef dependent); + void setImageSegments(uint32_t imageIndex, MachOParser& imageParser, uint64_t cacheUnslideBaseAddress); + void setImageFixups(Diagnostics& diag, uint32_t imageIndex, std::vector& fixups, bool hasTextRelocs); + void addImageAliasPath(uint32_t imageIndex, const char* anAlias); + void setImagePatchLocations(uint32_t imageIndex, uint32_t funcOffset, const std::unordered_set& patchLocations); + void setGroupCacheOverrides(const std::vector& cacheOverrides); + void addImageIsOverride(binary_format::ImageRef replacer, binary_format::ImageRef replacee); + + uint32_t addIndirectGroupNum(uint32_t groupNum); + + uint32_t addString(const char* str); + void alignStringPool(); + + uint32_t imageDependentsCount(uint32_t imageIndex) const; + binary_format::ImageRef imageDependent(uint32_t imageIndex, uint32_t depIndex) const; + +private: + struct InitializerInfo { + std::vector offsetsInImage; + std::vector initBeforeImages; + }; + + uint32_t imageCount() const; + binary_format::Image& imageByIndex(uint32_t); + const binary_format::Image& imageByIndex(uint32_t) const; + std::vector makeFixupOpcodes(const FixUp* start, const FixUp* end, uint32_t pageStartSegmentOffset, std::map&); + void makeDataFixupMapAndOrdinalTable(std::vector& fixupMap, std::vector& ordinalTable); + void computeInitializerOrdering(uint32_t imageIndex); + uint32_t addUniqueInitList(const std::vector& initBefore); + void layoutBinary(binary_format::ImageGroup* grp) const; + + const bool _isDiskImage; + const bool _is64; + const uint16_t _groupNum; + const uint32_t _pageSize; + bool _dylibsExpectedOnDisk; + bool _imageFileInfoIsCdHash; + std::vector _images; + std::vector _diskImages; + std::vector _aliases; + std::vector _segmentPool; + std::vector _dependentsPool; + std::vector _initializerOffsets; + std::vector _initializerBeforeLists; + std::vector _dofOffsets; + std::vector _targetsPool; + ContentBuffer _fixupsPool; + std::vector _patchPool; + std::vector _patchLocationPool; + std::vector_dyldCacheSymbolOverridePool; + std::vector _imageOverridePool; + std::vector _indirectGroupNumPool; + std::unordered_map _indirectGroupNumPoolExisting; + std::vector _stringPool; + std::unordered_map _stringPoolExisting; +}; + + + +} // namespace launch_cache +} // namespace dyld3 + + +#endif // LaunchCacheWriter_h + + diff --git a/dyld/dyld3/Loading.cpp b/dyld/dyld3/Loading.cpp new file mode 100644 index 0000000..50785b5 --- /dev/null +++ b/dyld/dyld3/Loading.cpp @@ -0,0 +1,804 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LaunchCache.h" +#include "LaunchCacheFormat.h" +#include "Logging.h" +#include "Loading.h" +#include "MachOParser.h" +#include "dyld.h" +#include "dyld_cache_format.h" + +extern "C" { + #include "closuredProtocol.h" +} + +namespace dyld { + void log(const char* m, ...); +} + +namespace dyld3 { +namespace loader { + +#if DYLD_IN_PROCESS + +static bool sandboxBlocked(const char* path, const char* kind) +{ +#if BUILDING_LIBDYLD || !TARGET_IPHONE_SIMULATOR + sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT); + return ( sandbox_check(getpid(), kind, filter, path) > 0 ); +#else + // sandbox calls not yet supported in dyld_sim + return false; +#endif +} + +static bool sandboxBlockedMmap(const char* path) +{ + return sandboxBlocked(path, "file-map-executable"); +} + +static bool sandboxBlockedOpen(const char* path) +{ + return sandboxBlocked(path, "file-read-data"); +} + +static bool sandboxBlockedStat(const char* path) +{ + return sandboxBlocked(path, "file-read-metadata"); +} + +#if TARGET_OS_WATCH +static uint64_t pageAlign(uint64_t value) +{ + return (value + 4095) & (-4096); +} +#endif + +static void updateSliceOffset(uint64_t& sliceOffset, uint64_t codeSignEndOffset, size_t fileLen) +{ +#if TARGET_OS_WATCH + if ( sliceOffset != 0 ) { + if ( pageAlign(codeSignEndOffset) == pageAlign(fileLen) ) { + // cache builder saw fat file, but file is now thin + sliceOffset = 0; + return; + } + } +#endif +} + +static const mach_header* mapImage(const dyld3::launch_cache::Image image, Diagnostics& diag, LogFunc log_loads, LogFunc log_segments) +{ + uint64_t sliceOffset = image.sliceOffsetInFile(); + const uint64_t totalVMSize = image.vmSizeToMap(); + const uint32_t codeSignFileOffset = image.asDiskImage()->codeSignFileOffset; + const uint32_t codeSignFileSize = image.asDiskImage()->codeSignFileSize; + + // open file + int fd = ::open(image.path(), O_RDONLY, 0); + if ( fd == -1 ) { + int openErr = errno; + if ( (openErr == EPERM) && sandboxBlockedOpen(image.path()) ) + diag.error("file system sandbox blocked open(\"%s\", O_RDONLY)", image.path()); + else + diag.error("open(\"%s\", O_RDONLY) failed with errno=%d", image.path(), openErr); + return nullptr; + } + + // get file info + struct stat statBuf; +#if TARGET_IPHONE_SIMULATOR + if ( stat(image.path(), &statBuf) != 0 ) { +#else + if ( fstat(fd, &statBuf) != 0 ) { +#endif + int statErr = errno; + if ( (statErr == EPERM) && sandboxBlockedStat(image.path()) ) + diag.error("file system sandbox blocked stat(\"%s\")", image.path()); + else + diag.error("stat(\"%s\") failed with errno=%d", image.path(), statErr); + close(fd); + return nullptr; + } + + // verify file has not changed since closure was built + if ( image.validateUsingModTimeAndInode() ) { + if ( (statBuf.st_mtime != image.fileModTime()) || (statBuf.st_ino != image.fileINode()) ) { + diag.error("file mtime/inode changed since closure was built for '%s'", image.path()); + close(fd); + return nullptr; + } + } + + // handle OS dylibs being thinned after closure was built + if ( image.group().groupNum() == 1 ) + updateSliceOffset(sliceOffset, codeSignFileOffset+codeSignFileSize, (size_t)statBuf.st_size); + + // register code signature + uint64_t coveredCodeLength = UINT64_MAX; + if ( codeSignFileOffset != 0 ) { + fsignatures_t siginfo; + siginfo.fs_file_start = sliceOffset; // start of mach-o slice in fat file + siginfo.fs_blob_start = (void*)(long)(codeSignFileOffset); // start of CD in mach-o file + siginfo.fs_blob_size = codeSignFileSize; // size of CD + int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); + if ( result == -1 ) { + int errnoCopy = errno; + if ( (errnoCopy == EPERM) || (errnoCopy == EBADEXEC) ) { + diag.error("code signature invalid (errno=%d) sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'", + errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image.path()); + } + else { + diag.error("fcntl(fd, F_ADDFILESIGS_RETURN) failed with errno=%d, sliceOffset=0x%08llX, codeBlobOffset=0x%08X, codeBlobSize=0x%08X for '%s'", + errnoCopy, sliceOffset, codeSignFileOffset, codeSignFileSize, image.path()); + } + close(fd); + return nullptr; + } + coveredCodeLength = siginfo.fs_file_start; + if ( coveredCodeLength < image.asDiskImage()->codeSignFileOffset ) { + diag.error("code signature does not cover entire file up to signature"); + close(fd); + return nullptr; + } + + // always call F_CHECK_LV to preflight + fchecklv checkInfo; + char messageBuffer[512]; + messageBuffer[0] = '\0'; + checkInfo.lv_file_start = sliceOffset; + checkInfo.lv_error_message_size = sizeof(messageBuffer); + checkInfo.lv_error_message = messageBuffer; + int res = fcntl(fd, F_CHECK_LV, &checkInfo); + if ( res == -1 ) { + diag.error("code signature in (%s) not valid for use in process: %s", image.path(), messageBuffer); + close(fd); + return nullptr; + } + } + + // reserve address range + vm_address_t loadAddress = 0; + kern_return_t r = vm_allocate(mach_task_self(), &loadAddress, (vm_size_t)totalVMSize, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + diag.error("vm_allocate(size=0x%0llX) failed with result=%d", totalVMSize, r); + close(fd); + return nullptr; + } + + if ( sliceOffset != 0 ) + log_segments("dyld: Mapping %s (slice offset=%llu)\n", image.path(), sliceOffset); + else + log_segments("dyld: Mapping %s\n", image.path()); + + // map each segment + __block bool mmapFailure = false; + __block const uint8_t* codeSignatureStartAddress = nullptr; + __block const uint8_t* linkeditEndAddress = nullptr; + __block bool mappedFirstSegment = false; + image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) { + // Mapping zero filled segments fails with mmap of size 0 + if ( fileSize == 0 ) + return; + void* segAddress = mmap((void*)(loadAddress+vmOffset), fileSize, permissions, MAP_FIXED | MAP_PRIVATE, fd, sliceOffset+fileOffset); + int mmapErr = errno; + if ( segAddress == MAP_FAILED ) { + if ( mmapErr == EPERM ) { + if ( sandboxBlockedMmap(image.path()) ) + diag.error("file system sandbox blocked mmap() of '%s'", image.path()); + else + diag.error("code signing blocked mmap() of '%s'", image.path()); + } + else { + diag.error("mmap(addr=0x%0llX, size=0x%08X) failed with errno=%d for %s", loadAddress+vmOffset, fileSize, mmapErr, image.path()); + } + mmapFailure = true; + stop = true; + } + else if ( codeSignFileOffset > fileOffset ) { + codeSignatureStartAddress = (uint8_t*)segAddress + (codeSignFileOffset-fileOffset); + linkeditEndAddress = (uint8_t*)segAddress + vmSize; + } + // sanity check first segment is mach-o header + if ( (segAddress != MAP_FAILED) && !mappedFirstSegment ) { + mappedFirstSegment = true; + if ( !MachOParser::isMachO(diag, segAddress, fileSize) ) { + mmapFailure = true; + stop = true; + } + } + if ( !mmapFailure ) { + MachOParser parser((mach_header*)loadAddress); + log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser.segmentName(segIndex), + (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' , + (long)segAddress, (long)segAddress+vmSize-1); + } + }); + if ( mmapFailure ) { + vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize); + close(fd); + return nullptr; + } + + // close file + close(fd); + + #if BUILDING_LIBDYLD + // verify file has not changed since closure was built by checking code signature has not changed + if ( image.validateUsingCdHash() ) { + if ( codeSignatureStartAddress == nullptr ) { + diag.error("code signature missing"); + } + else if ( codeSignatureStartAddress+codeSignFileSize > linkeditEndAddress ) { + diag.error("code signature extends beyond end of __LINKEDIT"); + } + else { + uint8_t cdHash[20]; + if ( MachOParser::cdHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize, cdHash) ) { + if ( memcmp(image.cdHash16(), cdHash, 16) != 0 ) + diag.error("code signature changed since closure was built"); + } + else{ + diag.error("code signature format invalid"); + } + } + if ( diag.hasError() ) { + vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize); + return nullptr; + } + } +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // tell kernel about fairplay encrypted regions + uint32_t fpTextOffset; + uint32_t fpSize; + if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) { + const mach_header* mh = (mach_header*)loadAddress; + int result = mremap_encrypted(((uint8_t*)mh) + fpTextOffset, fpSize, 1, mh->cputype, mh->cpusubtype); + diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result); + vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize); + return nullptr; + } +#endif + + log_loads("dyld: load %s\n", image.path()); + + return (mach_header*)loadAddress; +} + + +void unmapImage(const launch_cache::binary_format::Image* binImage, const mach_header* loadAddress) +{ + assert(loadAddress != nullptr); + launch_cache::Image image(binImage); + vm_deallocate(mach_task_self(), (vm_address_t)loadAddress, (vm_size_t)(image.vmSizeToMap())); +} + + +static void applyFixupsToImage(Diagnostics& diag, const mach_header* imageMH, const launch_cache::binary_format::Image* imageData, + launch_cache::TargetSymbolValue::LoadedImages& imageResolver, LogFunc log_fixups) +{ + launch_cache::Image image(imageData); + MachOParser imageParser(imageMH); + // Note, these are cached here to avoid recalculating them on every loop iteration + const launch_cache::ImageGroup& imageGroup = image.group(); + const char* leafName = image.leafName(); + intptr_t slide = imageParser.getSlide(); + image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool& segStop) { + if ( !image.segmentHasFixups(segIndex) ) + return; + const launch_cache::MemoryRange segContent = { (char*)imageMH + vmOffset, vmSize }; + #if __i386__ + bool textRelocs = ((protections & VM_PROT_WRITE) == 0); + if ( textRelocs ) { + kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)segContent.address, (vm_size_t)segContent.size, false, VM_PROT_WRITE | VM_PROT_READ); + if ( r != KERN_SUCCESS ) { + diag.error("vm_protect() failed trying to make text segment writable, result=%d", r); + return; + } + } + #else + if ( (protections & VM_PROT_WRITE) == 0 ) { + diag.error("fixups found in non-writable segment of %s", image.path()); + return; + } + #endif + image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, launch_cache::Image::FixupKind kind, launch_cache::TargetSymbolValue targetValue, bool& stop) { + if ( segOffset > segContent.size ) { + diag.error("fixup is past end of segment. segOffset=0x%0llX, segSize=0x%0llX, segIndex=%d", segOffset, segContent.size, segIndex); + stop = true; + return; + } + uintptr_t* fixUpLoc = (uintptr_t*)((char*)(segContent.address) + segOffset); + uintptr_t value; + #if __i386__ + uint32_t rel32; + uint8_t* jumpSlot; + #endif + //dyld::log("fixup loc=%p\n", fixUpLoc); + switch ( kind ) { + #if __LP64__ + case launch_cache::Image::FixupKind::rebase64: + #else + case launch_cache::Image::FixupKind::rebase32: + #endif + *fixUpLoc += slide; + log_fixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide); + break; + #if __LP64__ + case launch_cache::Image::FixupKind::bind64: + #else + case launch_cache::Image::FixupKind::bind32: + #endif + value = targetValue.resolveTarget(diag, imageGroup, imageResolver); + log_fixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); + *fixUpLoc = value; + break; + #if __i386__ + case launch_cache::Image::FixupKind::rebaseText32: + log_fixups("dyld: text fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide); + *fixUpLoc += slide; + break; + case launch_cache::Image::FixupKind::bindText32: + value = targetValue.resolveTarget(diag, imageGroup, imageResolver); + log_fixups("dyld: text fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value); + *fixUpLoc = value; + break; + case launch_cache::Image::FixupKind::bindTextRel32: + // CALL instruction uses pc-rel value + value = targetValue.resolveTarget(diag, imageGroup, imageResolver); + log_fixups("dyld: CALL fixup: %s:%p = %p (pc+0x%08X)\n", leafName, fixUpLoc, (void*)value, (value - (uintptr_t)(fixUpLoc))); + *fixUpLoc = (value - (uintptr_t)(fixUpLoc)); + break; + case launch_cache::Image::FixupKind::bindImportJmp32: + // JMP instruction in __IMPORT segment uses pc-rel value + jumpSlot = (uint8_t*)fixUpLoc; + value = targetValue.resolveTarget(diag, imageGroup, imageResolver); + rel32 = (value - ((uintptr_t)(fixUpLoc)+5)); + log_fixups("dyld: JMP fixup: %s:%p = %p (pc+0x%08X)\n", leafName, fixUpLoc, (void*)value, rel32); + jumpSlot[0] = 0xE9; // JMP rel32 + jumpSlot[1] = rel32 & 0xFF; + jumpSlot[2] = (rel32 >> 8) & 0xFF; + jumpSlot[3] = (rel32 >> 16) & 0xFF; + jumpSlot[4] = (rel32 >> 24) & 0xFF; + break; + #endif + default: + diag.error("unknown fixup kind %d", kind); + } + if ( diag.hasError() ) + stop = true; + }); + #if __i386__ + if ( textRelocs ) { + kern_return_t r = vm_protect(mach_task_self(), (vm_address_t)segContent.address, (vm_size_t)segContent.size, false, protections); + if ( r != KERN_SUCCESS ) { + diag.error("vm_protect() failed trying to make text segment non-writable, result=%d", r); + return; + } + } + #endif + }); +} + + + +class VIS_HIDDEN CurrentLoadImages : public launch_cache::TargetSymbolValue::LoadedImages +{ +public: + CurrentLoadImages(launch_cache::DynArray& images, const uint8_t* cacheAddr) + : _dyldCacheLoadAddress(cacheAddr), _images(images) { } + + virtual const uint8_t* dyldCacheLoadAddressForImage(); + virtual const mach_header* loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup); + virtual void forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop)); + virtual void setAsNeverUnload(uint32_t anIndex) { _images[anIndex].neverUnload = true; } +private: + const uint8_t* _dyldCacheLoadAddress; + launch_cache::DynArray& _images; +}; + +const uint8_t* CurrentLoadImages::dyldCacheLoadAddressForImage() +{ + return _dyldCacheLoadAddress; +} + +const mach_header* CurrentLoadImages::loadAddressFromGroupAndIndex(uint32_t groupNum, uint32_t indexInGroup) +{ + __block const mach_header* result = nullptr; + forEachImage(^(uint32_t anIndex, const launch_cache::binary_format::Image* imageData, const mach_header* mh, bool& stop) { + launch_cache::Image image(imageData); + launch_cache::ImageGroup imageGroup = image.group(); + if ( imageGroup.groupNum() != groupNum ) + return; + if ( imageGroup.indexInGroup(imageData) == indexInGroup ) { + result = mh; + stop = true; + } + }); + return result; +} + +void CurrentLoadImages::forEachImage(void (^handler)(uint32_t anIndex, const launch_cache::binary_format::Image*, const mach_header*, bool& stop)) +{ + bool stop = false; + for (int i=0; i < _images.count(); ++i) { + ImageInfo& info = _images[i]; + handler(i, info.imageData, info.loadAddress, stop); + if ( stop ) + break; + } +} + +struct DOFInfo { + const void* dof; + const mach_header* imageHeader; + const char* imageShortName; +}; + +static void registerDOFs(const DOFInfo* dofs, uint32_t dofSectionCount, LogFunc log_dofs) +{ + if ( dofSectionCount != 0 ) { + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + if ( fd < 0 ) { + log_dofs("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n"); + } + else { + // allocate a buffer on the stack for the variable length dof_ioctl_data_t type + uint8_t buffer[sizeof(dof_ioctl_data_t) + dofSectionCount*sizeof(dof_helper_t)]; + dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer; + + // fill in buffer with one dof_helper_t per DOF section + ioctlData->dofiod_count = dofSectionCount; + for (unsigned int i=0; i < dofSectionCount; ++i) { + strlcpy(ioctlData->dofiod_helpers[i].dofhp_mod, dofs[i].imageShortName, DTRACE_MODNAMELEN); + ioctlData->dofiod_helpers[i].dofhp_dof = (uintptr_t)(dofs[i].dof); + ioctlData->dofiod_helpers[i].dofhp_addr = (uintptr_t)(dofs[i].dof); + } + + // tell kernel about all DOF sections en mas + // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel + user_addr_t val = (user_addr_t)(unsigned long)ioctlData; + if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) { + // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field. + // Note, the closure marked the image as being never unload, so we don't need to keep the ID around + // or support unregistering it later. + for (unsigned int i=0; i < dofSectionCount; ++i) { + log_dofs("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n", + dofs[i].dof, dofs[i].imageShortName, (int)(ioctlData->dofiod_helpers[i].dofhp_dof)); + } + } + else { + //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n"); + } + close(fd); + } + } +} + + +void mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray& images, const uint8_t* cacheLoadAddress, + LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs) +{ + // scan array and map images not already loaded + for (int i=0; i < images.count(); ++i) { + ImageInfo& info = images[i]; + const dyld3::launch_cache::Image image(info.imageData); + if ( info.loadAddress != nullptr ) { + // log main executable's segments + if ( (info.groupNum == 2) && (info.loadAddress->filetype == MH_EXECUTE) && !info.previouslyFixedUp ) { + if ( log_segments("dyld: mapped by kernel %s\n", image.path()) ) { + MachOParser parser(info.loadAddress); + image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) { + uint64_t start = (long)info.loadAddress + vmOffset; + uint64_t end = start+vmSize-1; + if ( (segIndex == 0) && (permissions == 0) ) { + start = 0; + } + log_segments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", parser.segmentName(segIndex), + (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' , + start, end); + }); + } + } + // skip over ones already loaded + continue; + } + if ( image.isDiskImage() ) { + //dyld::log("need to load image[%d] %s\n", i, image.path()); + info.loadAddress = mapImage(image, diag, log_loads, log_segments); + if ( diag.hasError() ) { + break; // out of for loop + } + info.justMapped = true; + } + else { + bool expectedOnDisk = image.group().dylibsExpectedOnDisk(); + bool overridableDylib = image.overridableDylib(); + if ( expectedOnDisk || overridableDylib ) { + struct stat statBuf; + if ( ::stat(image.path(), &statBuf) == 0 ) { + if ( expectedOnDisk ) { + // macOS case: verify dylib file info matches what it was when cache was built + if ( image.fileModTime() != statBuf.st_mtime ) { + diag.error("cached dylib mod-time has changed, dylib cache has: 0x%08llX, file has: 0x%08lX, for: %s", image.fileModTime(), (long)statBuf.st_mtime, image.path()); + break; // out of for loop + } + if ( image.fileINode() != statBuf.st_ino ) { + diag.error("cached dylib inode has changed, dylib cache has: 0x%08llX, file has: 0x%08llX, for: %s", image.fileINode(), statBuf.st_ino, image.path()); + break; // out of for loop + } + } + else { + // iOS internal: dylib override installed + diag.error("cached dylib overridden: %s", image.path()); + break; // out of for loop + } + } + else { + if ( expectedOnDisk ) { + // macOS case: dylib that existed when cache built no longer exists + diag.error("missing cached dylib: %s", image.path()); + break; // out of for loop + } + } + } + info.loadAddress = (mach_header*)(cacheLoadAddress + image.cacheOffset()); + info.justUsedFromDyldCache = true; + if ( log_segments("dyld: Using from dyld cache %s\n", image.path()) ) { + MachOParser parser(info.loadAddress); + image.forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) { + log_segments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", parser.segmentName(segIndex), + (permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' , + (long)cacheLoadAddress+vmOffset, (long)cacheLoadAddress+vmOffset+vmSize-1); + }); + } + } + } + if ( diag.hasError() ) { + // back out and unmapped images all loaded so far + for (uint32_t j=0; j < images.count(); ++j) { + ImageInfo& anInfo = images[j]; + if ( anInfo.justMapped ) + unmapImage(anInfo.imageData, anInfo.loadAddress); + anInfo.loadAddress = nullptr; + } + return; + } + + // apply fixups + CurrentLoadImages fixupHelper(images, cacheLoadAddress); + for (int i=0; i < images.count(); ++i) { + ImageInfo& info = images[i]; + // images in shared cache do not need fixups applied + launch_cache::Image image(info.imageData); + if ( !image.isDiskImage() ) + continue; + // previously loaded images were previously fixed up + if ( info.previouslyFixedUp ) + continue; + //dyld::log("apply fixups to mh=%p, path=%s\n", info.loadAddress, Image(info.imageData).path()); + dyld3::loader::applyFixupsToImage(diag, info.loadAddress, info.imageData, fixupHelper, log_fixups); + if ( diag.hasError() ) + break; + } + + // Record dtrace DOFs + // if ( /* FIXME! register dofs */ ) + { + __block uint32_t dofCount = 0; + for (int i=0; i < images.count(); ++i) { + ImageInfo& info = images[i]; + launch_cache::Image image(info.imageData); + // previously loaded images were previously fixed up + if ( info.previouslyFixedUp ) + continue; + image.forEachDOF(nullptr, ^(const void* section) { + // DOFs cause the image to be never-unload + assert(image.neverUnload()); + ++dofCount; + }); + } + + // struct RegisteredDOF { const mach_header* mh; int registrationID; }; + DOFInfo dofImages[dofCount]; + __block DOFInfo* dofImagesBase = dofImages; + dofCount = 0; + for (int i=0; i < images.count(); ++i) { + ImageInfo& info = images[i]; + launch_cache::Image image(info.imageData); + // previously loaded images were previously fixed up + if ( info.previouslyFixedUp ) + continue; + image.forEachDOF(info.loadAddress, ^(const void* section) { + DOFInfo dofInfo; + dofInfo.dof = section; + dofInfo.imageHeader = info.loadAddress; + dofInfo.imageShortName = image.leafName(); + dofImagesBase[dofCount++] = dofInfo; + }); + } + registerDOFs(dofImages, dofCount, log_dofs); + } +} + +#if BUILDING_DYLD +void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop)) +{ + int fd = dyld::my_open(path, O_RDONLY, 0); + if ( fd != -1 ) { + struct stat statBuf; + if ( fstat(fd, &statBuf) == 0 ) { + const char* lines = (const char*)mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if ( lines != MAP_FAILED ) { + bool stop = false; + const char* const eof = &lines[statBuf.st_size]; + for (const char* s = lines; s < eof; ++s) { + char lineBuffer[MAXPATHLEN]; + char* t = lineBuffer; + char* tEnd = &lineBuffer[MAXPATHLEN]; + while ( (s < eof) && (t != tEnd) ) { + if ( *s == '\n' ) + break; + *t++ = *s++; + } + *t = '\0'; + lineHandler(lineBuffer, stop); + if ( stop ) + break; + } + munmap((void*)lines, (size_t)statBuf.st_size); + } + } + close(fd); + } +} + + +bool internalInstall() +{ +#if TARGET_IPHONE_SIMULATOR + return false; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + uint32_t devFlags = *((uint32_t*)_COMM_PAGE_DEV_FIRM); + return ( (devFlags & 1) == 1 ); +#else + return ( csr_check(CSR_ALLOW_APPLE_INTERNAL) == 0 ); +#endif +} + +/* Checks to see if there are any args that impact dyld. These args + * can be set sevaral ways. These will only be honored on development + * and Apple Internal builds. + * + * First the existence of a file is checked for: + * /S/L/C/com.apple.dyld/dyld-bootargs + * If it exists it will be mapped and scanned line by line. If the executable + * exists in the file then the arguments on its line will be applied. "*" may + * be used a wildcard to represent all apps. First matching line will be used, + * the wild card must be one the last line. Additionally, lines must end with + * a "\n" + * + * + * SAMPLE FILE: + + /bin/ls:force_dyld2=1 + /usr/bin/sw_vers:force_dyld2=1 +*:force_dyld3=1 +EOL + + If no file exists then the kernel boot-args will be scanned. + */ +bool bootArgsContains(const char* arg) +{ + //FIXME: Use strnstr(). Unfortunately we are missing an imp libc. +#if TARGET_IPHONE_SIMULATOR + return false; +#else + // don't check for boot-args on customer installs + if ( !internalInstall() ) + return false; + + char pathBuffer[MAXPATHLEN+1]; +#if __IPHONE_OS_VERSION_MIN_REQUIRED + strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR)); +#else + strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR)); +#endif + strlcat(pathBuffer, "dyld-bootargs", MAXPATHLEN+1); + __block bool result = false; + forEachLineInFile(pathBuffer, ^(const char* line, bool& stop) { + const char* delim = strchr(line, ':'); + if ( delim == nullptr ) + return; + char binary[MAXPATHLEN]; + char options[MAXPATHLEN]; + strlcpy(binary, line, MAXPATHLEN); + binary[delim-line] = '\0'; + strlcpy(options, delim+1, MAXPATHLEN); + if ( (strcmp(dyld::getExecutablePath(), binary) == 0) || (strcmp("*", binary) == 0) ) { + result = (strstr(options, arg) != nullptr); + return; + } + }); + + // get length of full boot-args string + size_t len; + if ( sysctlbyname("kern.bootargs", NULL, &len, NULL, 0) != 0 ) + return false; + + // get copy of boot-args string + char bootArgsBuffer[len]; + if ( sysctlbyname("kern.bootargs", bootArgsBuffer, &len, NULL, 0) != 0 ) + return false; + + // return true if 'arg' is a sub-string of boot-args + return (strstr(bootArgsBuffer, arg) != nullptr); +#endif +} +#endif + +#if BUILDING_LIBDYLD +// hack because libdyld.dylib should not link with libc++.dylib +extern "C" void __cxa_pure_virtual() __attribute__((visibility("hidden"))); +void __cxa_pure_virtual() +{ + abort(); +} +#endif + + +#endif // DYLD_IN_PROCESS + +} // namespace loader +} // namespace dyld3 + + + + + diff --git a/dyld/dyld3/Loading.h b/dyld/dyld3/Loading.h new file mode 100644 index 0000000..177543b --- /dev/null +++ b/dyld/dyld3/Loading.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 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_LOADING_H__ +#define __DYLD_LOADING_H__ + +#include +#include +#include +#include <_simple.h> +#include "LaunchCache.h" +#include "LaunchCacheFormat.h" +#include "MachOParser.h" +#include "ClosureBuffer.h" + + + +namespace dyld3 { + +ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input); + +namespace loader { + +struct ImageInfo +{ + const launch_cache::binary_format::Image* imageData; + const mach_header* loadAddress; + uint32_t groupNum; + uint32_t indexInGroup; + bool previouslyFixedUp; + bool justMapped; + bool justUsedFromDyldCache; + bool neverUnload; +}; + + +#if DYLD_IN_PROCESS + +typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2))); + +void mapAndFixupImages(Diagnostics& diag, launch_cache::DynArray& images, const uint8_t* cacheLoadAddress, + LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs) VIS_HIDDEN; + + +void unmapImage(const launch_cache::binary_format::Image* image, const mach_header* loadAddress) VIS_HIDDEN; + +#if BUILDING_DYLD +bool bootArgsContains(const char* arg) VIS_HIDDEN; +bool internalInstall(); +void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop)); +#endif +#endif + +} // namespace loader +} // namespace dyld3 + + +#endif // __DYLD_LOADING_H__ + + diff --git a/dyld/dyld3/Logging.cpp b/dyld/dyld3/Logging.cpp new file mode 100644 index 0000000..593f3c7 --- /dev/null +++ b/dyld/dyld3/Logging.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include <_simple.h> + +#include "Logging.h" + + +namespace dyld3 { + + +static bool sVerboseLoading = false; +static bool sVerboseInitializers = false; +static bool sVerboseSegments = false; +static bool sVerboseAPIs = false; +static bool sVerboseNotifications = false; +static bool sVerboseFixups = false; +static bool sVerboseDOFs = false; + +static void vlog_default(const char* format, va_list list) +{ + _simple_vdprintf(2, format, list); +} + +static void (*sLogFunction)(const char* format, va_list list) = &vlog_default; +static void (*sHaltFunction)(const char* message) __attribute__((noreturn)) = nullptr; + +void halt(const char* message) +{ + (*sHaltFunction)(message); +} + +static void vlog(const char* format, va_list list) +{ + (*sLogFunction)(format, list); +} + +bool log_loads(const char* format, ...) +{ + if ( !sVerboseLoading ) + return false; + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + +bool log_segments(const char* format, ...) +{ + if ( !sVerboseSegments ) + return false; + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + +bool log_fixups(const char* format, ...) +{ + if ( !sVerboseFixups ) + return false; + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + +bool log_initializers(const char* format, ...) +{ + if ( !sVerboseInitializers ) + return false; + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + +bool log_apis(const char* format, ...) +{ + if ( !sVerboseAPIs ) + return false; + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + +bool log_notifications(const char* format, ...) +{ + if ( !sVerboseNotifications ) + return false; + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + +bool log_dofs(const char* format, ...) +{ + if ( !sVerboseDOFs ) + return false; + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + + + +void setLoggingFromEnvs(const char* envp[]) +{ + for(const char** p = envp; *p != NULL; p++) { + const char* keyEqualsValue = *p; + const char* equals = strchr(keyEqualsValue, '='); + if ( equals != NULL ) { + //const char* value = &equals[1]; + const size_t keyLen = equals-keyEqualsValue; + char key[keyLen+1]; + strncpy(key, keyEqualsValue, keyLen); + key[keyLen] = '\0'; + if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) { + sVerboseLoading = true; + } + else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) { + sVerboseSegments = true; + } + else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) { + sVerboseInitializers = true; + } + else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) { + sVerboseAPIs = true; + } + else if ( strcmp(key, "DYLD_PRINT_NOTIFICATIONS") == 0 ) { + sVerboseNotifications = true; + } + else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) { + sVerboseFixups = true; + } + else if ( strcmp(key, "DYLD_PRINT_DOFS") == 0 ) { + sVerboseDOFs = true; + } + } + } +} + +void setLoggingFunction(void (*func)(const char* format, va_list list)) +{ + sLogFunction = func; +} + +void setHaltFunction(void (*func)(const char* message) __attribute__((noreturn))) +{ + sHaltFunction = func; +} + + +} // namespace dyld3 + diff --git a/dyld/dyld3/Logging.h b/dyld/dyld3/Logging.h new file mode 100644 index 0000000..f6b82fb --- /dev/null +++ b/dyld/dyld3/Logging.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 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_LOGGING_H__ +#define __DYLD_LOGGING_H__ + +#include +#include +#include + +#define VIS_HIDDEN __attribute__((visibility("hidden"))) + + +namespace dyld3 { + + +bool log_loads(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN; +bool log_apis(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN; +bool log_segments(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN; +bool log_initializers(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN; +bool log_fixups(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN; +bool log_notifications(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN; +bool log_dofs(const char* format, ...) __attribute__((format(printf, 1, 2))) VIS_HIDDEN; + +void halt(const char* message) __attribute((noreturn)) VIS_HIDDEN ; + + +// only called during libdyld set up +void setLoggingFromEnvs(const char* envp[]) VIS_HIDDEN ; +void setLoggingFunction(void (*func)(const char* format, va_list list)) VIS_HIDDEN; +void setHaltFunction(void (*func)(const char* message) __attribute__((noreturn))) VIS_HIDDEN; + + + + +} // namespace dyld3 + +#endif // __DYLD_LOGGING_H__ + + diff --git a/dyld/dyld3/MachOParser.cpp b/dyld/dyld3/MachOParser.cpp new file mode 100644 index 0000000..27bf3ba --- /dev/null +++ b/dyld/dyld3/MachOParser.cpp @@ -0,0 +1,3509 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !DYLD_IN_PROCESS +#include +#endif + +#include "MachOParser.h" +#include "Logging.h" +#include "CodeSigningTypes.h" +#include "DyldSharedCache.h" +#include "Trie.hpp" + +#if DYLD_IN_PROCESS + #include "APIs.h" +#else + #include "StringUtils.h" +#endif + + + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + +#ifndef CPU_SUBTYPE_ARM64_E + #define CPU_SUBTYPE_ARM64_E 2 +#endif + +#ifndef LC_BUILD_VERSION + #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */ + + /* + * The build_version_command contains the min OS version on which this + * binary was built to run for its platform. The list of known platforms and + * tool values following it. + */ + struct build_version_command { + uint32_t cmd; /* LC_BUILD_VERSION */ + uint32_t cmdsize; /* sizeof(struct build_version_command) plus */ + /* ntools * sizeof(struct build_tool_version) */ + uint32_t platform; /* platform */ + uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t ntools; /* number of tool entries following this */ + }; + + struct build_tool_version { + uint32_t tool; /* enum for the tool */ + uint32_t version; /* version number of the tool */ + }; + + /* Known values for the platform field above. */ + #define PLATFORM_MACOS 1 + #define PLATFORM_IOS 2 + #define PLATFORM_TVOS 3 + #define PLATFORM_WATCHOS 4 + #define PLATFORM_BRIDGEOS 5 + + /* Known values for the tool field above. */ + #define TOOL_CLANG 1 + #define TOOL_SWIFT 2 + #define TOOL_LD 3 +#endif + + +namespace dyld3 { + + +bool FatUtil::isFatFile(const void* fileStart) +{ + const fat_header* fileStartAsFat = (fat_header*)fileStart; + return ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ); +} + +/// Returns true if (addLHS + addRHS) > b, or if the add overflowed +template +static bool greaterThanAddOrOverflow(uint32_t addLHS, uint32_t addRHS, T b) { + return (addLHS > b) || (addRHS > (b-addLHS)); +} + +/// Returns true if (addLHS + addRHS) > b, or if the add overflowed +template +static bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) { + return (addLHS > b) || (addRHS > (b-addLHS)); +} + +void FatUtil::forEachSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop)) +{ + const fat_header* fh = (fat_header*)fileContent; + if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) ) { + diag.error("not a fat file"); + return; + } + + if ( OSSwapBigToHostInt32(fh->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) { + diag.error("fat header too large: %u entries", OSSwapBigToHostInt32(fh->nfat_arch)); + } + const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + bool stop = false; + for (uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + uint32_t cpuType = OSSwapBigToHostInt32(archs[i].cputype); + uint32_t cpuSubType = OSSwapBigToHostInt32(archs[i].cpusubtype); + uint32_t offset = OSSwapBigToHostInt32(archs[i].offset); + uint32_t len = OSSwapBigToHostInt32(archs[i].size); + if (greaterThanAddOrOverflow(offset, len, fileLen)) { + diag.error("slice %d extends beyond end of file", i); + return; + } + callback(cpuType, cpuSubType, (uint8_t*)fileContent+offset, len, stop); + if ( stop ) + break; + } +} + +#if !DYLD_IN_PROCESS +bool FatUtil::isFatFileWithSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, const std::string& archName, size_t& sliceOffset, size_t& sliceLen, bool& missingSlice) +{ + missingSlice = false; + if ( !isFatFile(fileContent) ) + return false; + + __block bool found = false; + forEachSlice(diag, fileContent, fileLen, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) { + std::string sliceArchName = MachOParser::archName(sliceCpuType, sliceCpuSubType); + if ( sliceArchName == archName ) { + sliceOffset = (char*)sliceStart - (char*)fileContent; + sliceLen = sliceSize; + found = true; + stop = true; + } + }); + if ( diag.hasError() ) + return false; + + if ( !found ) + missingSlice = true; + + // when looking for x86_64h fallback to x86_64 + if ( !found && (archName == "x86_64h") ) + return isFatFileWithSlice(diag, fileContent, fileLen, "x86_64", sliceOffset, sliceLen, missingSlice); + + return found; +} + +#endif + +MachOParser::MachOParser(const mach_header* mh, bool dyldCacheIsRaw) +{ +#if DYLD_IN_PROCESS + // assume all in-process mach_headers are real loaded images + _data = (long)mh; +#else + if (mh == nullptr) + return; + _data = (long)mh; + if ( (mh->flags & 0x80000000) == 0 ) { + // asssume out-of-process mach_header not in a dyld cache are raw mapped files + _data |= 1; + } + else { + // out-of-process mach_header in a dyld cache are not raw, but cache may be raw + if ( dyldCacheIsRaw ) + _data |= 2; + } +#endif +} + +const mach_header* MachOParser::header() const +{ + return (mach_header*)(_data & -4); +} + +// "raw" means the whole mach-o file was mapped as one contiguous region +// not-raw means the the mach-o file was mapped like dyld does - with zero fill expansion +bool MachOParser::isRaw() const +{ + return (_data & 1); +} + +// A raw dyld cache is when the whole dyld cache file is mapped in one contiguous region +// not-raw manes the dyld cache was mapped as it is at runtime with padding between regions +bool MachOParser::inRawCache() const +{ + return (_data & 2); +} + +uint32_t MachOParser::fileType() const +{ + return header()->filetype; +} + +bool MachOParser::inDyldCache() const +{ + return (header()->flags & 0x80000000); +} + +bool MachOParser::hasThreadLocalVariables() const +{ + return (header()->flags & MH_HAS_TLV_DESCRIPTORS); +} + +Platform MachOParser::platform() const +{ + Platform platform; + uint32_t minOS; + uint32_t sdk; + if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) + return platform; + + // old binary with no explict load command to mark platform, look at arch + switch ( header()->cputype ) { + case CPU_TYPE_X86_64: + case CPU_TYPE_I386: + return Platform::macOS; + case CPU_TYPE_ARM64: + case CPU_TYPE_ARM: + return Platform::iOS; + } + return Platform::macOS; +} + + +#if !DYLD_IN_PROCESS + +const MachOParser::ArchInfo MachOParser::_s_archInfos[] = { + { "x86_64", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL }, + { "x86_64h", CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_H }, + { "i386", CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }, + { "arm64", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL }, + { "arm64e", CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_E }, + { "armv7k", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K }, + { "armv7s", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S }, + { "armv7", CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 } +}; + +bool MachOParser::isValidMachO(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables) +{ + // must start with mach-o magic value + const mach_header* mh = (const mach_header*)fileContent; + if ( (mh->magic != MH_MAGIC) && (mh->magic != MH_MAGIC_64) ) { + diag.warning("could not use '%s' because it is not a mach-o file", pathOpened.c_str()); + return false; + } + + // must match requested architecture if specified + if (!archName.empty() && !isArch(mh, archName)) { + // except when looking for x86_64h, fallback to x86_64 + if ( (archName != "x86_64h") || !isArch(mh, "x86_64") ) { + diag.warning("could not use '%s' because it does not contain required architecture %s", pathOpened.c_str(), archName.c_str()); + return false; + } + } + + // must be a filetype dyld can load + switch ( mh->filetype ) { + case MH_EXECUTE: + if ( ignoreMainExecutables ) + return false; + break; + case MH_DYLIB: + case MH_BUNDLE: + break; + default: + diag.warning("could not use '%s' because it is not a dylib, bundle, or executable", pathOpened.c_str()); + return false; + } + + // must be from a file - not in the dyld shared cache + if ( mh->flags & 0x80000000 ) { + diag.warning("could not use '%s' because the high bit of mach_header flags is reserved for images in dyld cache", pathOpened.c_str()); + return false; + } + + // validate load commands structure + MachOParser parser(mh); + if ( !parser.validLoadCommands(diag, fileLength) ) + return false; + + // must match requested platform + if ( parser.platform() != platform ) { + diag.warning("could not use '%s' because it was built for a different platform", pathOpened.c_str()); + return false; + } + + // cannot be a static executable + if ( (mh->filetype == MH_EXECUTE) && !parser.isDynamicExecutable() ) { + diag.warning("could not use '%s' because it is a static executable", pathOpened.c_str()); + return false; + } + + // validate dylib loads + if ( !parser.validEmbeddedPaths(diag) ) + return false; + + // validate segments + if ( !parser.validSegments(diag, fileLength) ) + return false; + + // validate LINKEDIT layout + if ( !parser.validLinkeditLayout(diag) ) + return false; + + return true; +} + + +bool MachOParser::validLoadCommands(Diagnostics& diag, size_t fileLen) +{ + // check load command don't exceed file length + if ( header()->sizeofcmds + sizeof(mach_header_64) > fileLen ) { + diag.warning("load commands exceed length of file"); + return false; + } + // walk all load commands and sanity check them + Diagnostics walkDiag; + LinkEditInfo lePointers; + getLinkEditLoadCommands(walkDiag, lePointers); + if ( walkDiag.hasError() ) { + diag.warning("%s", walkDiag.errorMessage().c_str()); + return false; + } + + // check load commands fit in TEXT segment + __block bool overflowText = false; + forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( strcmp(segName, "__TEXT") == 0 ) { + if ( header()->sizeofcmds + sizeof(mach_header_64) > segFileSize ) { + diag.warning("load commands exceed length of __TEXT segment"); + overflowText = true; + } + stop = true; + } + }); + if ( overflowText ) + return false; + + return true; +} + +bool MachOParser::validEmbeddedPaths(Diagnostics& diag) +{ + __block int index = 1; + __block bool allGood = true; + __block bool foundInstallName = false; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + const dylib_command* dylibCmd; + const rpath_command* rpathCmd; + switch ( cmd->cmd ) { + case LC_ID_DYLIB: + foundInstallName = true; + // fall through + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylibCmd = (dylib_command*)cmd; + if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) { + diag.warning("load command #%d name offset (%u) outside its size (%u)", index, dylibCmd->dylib.name.offset, cmd->cmdsize); + stop = true; + allGood = false; + } + else { + bool foundEnd = false; + const char* start = (char*)dylibCmd + dylibCmd->dylib.name.offset; + const char* end = (char*)dylibCmd + cmd->cmdsize; + for (const char* s=start; s < end; ++s) { + if ( *s == '\0' ) { + foundEnd = true; + break; + } + } + if ( !foundEnd ) { + diag.warning("load command #%d string extends beyond end of load command", index); + stop = true; + allGood = false; + } + } + break; + case LC_RPATH: + rpathCmd = (rpath_command*)cmd; + if ( rpathCmd->path.offset > cmd->cmdsize ) { + diag.warning("load command #%d path offset (%u) outside its size (%u)", index, rpathCmd->path.offset, cmd->cmdsize); + stop = true; + allGood = false; + } + else { + bool foundEnd = false; + const char* start = (char*)rpathCmd + rpathCmd->path.offset; + const char* end = (char*)rpathCmd + cmd->cmdsize; + for (const char* s=start; s < end; ++s) { + if ( *s == '\0' ) { + foundEnd = true; + break; + } + } + if ( !foundEnd ) { + diag.warning("load command #%d string extends beyond end of load command", index); + stop = true; + allGood = false; + } + } + break; + } + ++index; + }); + + if ( header()->filetype == MH_DYLIB ) { + if ( !foundInstallName ) { + diag.warning("MH_DYLIB is missing LC_ID_DYLIB"); + allGood = false; + } + } + else { + if ( foundInstallName ) { + diag.warning("LC_ID_DYLIB found in non-MH_DYLIB"); + allGood = false; + } + } + + return allGood; +} + +bool MachOParser::validSegments(Diagnostics& diag, size_t fileLen) +{ + // check segment load command size + __block bool badSegmentLoadCommand = false; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* seg = (segment_command_64*)cmd; + int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command_64); + if ( sectionsSpace < 0 ) { + diag.warning("load command size too small for LC_SEGMENT_64"); + badSegmentLoadCommand = true; + stop = true; + } + else if ( (sectionsSpace % sizeof(section_64)) != 0 ) { + diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize); + badSegmentLoadCommand = true; + stop = true; + } + else if ( sectionsSpace != (seg->nsects * sizeof(section_64)) ) { + diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects); + badSegmentLoadCommand = true; + stop = true; + } else if (greaterThanAddOrOverflow(seg->fileoff, seg->filesize, fileLen)) { + diag.warning("segment load command content extends beyond end of file"); + badSegmentLoadCommand = true; + stop = true; + } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) { + // dyld should support non-allocatable __LLVM segment + diag.warning("segment filesize exceeds vmsize"); + badSegmentLoadCommand = true; + stop = true; + } + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* seg = (segment_command*)cmd; + int32_t sectionsSpace = cmd->cmdsize - sizeof(segment_command); + if ( sectionsSpace < 0 ) { + diag.warning("load command size too small for LC_SEGMENT"); + badSegmentLoadCommand = true; + stop = true; + } + else if ( (sectionsSpace % sizeof(section)) != 0 ) { + diag.warning("segment load command size 0x%X will not fit whole number of sections", cmd->cmdsize); + badSegmentLoadCommand = true; + stop = true; + } + else if ( sectionsSpace != (seg->nsects * sizeof(section)) ) { + diag.warning("load command size 0x%X does not match nsects %d", cmd->cmdsize, seg->nsects); + badSegmentLoadCommand = true; + stop = true; + } else if ( (seg->filesize > seg->vmsize) && ((seg->vmsize != 0) || ((seg->flags & SG_NORELOC) == 0)) ) { + // dyld should support non-allocatable __LLVM segment + diag.warning("segment filesize exceeds vmsize"); + badSegmentLoadCommand = true; + stop = true; + } + } + }); + if ( badSegmentLoadCommand ) + return false; + + // check mapping permissions of segments + __block bool badPermissions = false; + __block bool badSize = false; + __block bool hasTEXT = false; + __block bool hasLINKEDIT = false; + forEachSegment(^(const char* segName, uint32_t segFileOffset, uint32_t segFileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( strcmp(segName, "__TEXT") == 0 ) { + if ( protections != (VM_PROT_READ|VM_PROT_EXECUTE) ) { + diag.warning("__TEXT segment permissions is not 'r-x'"); + badPermissions = true; + stop = true; + } + hasTEXT = true; + } + else if ( strcmp(segName, "__LINKEDIT") == 0 ) { + if ( protections != VM_PROT_READ ) { + diag.warning("__LINKEDIT segment permissions is not 'r--'"); + badPermissions = true; + stop = true; + } + hasLINKEDIT = true; + } + else if ( (protections & 0xFFFFFFF8) != 0 ) { + diag.warning("%s segment permissions has invalid bits set", segName); + badPermissions = true; + stop = true; + } + if (greaterThanAddOrOverflow(segFileOffset, segFileSize, fileLen)) { + diag.warning("%s segment content extends beyond end of file", segName); + badSize = true; + stop = true; + } + if ( is64() ) { + if ( vmAddr+vmSize < vmAddr ) { + diag.warning("%s segment vm range wraps", segName); + badSize = true; + stop = true; + } + } + else { + if ( (uint32_t)(vmAddr+vmSize) < (uint32_t)(vmAddr) ) { + diag.warning("%s segment vm range wraps", segName); + badSize = true; + stop = true; + } + } + }); + if ( badPermissions || badSize ) + return false; + if ( !hasTEXT ) { + diag.warning("missing __TEXT segment"); + return false; + } + if ( !hasLINKEDIT ) { + diag.warning("missing __LINKEDIT segment"); + return false; + } + + // check for overlapping segments + __block bool badSegments = false; + forEachSegment(^(const char* seg1Name, uint32_t seg1FileOffset, uint32_t seg1FileSize, uint64_t seg1vmAddr, uint64_t seg1vmSize, uint8_t seg1Protections, uint32_t seg1Index, uint64_t seg1SizeOfSections, uint8_t seg1Align, bool& stop1) { + uint64_t seg1vmEnd = seg1vmAddr + seg1vmSize; + uint32_t seg1FileEnd = seg1FileOffset + seg1FileSize; + forEachSegment(^(const char* seg2Name, uint32_t seg2FileOffset, uint32_t seg2FileSize, uint64_t seg2vmAddr, uint64_t seg2vmSize, uint8_t seg2Protections, uint32_t seg2Index, uint64_t seg2SizeOfSections, uint8_t seg2Align, bool& stop2) { + if ( seg1Index == seg2Index ) + return; + uint64_t seg2vmEnd = seg2vmAddr + seg2vmSize; + uint32_t seg2FileEnd = seg2FileOffset + seg2FileSize; + if ( ((seg2vmAddr <= seg1vmAddr) && (seg2vmEnd > seg1vmAddr) && (seg1vmEnd > seg1vmAddr)) || ((seg2vmAddr >= seg1vmAddr) && (seg2vmAddr < seg1vmEnd) && (seg2vmEnd > seg2vmAddr)) ) { + diag.warning("segment %s vm range overlaps segment %s", seg1Name, seg2Name); + badSegments = true; + stop1 = true; + stop2 = true; + } + if ( ((seg2FileOffset <= seg1FileOffset) && (seg2FileEnd > seg1FileOffset) && (seg1FileEnd > seg1FileOffset)) || ((seg2FileOffset >= seg1FileOffset) && (seg2FileOffset < seg1FileEnd) && (seg2FileEnd > seg2FileOffset)) ) { + diag.warning("segment %s file content overlaps segment %s", seg1Name, seg2Name); + badSegments = true; + stop1 = true; + stop2 = true; + } + // check for out of order segments + if ( (seg1Index < seg2Index) && !stop1 ) { + if ( (seg1vmAddr > seg2vmAddr) || ((seg1FileOffset > seg2FileOffset) && (seg1FileOffset != 0) && (seg2FileOffset != 0)) ){ + diag.warning("segment load commands out of order with respect to layout for %s and %s", seg1Name, seg2Name); + badSegments = true; + stop1 = true; + stop2 = true; + } + } + }); + }); + if ( badSegments ) + return false; + + // check sections are within segment + __block bool badSections = false; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* seg = (segment_command_64*)cmd; + const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64)); + const section_64* const sectionsEnd = §ionsStart[seg->nsects]; + for (const section_64* sect=sectionsStart; (sect < sectionsEnd); ++sect) { + if ( (int64_t)(sect->size) < 0 ) { + diag.warning("section %s size too large 0x%llX", sect->sectname, sect->size); + badSections = true; + } + else if ( sect->addr < seg->vmaddr ) { + diag.warning("section %s start address 0x%llX is before containing segment's address 0x%0llX", sect->sectname, sect->addr, seg->vmaddr); + badSections = true; + } + else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) { + diag.warning("section %s end address 0x%llX is beyond containing segment's end address 0x%0llX", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize); + badSections = true; + } + } + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* seg = (segment_command*)cmd; + const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command)); + const section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) { + if ( (int64_t)(sect->size) < 0 ) { + diag.warning("section %s size too large 0x%X", sect->sectname, sect->size); + badSections = true; + } + else if ( sect->addr < seg->vmaddr ) { + diag.warning("section %s start address 0x%X is before containing segment's address 0x%0X", sect->sectname, sect->addr, seg->vmaddr); + badSections = true; + } + else if ( sect->addr+sect->size > seg->vmaddr+seg->vmsize ) { + diag.warning("section %s end address 0x%X is beyond containing segment's end address 0x%0X", sect->sectname, sect->addr+sect->size, seg->vmaddr+seg->vmsize); + badSections = true; + } + } + } + }); + + return !badSections; +} + +struct LinkEditContent +{ + const char* name; + uint32_t stdOrder; + uint32_t fileOffsetStart; + uint32_t size; +}; + + + +bool MachOParser::validLinkeditLayout(Diagnostics& diag) +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return false; + const bool is64Bit = is64(); + const uint32_t pointerSize = (is64Bit ? 8 : 4); + + // build vector of all blobs in LINKEDIT + std::vector blobs; + if ( leInfo.dyldInfo != nullptr ) { + if ( leInfo.dyldInfo->rebase_size != 0 ) + blobs.push_back({"rebase opcodes", 1, leInfo.dyldInfo->rebase_off, leInfo.dyldInfo->rebase_size}); + if ( leInfo.dyldInfo->bind_size != 0 ) + blobs.push_back({"bind opcodes", 2, leInfo.dyldInfo->bind_off, leInfo.dyldInfo->bind_size}); + if ( leInfo.dyldInfo->weak_bind_size != 0 ) + blobs.push_back({"weak bind opcodes", 3, leInfo.dyldInfo->weak_bind_off, leInfo.dyldInfo->weak_bind_size}); + if ( leInfo.dyldInfo->lazy_bind_size != 0 ) + blobs.push_back({"lazy bind opcodes", 4, leInfo.dyldInfo->lazy_bind_off, leInfo.dyldInfo->lazy_bind_size}); + if ( leInfo.dyldInfo->export_size!= 0 ) + blobs.push_back({"exports trie", 5, leInfo.dyldInfo->export_off, leInfo.dyldInfo->export_size}); + } + if ( leInfo.dynSymTab != nullptr ) { + if ( leInfo.dynSymTab->nlocrel != 0 ) + blobs.push_back({"local relocations", 6, leInfo.dynSymTab->locreloff, static_cast(leInfo.dynSymTab->nlocrel*sizeof(relocation_info))}); + if ( leInfo.dynSymTab->nextrel != 0 ) + blobs.push_back({"external relocations", 11, leInfo.dynSymTab->extreloff, static_cast(leInfo.dynSymTab->nextrel*sizeof(relocation_info))}); + if ( leInfo.dynSymTab->nindirectsyms != 0 ) + blobs.push_back({"indirect symbol table", 12, leInfo.dynSymTab->indirectsymoff, leInfo.dynSymTab->nindirectsyms*4}); + } + if ( leInfo.splitSegInfo != nullptr ) { + if ( leInfo.splitSegInfo->datasize != 0 ) + blobs.push_back({"shared cache info", 6, leInfo.splitSegInfo->dataoff, leInfo.splitSegInfo->datasize}); + } + if ( leInfo.functionStarts != nullptr ) { + if ( leInfo.functionStarts->datasize != 0 ) + blobs.push_back({"function starts", 7, leInfo.functionStarts->dataoff, leInfo.functionStarts->datasize}); + } + if ( leInfo.dataInCode != nullptr ) { + if ( leInfo.dataInCode->datasize != 0 ) + blobs.push_back({"data in code", 8, leInfo.dataInCode->dataoff, leInfo.dataInCode->datasize}); + } + if ( leInfo.symTab != nullptr ) { + if ( leInfo.symTab->nsyms != 0 ) + blobs.push_back({"symbol table", 10, leInfo.symTab->symoff, static_cast(leInfo.symTab->nsyms*(is64Bit ? sizeof(nlist_64) : sizeof(struct nlist)))}); + if ( leInfo.symTab->strsize != 0 ) + blobs.push_back({"symbol table strings", 20, leInfo.symTab->stroff, leInfo.symTab->strsize}); + } + if ( leInfo.codeSig != nullptr ) { + if ( leInfo.codeSig->datasize != 0 ) + blobs.push_back({"code signature", 21, leInfo.codeSig->dataoff, leInfo.codeSig->datasize}); + } + + // check for bad combinations + if ( (leInfo.dyldInfo != nullptr) && (leInfo.dyldInfo->cmd == LC_DYLD_INFO_ONLY) && (leInfo.dynSymTab != nullptr) ) { + if ( leInfo.dynSymTab->nlocrel != 0 ) { + diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and local relocations"); + return false; + } + if ( leInfo.dynSymTab->nextrel != 0 ) { + diag.error("malformed mach-o contains LC_DYLD_INFO_ONLY and external relocations"); + return false; + } + } + if ( (leInfo.dyldInfo == nullptr) && (leInfo.dynSymTab == nullptr) ) { + diag.error("malformed mach-o misssing LC_DYLD_INFO and LC_DYSYMTAB"); + return false; + } + if ( blobs.empty() ) { + diag.error("malformed mach-o misssing LINKEDIT"); + return false; + } + + // sort vector by file offset and error on overlaps + std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) { + return a.fileOffsetStart < b.fileOffsetStart; + }); + uint32_t prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset); + const char* prevName = "start of LINKEDIT"; + for (const LinkEditContent& blob : blobs) { + if ( blob.fileOffsetStart < prevEnd ) { + diag.error("LINKEDIT overlap of %s and %s", prevName, blob.name); + return false; + } + prevEnd = blob.fileOffsetStart + blob.size; + prevName = blob.name; + } + const LinkEditContent& lastBlob = blobs.back(); + uint32_t linkeditFileEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset + leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileSize); + if (greaterThanAddOrOverflow(lastBlob.fileOffsetStart, lastBlob.size, linkeditFileEnd)) { + diag.error("LINKEDIT content '%s' extends beyond end of segment", lastBlob.name); + return false; + } + + // sort vector by order and warn on non standard order or mis-alignment + std::sort(blobs.begin(), blobs.end(), [&](const LinkEditContent& a, const LinkEditContent& b) { + return a.stdOrder < b.stdOrder; + }); + prevEnd = (uint32_t)(leInfo.layout.segments[leInfo.layout.linkeditSegIndex].fileOffset); + prevName = "start of LINKEDIT"; + for (const LinkEditContent& blob : blobs) { + if ( ((blob.fileOffsetStart & (pointerSize-1)) != 0) && (blob.stdOrder != 20) ) // ok for "symbol table strings" to be mis-aligned + diag.warning("mis-aligned LINKEDIT content '%s'", blob.name); + if ( blob.fileOffsetStart < prevEnd ) { + diag.warning("LINKEDIT out of order %s", blob.name); + } + prevEnd = blob.fileOffsetStart; + prevName = blob.name; + } + + // Check for invalid symbol table sizes + if ( leInfo.symTab != nullptr ) { + if ( leInfo.symTab->nsyms > 0x10000000 ) { + diag.error("malformed mach-o image: symbol table too large"); + return false; + } + if ( leInfo.dynSymTab != nullptr ) { + // validate indirect symbol table + if ( leInfo.dynSymTab->nindirectsyms != 0 ) { + if ( leInfo.dynSymTab->nindirectsyms > 0x10000000 ) { + diag.error("malformed mach-o image: indirect symbol table too large"); + return false; + } + } + if ( (leInfo.dynSymTab->nlocalsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->ilocalsym > leInfo.symTab->nsyms) ) { + diag.error("malformed mach-o image: indirect symbol table local symbol count exceeds total symbols"); + return false; + } + if ( leInfo.dynSymTab->ilocalsym + leInfo.dynSymTab->nlocalsym < leInfo.dynSymTab->ilocalsym ) { + diag.error("malformed mach-o image: indirect symbol table local symbol count wraps"); + return false; + } + if ( (leInfo.dynSymTab->nextdefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iextdefsym > leInfo.symTab->nsyms) ) { + diag.error("malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols"); + return false; + } + if ( leInfo.dynSymTab->iextdefsym + leInfo.dynSymTab->nextdefsym < leInfo.dynSymTab->iextdefsym ) { + diag.error("malformed mach-o image: indirect symbol table extern symbol count wraps"); + return false; + } + if ( (leInfo.dynSymTab->nundefsym > leInfo.symTab->nsyms) || (leInfo.dynSymTab->iundefsym > leInfo.symTab->nsyms) ) { + diag.error("malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols"); + return false; + } + if ( leInfo.dynSymTab->iundefsym + leInfo.dynSymTab->nundefsym < leInfo.dynSymTab->iundefsym ) { + diag.error("malformed mach-o image: indirect symbol table undefined symbol count wraps"); + return false; + } + } + } + + return true; +} + +bool MachOParser::isArch(const mach_header* mh, const std::string& archName) +{ + for (const ArchInfo& info : _s_archInfos) { + if ( archName == info.name ) { + return ( (mh->cputype == info.cputype) && ((mh->cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ); + } + } + return false; +} + + +std::string MachOParser::archName(uint32_t cputype, uint32_t cpusubtype) +{ + for (const ArchInfo& info : _s_archInfos) { + if ( (cputype == info.cputype) && ((cpusubtype & ~CPU_SUBTYPE_MASK) == info.cpusubtype) ) { + return info.name; + } + } + return "unknown"; +} + +uint32_t MachOParser::cpuTypeFromArchName(const std::string& archName) +{ + for (const ArchInfo& info : _s_archInfos) { + if ( archName == info.name ) { + return info.cputype; + } + } + return 0; +} + +uint32_t MachOParser::cpuSubtypeFromArchName(const std::string& archName) +{ + for (const ArchInfo& info : _s_archInfos) { + if ( archName == info.name ) { + return info.cpusubtype; + } + } + return 0; +} + +std::string MachOParser::archName() const +{ + return archName(header()->cputype, header()->cpusubtype); +} + +std::string MachOParser::platformName(Platform platform) +{ + switch ( platform ) { + case Platform::unknown: + return "unknown"; + case Platform::macOS: + return "macOS"; + case Platform::iOS: + return "iOS"; + case Platform::tvOS: + return "tvOS"; + case Platform::watchOS: + return "watchOS"; + case Platform::bridgeOS: + return "bridgeOS"; + } + return "unknown platform"; +} + +std::string MachOParser::versionString(uint32_t packedVersion) +{ + char buff[64]; + sprintf(buff, "%d.%d.%d", (packedVersion >> 16), ((packedVersion >> 8) & 0xFF), (packedVersion & 0xFF)); + return buff; +} + +#else + +bool MachOParser::isMachO(Diagnostics& diag, const void* fileContent, size_t mappedLength) +{ + // sanity check length + if ( mappedLength < 4096 ) { + diag.error("file too short"); + return false; + } + + // must start with mach-o magic value + const mach_header* mh = (const mach_header*)fileContent; +#if __LP64__ + const uint32_t requiredMagic = MH_MAGIC_64; +#else + const uint32_t requiredMagic = MH_MAGIC; +#endif + if ( mh->magic != requiredMagic ) { + diag.error("not a mach-o file"); + return false; + } + +#if __x86_64__ + const uint32_t requiredCPU = CPU_TYPE_X86_64; +#elif __i386__ + const uint32_t requiredCPU = CPU_TYPE_I386; +#elif __arm__ + const uint32_t requiredCPU = CPU_TYPE_ARM; +#elif __arm64__ + const uint32_t requiredCPU = CPU_TYPE_ARM64; +#else + #error unsupported architecture +#endif + if ( mh->cputype != requiredCPU ) { + diag.error("wrong cpu type"); + return false; + } + + return true; +} + +bool MachOParser::wellFormedMachHeaderAndLoadCommands(const mach_header* mh) +{ + const load_command* startCmds = nullptr; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return false; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return false; + } + cmd = nextCmd; + } + return true; +} + +#endif + +Platform MachOParser::currentPlatform() +{ +#if TARGET_OS_BRIDGE + return Platform::bridgeOS; +#elif TARGET_OS_WATCH + return Platform::watchOS; +#elif TARGET_OS_TV + return Platform::tvOS; +#elif TARGET_OS_IOS + return Platform::iOS; +#elif TARGET_OS_MAC + return Platform::macOS; +#else + #error unknown platform +#endif +} + + +bool MachOParser::valid(Diagnostics& diag) +{ +#if DYLD_IN_PROCESS + // only images loaded by dyld to be parsed + const mach_header* inImage = dyld3::dyld_image_header_containing_address(header()); + if ( inImage != header() ) { + diag.error("only dyld loaded images can be parsed by MachOParser"); + return false; + } +#else + +#endif + return true; +} + + +void MachOParser::forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const +{ + bool stop = false; + const load_command* startCmds = nullptr; + if ( header()->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)header() + sizeof(mach_header_64)); + else if ( header()->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)header() + sizeof(mach_header)); + else { + diag.error("file does not start with MH_MAGIC[_64]"); + return; // not a mach-o file, or wrong endianness + } + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + header()->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < header()->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( cmd->cmdsize < 8 ) { + diag.error("malformed load command #%d, size too small %d", i, cmd->cmdsize); + return; + } + if ( (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + diag.error("malformed load command #%d, size too large 0x%X", i, cmd->cmdsize); + return; + } + callback(cmd, stop); + if ( stop ) + return; + cmd = nextCmd; + } +} + +UUID MachOParser::uuid() const +{ + uuid_t uuid; + getUuid(uuid); + return uuid; +} + +bool MachOParser::getUuid(uuid_t uuid) const +{ + Diagnostics diag; + __block bool found = false; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_UUID ) { + const uuid_command* uc = (const uuid_command*)cmd; + memcpy(uuid, uc->uuid, sizeof(uuid_t)); + found = true; + stop = true; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call + if ( !found ) + bzero(uuid, sizeof(uuid_t)); + return found; +} + +uint64_t MachOParser::preferredLoadAddress() const +{ + __block uint64_t result = 0; + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( strcmp(segName, "__TEXT") == 0 ) { + result = vmAddr; + stop = true; + } + }); + return result; +} + +bool MachOParser::getPlatformAndVersion(Platform* platform, uint32_t* minOS, uint32_t* sdk) const +{ + Diagnostics diag; + __block bool found = false; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + const version_min_command* versCmd; + switch ( cmd->cmd ) { + case LC_VERSION_MIN_IPHONEOS: + versCmd = (version_min_command*)cmd; + *platform = Platform::iOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + found = true; + stop = true; + break; + case LC_VERSION_MIN_MACOSX: + versCmd = (version_min_command*)cmd; + *platform = Platform::macOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + found = true; + stop = true; + break; + case LC_VERSION_MIN_TVOS: + versCmd = (version_min_command*)cmd; + *platform = Platform::tvOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + found = true; + stop = true; + break; + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + *platform = Platform::watchOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + found = true; + stop = true; + break; + case LC_BUILD_VERSION: { + const build_version_command* buildCmd = (build_version_command *)cmd; + *minOS = buildCmd->minos; + *sdk = buildCmd->sdk; + + switch(buildCmd->platform) { + /* Known values for the platform field above. */ + case PLATFORM_MACOS: + *platform = Platform::macOS; + break; + case PLATFORM_IOS: + *platform = Platform::iOS; + break; + case PLATFORM_TVOS: + *platform = Platform::tvOS; + break; + case PLATFORM_WATCHOS: + *platform = Platform::watchOS; + break; + case PLATFORM_BRIDGEOS: + *platform = Platform::bridgeOS; + break; + } + found = true; + stop = true; + } break; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call + return found; +} + + +bool MachOParser::isSimulatorBinary() const +{ + Platform platform; + uint32_t minOS; + uint32_t sdk; + switch ( header()->cputype ) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) { + return (platform != Platform::macOS); + } + break; + } + return false; +} + + +bool MachOParser::getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const +{ + Diagnostics diag; + __block bool found = false; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_ID_DYLIB ) { + const dylib_command* dylibCmd = (dylib_command*)cmd; + *compatVersion = dylibCmd->dylib.compatibility_version; + *currentVersion = dylibCmd->dylib.current_version; + *installName = (char*)dylibCmd + dylibCmd->dylib.name.offset; + found = true; + stop = true; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call + return found; +} + +const char* MachOParser::installName() const +{ + assert(header()->filetype == MH_DYLIB); + const char* result; + uint32_t ignoreVersion; + assert(getDylibInstallName(&result, &ignoreVersion, &ignoreVersion)); + return result; +} + + +uint32_t MachOParser::dependentDylibCount() const +{ + __block uint32_t count = 0; + forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + ++count; + }); + return count; +} + +const char* MachOParser::dependentDylibLoadPath(uint32_t depIndex) const +{ + __block const char* foundLoadPath = nullptr; + __block uint32_t curDepIndex = 0; + forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + if ( curDepIndex == depIndex ) { + foundLoadPath = loadPath; + stop = true; + } + ++curDepIndex; + }); + return foundLoadPath; +} + + +void MachOParser::forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const +{ + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + switch ( cmd->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: { + const dylib_command* dylibCmd = (dylib_command*)cmd; + assert(dylibCmd->dylib.name.offset < cmd->cmdsize); + const char* loadPath = (char*)dylibCmd + dylibCmd->dylib.name.offset; + callback(loadPath, (cmd->cmd == LC_LOAD_WEAK_DYLIB), (cmd->cmd == LC_REEXPORT_DYLIB), (cmd->cmd == LC_LOAD_UPWARD_DYLIB), + dylibCmd->dylib.compatibility_version, dylibCmd->dylib.current_version, stop); + } + break; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call +} + +void MachOParser::forEachRPath(void (^callback)(const char* rPath, bool& stop)) const +{ + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_RPATH ) { + const char* rpath = (char*)cmd + ((struct rpath_command*)cmd)->path.offset; + callback(rpath, stop); + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call +} + +/* + struct LayoutInfo { +#if DYLD_IN_PROCESS + uintptr_t slide; + uintptr_t textUnslidVMAddr; + uintptr_t linkeditUnslidVMAddr; + uint32_t linkeditFileOffset; +#else + uint32_t segmentCount; + uint32_t linkeditSegIndex; + struct { + uint64_t mappingOffset; + uint64_t fileOffset; + uint64_t segUnslidAddress; + uint64_t segSize; + } segments[16]; +#endif + }; +*/ + +#if !DYLD_IN_PROCESS +const uint8_t* MachOParser::getContentForVMAddr(const LayoutInfo& info, uint64_t addr) const +{ + for (uint32_t i=0; i < info.segmentCount; ++i) { + if ( (addr >= info.segments[i].segUnslidAddress) && (addr < (info.segments[i].segUnslidAddress+info.segments[i].segSize)) ) + return (uint8_t*)header() + info.segments[i].mappingOffset + (addr - info.segments[i].segUnslidAddress); + } + // value is outside this image. could be pointer into another image + if ( inDyldCache() ) { + return (uint8_t*)header() + info.segments[0].mappingOffset + (addr - info.segments[0].segUnslidAddress); + } + assert(0 && "address not found in segment"); + return nullptr; +} +#endif + +const uint8_t* MachOParser::getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const +{ +#if DYLD_IN_PROCESS + uint32_t offsetInLinkedit = fileOffset - info.linkeditFileOffset; + uintptr_t linkeditStartAddr = info.linkeditUnslidVMAddr + info.slide; + return (uint8_t*)(linkeditStartAddr + offsetInLinkedit); +#else + uint32_t offsetInLinkedit = fileOffset - (uint32_t)(info.segments[info.linkeditSegIndex].fileOffset); + const uint8_t* linkeditStart = (uint8_t*)header() + info.segments[info.linkeditSegIndex].mappingOffset; + return linkeditStart + offsetInLinkedit; +#endif +} + + +void MachOParser::getLayoutInfo(LayoutInfo& result) const +{ +#if DYLD_IN_PROCESS + // image loaded by dyld, just record the addr and file offset of TEXT and LINKEDIT segments + result.slide = getSlide(); + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( strcmp(segName, "__TEXT") == 0 ) { + result.textUnslidVMAddr = (uintptr_t)vmAddr; + } + else if ( strcmp(segName, "__LINKEDIT") == 0 ) { + result.linkeditUnslidVMAddr = (uintptr_t)vmAddr; + result.linkeditFileOffset = fileOffset; + } + }); +#else + bool inCache = inDyldCache(); + bool intel32 = (header()->cputype == CPU_TYPE_I386); + result.segmentCount = 0; + result.linkeditSegIndex = 0xFFFFFFFF; + __block uint64_t textSegAddr = 0; + __block uint64_t textSegFileOffset = 0; + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + auto& segInfo = result.segments[result.segmentCount]; + if ( strcmp(segName, "__TEXT") == 0 ) { + textSegAddr = vmAddr; + textSegFileOffset = fileOffset; + } + __block bool textRelocsAllowed = false; + if ( intel32 ) { + forEachSection(^(const char* curSegName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags, + uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) { + if ( strcmp(curSegName, segName) == 0 ) { + if ( sectFlags & (S_ATTR_EXT_RELOC|S_ATTR_LOC_RELOC) ) { + textRelocsAllowed = true; + sectStop = true; + } + } + }); + } + if ( inCache ) { + if ( inRawCache() ) { + // whole cache file mapped somewhere (padding not expanded) + // vmaddrs are useless. only file offset make sense + segInfo.mappingOffset = fileOffset - textSegFileOffset; + } + else { + // cache file was loaded by dyld into shared region + // vmaddrs of segments are correct except for ASLR slide + segInfo.mappingOffset = vmAddr - textSegAddr; + } + } + else { + // individual mach-o file mapped in one region, so mappingOffset == fileOffset + segInfo.mappingOffset = fileOffset; + } + segInfo.fileOffset = fileOffset; + segInfo.fileSize = fileSize; + segInfo.segUnslidAddress = vmAddr; + segInfo.segSize = vmSize; + segInfo.writable = ((protections & VM_PROT_WRITE) == VM_PROT_WRITE); + segInfo.executable = ((protections & VM_PROT_EXECUTE) == VM_PROT_EXECUTE); + segInfo.textRelocsAllowed = textRelocsAllowed; + if ( strcmp(segName, "__LINKEDIT") == 0 ) { + result.linkeditSegIndex = result.segmentCount; + } + ++result.segmentCount; + if ( result.segmentCount > 127 ) + stop = true; + }); +#endif +} + + +void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, + const void* content, size_t size, bool illegalSectionSize, bool& stop)) const +{ + forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, + const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) { + callback(segName, sectionName, flags, content, (size_t)size, illegalSectionSize, stop); + }); +} + +void MachOParser::forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, + const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, + bool illegalSectionSize, bool& stop)) const +{ + Diagnostics diag; + //fprintf(stderr, "forEachSection() mh=%p\n", header()); + LayoutInfo layout; + getLayoutInfo(layout); + forEachSection(^(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags, + uint64_t sectAddr, uint64_t sectSize, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) { + #if DYLD_IN_PROCESS + const uint8_t* segContentStart = (uint8_t*)(segVMAddr + layout.slide); + #else + const uint8_t* segContentStart = (uint8_t*)header() + layout.segments[segIndex].mappingOffset; + #endif + const void* contentAddr = segContentStart + (sectAddr - segVMAddr); + callback(segName, sectionName, sectFlags, sectAddr, contentAddr, sectSize, alignP2, reserved1, reserved2, illegalSectionSize, stop); + }); + +} + +// this iterator just walks the segment/section array. It does interpret addresses +void MachOParser::forEachSection(void (^callback)(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags, + uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const +{ + Diagnostics diag; + //fprintf(stderr, "forEachSection() mh=%p\n", header()); + __block uint32_t segIndex = 0; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* seg = (segment_command_64*)cmd; + const section_64* const sectionsStart = (section_64*)((char*)seg + sizeof(struct segment_command_64)); + const section_64* const sectionsEnd = §ionsStart[seg->nsects]; + for (const section_64* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) { + const char* sectName = sect->sectname; + char sectNameCopy[20]; + if ( sectName[15] != '\0' ) { + strlcpy(sectNameCopy, sectName, 17); + sectName = sectNameCopy; + } + bool illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize); + callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop); + } + ++segIndex; + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* seg = (segment_command*)cmd; + const section* const sectionsStart = (section*)((char*)seg + sizeof(struct segment_command)); + const section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const section* sect=sectionsStart; !stop && (sect < sectionsEnd); ++sect) { + const char* sectName = sect->sectname; + char sectNameCopy[20]; + if ( sectName[15] != '\0' ) { + strlcpy(sectNameCopy, sectName, 17); + sectName = sectNameCopy; + } + bool illegalSectionSize = (sect->addr < seg->vmaddr) || greaterThanAddOrOverflow(sect->addr, sect->size, seg->vmaddr + seg->filesize); + callback(seg->segname, segIndex, seg->vmaddr, sectName, sect->flags, sect->addr, sect->size, sect->align, sect->reserved1, sect->reserved2, illegalSectionSize, stop); + } + ++segIndex; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call +} + +void MachOParser::forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return; + + const bool is64Bit = is64(); + if ( leInfo.symTab != nullptr ) { + uint32_t globalsStartIndex = 0; + uint32_t globalsCount = leInfo.symTab->nsyms; + if ( leInfo.dynSymTab != nullptr ) { + globalsStartIndex = leInfo.dynSymTab->iextdefsym; + globalsCount = leInfo.dynSymTab->nextdefsym; + } + uint32_t maxStringOffset = leInfo.symTab->strsize; + const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff); + const struct nlist* symbols = (struct nlist*) (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff)); + const struct nlist_64* symbols64 = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff)); + bool stop = false; + for (uint32_t i=0; (i < globalsCount) && !stop; ++i) { + if ( is64Bit ) { + const struct nlist_64& sym = symbols64[globalsStartIndex+i]; + if ( sym.n_un.n_strx > maxStringOffset ) + continue; + if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) ) + callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop); + } + else { + const struct nlist& sym = symbols[globalsStartIndex+i]; + if ( sym.n_un.n_strx > maxStringOffset ) + continue; + if ( (sym.n_type & N_EXT) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) ) + callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop); + } + } + } +} + +void MachOParser::forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return; + + const bool is64Bit = is64(); + if ( leInfo.symTab != nullptr ) { + uint32_t localsStartIndex = 0; + uint32_t localsCount = leInfo.symTab->nsyms; + if ( leInfo.dynSymTab != nullptr ) { + localsStartIndex = leInfo.dynSymTab->ilocalsym; + localsCount = leInfo.dynSymTab->nlocalsym; + } + uint32_t maxStringOffset = leInfo.symTab->strsize; + const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff); + const struct nlist* symbols = (struct nlist*) (getLinkEditContent(leInfo.layout, leInfo.symTab->symoff)); + const struct nlist_64* symbols64 = (struct nlist_64*)(getLinkEditContent(leInfo.layout, leInfo.symTab->symoff)); + bool stop = false; + for (uint32_t i=0; (i < localsCount) && !stop; ++i) { + if ( is64Bit ) { + const struct nlist_64& sym = symbols64[localsStartIndex+i]; + if ( sym.n_un.n_strx > maxStringOffset ) + continue; + if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) ) + callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop); + } + else { + const struct nlist& sym = symbols[localsStartIndex+i]; + if ( sym.n_un.n_strx > maxStringOffset ) + continue; + if ( ((sym.n_type & N_EXT) == 0) && ((sym.n_type & N_TYPE) == N_SECT) && ((sym.n_type & N_STAB) == 0) ) + callback(&stringPool[sym.n_un.n_strx], sym.n_value, sym.n_type, sym.n_sect, sym.n_desc, stop); + } + } + } +} + + +bool MachOParser::findExportedSymbol(Diagnostics& diag, const char* symbolName, void* extra, FoundSymbol& foundInfo, DependentFinder findDependent) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return false; + if ( leInfo.dyldInfo != nullptr ) { + const uint8_t* trieStart = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off); + const uint8_t* trieEnd = trieStart + leInfo.dyldInfo->export_size; + const uint8_t* node = trieWalk(diag, trieStart, trieEnd, symbolName); + if ( node == nullptr ) { + // symbol not exported from this image. Seach any re-exported dylibs + __block unsigned depIndex = 0; + __block bool foundInReExportedDylib = false; + forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + if ( isReExport && findDependent ) { + const mach_header* depMH; + void* depExtra; + if ( findDependent(depIndex, loadPath, extra, &depMH, &depExtra) ) { + bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000); + MachOParser dep(depMH, depInRawCache); + if ( dep.findExportedSymbol(diag, symbolName, depExtra, foundInfo, findDependent) ) { + stop = true; + foundInReExportedDylib = true; + } + } + else { + fprintf(stderr, "could not find re-exported dylib %s\n", loadPath); + } + } + ++depIndex; + }); + return foundInReExportedDylib; + } + const uint8_t* p = node; + const uint64_t flags = read_uleb128(diag, p, trieEnd); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( !findDependent ) + return false; + // re-export from another dylib, lookup there + const uint64_t ordinal = read_uleb128(diag, p, trieEnd); + const char* importedName = (char*)p; + if ( importedName[0] == '\0' ) + importedName = symbolName; + assert(ordinal >= 1); + if (ordinal > dependentDylibCount()) { + diag.error("ordinal %lld out of range for %s", ordinal, symbolName); + return false; + } + uint32_t depIndex = (uint32_t)(ordinal-1); + const mach_header* depMH; + void* depExtra; + if ( findDependent(depIndex, dependentDylibLoadPath(depIndex), extra, &depMH, &depExtra) ) { + bool depInRawCache = inRawCache() && (depMH->flags & 0x80000000); + MachOParser depParser(depMH, depInRawCache); + return depParser.findExportedSymbol(diag, importedName, depExtra, foundInfo, findDependent); + } + else { + diag.error("dependent dylib %lld not found for re-exported symbol %s", ordinal, symbolName); + return false; + } + } + foundInfo.kind = FoundSymbol::Kind::headerOffset; + foundInfo.isThreadLocal = false; + foundInfo.foundInDylib = header(); + foundInfo.foundExtra = extra; + foundInfo.value = read_uleb128(diag, p, trieEnd); + foundInfo.resolverFuncOffset = 0; + foundInfo.foundSymbolName = symbolName; + if ( diag.hasError() ) + return false; + switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + foundInfo.kind = FoundSymbol::Kind::headerOffset; + foundInfo.resolverFuncOffset = (uint32_t)read_uleb128(diag, p, trieEnd); + } + else { + foundInfo.kind = FoundSymbol::Kind::headerOffset; + } + break; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + foundInfo.isThreadLocal = true; + break; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + foundInfo.kind = FoundSymbol::Kind::absolute; + break; + default: + diag.error("unsupported exported symbol kind. flags=%llu at node offset=0x%0lX", flags, (long)(node-trieStart)); + return false; + } + return true; + } + else { + // this is an old binary (before macOS 10.6), scan the symbol table + foundInfo.foundInDylib = nullptr; + uint64_t baseAddress = preferredLoadAddress(); + forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) { + if ( strcmp(aSymbolName, symbolName) == 0 ) { + foundInfo.kind = FoundSymbol::Kind::headerOffset; + foundInfo.isThreadLocal = false; + foundInfo.foundInDylib = header(); + foundInfo.foundExtra = extra; + foundInfo.value = n_value - baseAddress; + foundInfo.resolverFuncOffset = 0; + foundInfo.foundSymbolName = symbolName; + stop = true; + } + }); + return (foundInfo.foundInDylib != nullptr); + } +} + + +void MachOParser::getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const +{ + result.dyldInfo = nullptr; + result.symTab = nullptr; + result.dynSymTab = nullptr; + result.splitSegInfo = nullptr; + result.functionStarts = nullptr; + result.dataInCode = nullptr; + result.codeSig = nullptr; + __block bool hasUUID = false; + __block bool hasVersion = false; + __block bool hasEncrypt = false; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + switch ( cmd->cmd ) { + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + if ( cmd->cmdsize != sizeof(dyld_info_command) ) + diag.error("LC_DYLD_INFO load command size wrong"); + else if ( result.dyldInfo != nullptr ) + diag.error("multiple LC_DYLD_INFO load commands"); + result.dyldInfo = (dyld_info_command*)cmd; + break; + case LC_SYMTAB: + if ( cmd->cmdsize != sizeof(symtab_command) ) + diag.error("LC_SYMTAB load command size wrong"); + else if ( result.symTab != nullptr ) + diag.error("multiple LC_SYMTAB load commands"); + result.symTab = (symtab_command*)cmd; + break; + case LC_DYSYMTAB: + if ( cmd->cmdsize != sizeof(dysymtab_command) ) + diag.error("LC_DYSYMTAB load command size wrong"); + else if ( result.dynSymTab != nullptr ) + diag.error("multiple LC_DYSYMTAB load commands"); + result.dynSymTab = (dysymtab_command*)cmd; + break; + case LC_SEGMENT_SPLIT_INFO: + if ( cmd->cmdsize != sizeof(linkedit_data_command) ) + diag.error("LC_SEGMENT_SPLIT_INFO load command size wrong"); + else if ( result.splitSegInfo != nullptr ) + diag.error("multiple LC_SEGMENT_SPLIT_INFO load commands"); + result.splitSegInfo = (linkedit_data_command*)cmd; + break; + case LC_FUNCTION_STARTS: + if ( cmd->cmdsize != sizeof(linkedit_data_command) ) + diag.error("LC_FUNCTION_STARTS load command size wrong"); + else if ( result.functionStarts != nullptr ) + diag.error("multiple LC_FUNCTION_STARTS load commands"); + result.functionStarts = (linkedit_data_command*)cmd; + break; + case LC_DATA_IN_CODE: + if ( cmd->cmdsize != sizeof(linkedit_data_command) ) + diag.error("LC_DATA_IN_CODE load command size wrong"); + else if ( result.dataInCode != nullptr ) + diag.error("multiple LC_DATA_IN_CODE load commands"); + result.dataInCode = (linkedit_data_command*)cmd; + break; + case LC_CODE_SIGNATURE: + if ( cmd->cmdsize != sizeof(linkedit_data_command) ) + diag.error("LC_CODE_SIGNATURE load command size wrong"); + else if ( result.codeSig != nullptr ) + diag.error("multiple LC_CODE_SIGNATURE load commands"); + result.codeSig = (linkedit_data_command*)cmd; + break; + case LC_UUID: + if ( cmd->cmdsize != sizeof(uuid_command) ) + diag.error("LC_UUID load command size wrong"); + else if ( hasUUID ) + diag.error("multiple LC_UUID load commands"); + hasUUID = true; + break; + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + if ( cmd->cmdsize != sizeof(version_min_command) ) + diag.error("LC_VERSION_* load command size wrong"); + else if ( hasVersion ) + diag.error("multiple LC_VERSION_MIN_* load commands"); + hasVersion = true; + break; + case LC_BUILD_VERSION: + if ( cmd->cmdsize != (sizeof(build_version_command) + ((build_version_command*)cmd)->ntools * sizeof(build_tool_version)) ) + diag.error("LC_BUILD_VERSION load command size wrong"); + else if ( hasVersion ) + diag.error("multiple LC_BUILD_VERSION load commands"); + hasVersion = true; + break; + case LC_ENCRYPTION_INFO: + if ( cmd->cmdsize != sizeof(encryption_info_command) ) + diag.error("LC_ENCRYPTION_INFO load command size wrong"); + else if ( hasEncrypt ) + diag.error("multiple LC_ENCRYPTION_INFO load commands"); + else if ( is64() ) + diag.error("LC_ENCRYPTION_INFO found in 64-bit mach-o"); + hasEncrypt = true; + break; + case LC_ENCRYPTION_INFO_64: + if ( cmd->cmdsize != sizeof(encryption_info_command_64) ) + diag.error("LC_ENCRYPTION_INFO_64 load command size wrong"); + else if ( hasEncrypt ) + diag.error("multiple LC_ENCRYPTION_INFO_64 load commands"); + else if ( !is64() ) + diag.error("LC_ENCRYPTION_INFO_64 found in 32-bit mach-o"); + hasEncrypt = true; + break; + } + }); + if ( diag.noError() && (result.dynSymTab != nullptr) && (result.symTab == nullptr) ) + diag.error("LC_DYSYMTAB but no LC_SYMTAB load command"); + +} + +void MachOParser::getLinkEditPointers(Diagnostics& diag, LinkEditInfo& result) const +{ + getLinkEditLoadCommands(diag, result); + if ( diag.noError() ) + getLayoutInfo(result.layout); +} + +void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop)) const +{ + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* seg = (segment_command_64*)cmd; + callback(seg->segname, (uint32_t)seg->fileoff, (uint32_t)seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop); + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* seg = (segment_command*)cmd; + callback(seg->segname, seg->fileoff, seg->filesize, seg->vmaddr, seg->vmsize, seg->initprot, stop); + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call +} + +const uint8_t* MachOParser::trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol) +{ + uint32_t visitedNodeOffsets[128]; + int visitedNodeOffsetCount = 0; + visitedNodeOffsets[visitedNodeOffsetCount++] = 0; + const uint8_t* p = start; + while ( p < end ) { + uint64_t terminalSize = *p++; + if ( terminalSize > 127 ) { + // except for re-export-with-rename, all terminal sizes fit in one byte + --p; + terminalSize = read_uleb128(diag, p, end); + if ( diag.hasError() ) + return nullptr; + } + if ( (*symbol == '\0') && (terminalSize != 0) ) { + return p; + } + const uint8_t* children = p + terminalSize; + if ( children > end ) { + diag.error("malformed trie node, terminalSize=0x%llX extends past end of trie\n", terminalSize); + return nullptr; + } + uint8_t childrenRemaining = *children++; + p = children; + uint64_t nodeOffset = 0; + for (; childrenRemaining > 0; --childrenRemaining) { + const char* ss = symbol; + bool wrongEdge = false; + // scan whole edge to get to next edge + // if edge is longer than target symbol name, don't read past end of symbol name + char c = *p; + while ( c != '\0' ) { + if ( !wrongEdge ) { + if ( c != *ss ) + wrongEdge = true; + ++ss; + } + ++p; + c = *p; + } + if ( wrongEdge ) { + // advance to next child + ++p; // skip over zero terminator + // skip over uleb128 until last byte is found + while ( (*p & 0x80) != 0 ) + ++p; + ++p; // skip over last byte of uleb128 + if ( p > end ) { + diag.error("malformed trie node, child node extends past end of trie\n"); + return nullptr; + } + } + else { + // the symbol so far matches this edge (child) + // so advance to the child's node + ++p; + nodeOffset = read_uleb128(diag, p, end); + if ( diag.hasError() ) + return nullptr; + if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) { + diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset); + return nullptr; + } + symbol = ss; + break; + } + } + if ( nodeOffset != 0 ) { + if ( nodeOffset > (end-start) ) { + diag.error("malformed trie child, nodeOffset=0x%llX out of range\n", nodeOffset); + return nullptr; + } + for (int i=0; i < visitedNodeOffsetCount; ++i) { + if ( visitedNodeOffsets[i] == nodeOffset ) { + diag.error("malformed trie child, cycle to nodeOffset=0x%llX\n", nodeOffset); + return nullptr; + } + } + visitedNodeOffsets[visitedNodeOffsetCount++] = (uint32_t)nodeOffset; + if ( visitedNodeOffsetCount >= 128 ) { + diag.error("malformed trie too deep\n"); + return nullptr; + } + p = &start[nodeOffset]; + } + else + p = end; + } + return nullptr; +} + + +uint64_t MachOParser::read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if ( p == end ) { + diag.error("malformed uleb128"); + break; + } + uint64_t slice = *p & 0x7f; + + if ( bit > 63 ) { + diag.error("uleb128 too big for uint64"); + break; + } + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + +int64_t MachOParser::read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte = 0; + do { + if ( p == end ) { + diag.error("malformed sleb128"); + break; + } + byte = *p++; + result |= (((int64_t)(byte & 0x7f)) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + +bool MachOParser::is64() const +{ +#if DYLD_IN_PROCESS + return (sizeof(void*) == 8); +#else + return (header()->magic == MH_MAGIC_64); +#endif +} + + + + +bool MachOParser::findClosestSymbol(uint64_t targetUnslidAddress, const char** symbolName, uint64_t* symbolUnslidAddr) const +{ + Diagnostics diag; + __block uint64_t closestNValueSoFar = 0; + __block const char* closestNameSoFar = nullptr; + forEachGlobalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) { + if ( n_value <= targetUnslidAddress ) { + if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) { + closestNValueSoFar = n_value; + closestNameSoFar = aSymbolName; + } + } + }); + forEachLocalSymbol(diag, ^(const char* aSymbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop) { + if ( n_value <= targetUnslidAddress ) { + if ( (closestNameSoFar == nullptr) || (closestNValueSoFar < n_value) ) { + closestNValueSoFar = n_value; + closestNameSoFar = aSymbolName; + } + } + }); + if ( closestNameSoFar == nullptr ) { + return false; + } + + *symbolName = closestNameSoFar; + *symbolUnslidAddr = closestNValueSoFar; + return true; +} + + +#if DYLD_IN_PROCESS + +bool MachOParser::findClosestSymbol(const void* addr, const char** symbolName, const void** symbolAddress) const +{ + uint64_t slide = getSlide(); + uint64_t symbolUnslidAddr; + if ( findClosestSymbol((uint64_t)addr - slide, symbolName, &symbolUnslidAddr) ) { + *symbolAddress = (const void*)(long)(symbolUnslidAddr + slide); + return true; + } + return false; +} + +intptr_t MachOParser::getSlide() const +{ + Diagnostics diag; + __block intptr_t slide = 0; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { +#if __LP64__ + if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* seg = (segment_command_64*)cmd; + if ( strcmp(seg->segname, "__TEXT") == 0 ) { + slide = ((uint64_t)header()) - seg->vmaddr; + stop = true; + } + } +#else + if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* seg = (segment_command*)cmd; + if ( strcmp(seg->segname, "__TEXT") == 0 ) { + slide = ((uint32_t)header()) - seg->vmaddr; + stop = true; + } + } +#endif + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call + return slide; +} + +// this is only used by dlsym() at runtime. All other binding is done when the closure is built. +bool MachOParser::hasExportedSymbol(const char* symbolName, DependentFinder finder, void** result) const +{ + typedef void* (*ResolverFunc)(void); + ResolverFunc resolver; + Diagnostics diag; + FoundSymbol foundInfo; + if ( findExportedSymbol(diag, symbolName, (void*)header(), foundInfo, finder) ) { + switch ( foundInfo.kind ) { + case FoundSymbol::Kind::headerOffset: + *result = (uint8_t*)foundInfo.foundInDylib + foundInfo.value; + break; + case FoundSymbol::Kind::absolute: + *result = (void*)(long)foundInfo.value; + break; + case FoundSymbol::Kind::resolverOffset: + // foundInfo.value contains "stub". + // in dlsym() we want to call resolver function to get final function address + resolver = (ResolverFunc)((uint8_t*)foundInfo.foundInDylib + foundInfo.resolverFuncOffset); + *result = (*resolver)(); + break; + } + return true; + } + return false; +} + +const char* MachOParser::segmentName(uint32_t targetSegIndex) const +{ + __block const char* result = nullptr; + __block uint32_t segIndex = 0; + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( segIndex == targetSegIndex ) { + result = segName; + stop = true; + } + ++segIndex; + }); + return result; +} + +#else + + +bool MachOParser::uses16KPages() const +{ + return (header()->cputype == CPU_TYPE_ARM64); +} + + +bool MachOParser::isEncrypted() const +{ + __block bool result = false; + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* segCmd = (segment_command_64*)cmd; + if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) { + result = true; + stop = true; + } + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* segCmd = (segment_command*)cmd; + if ( segCmd->flags & SG_PROTECTED_VERSION_1 ) { + result = true; + stop = true; + } + } + else if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) { + const encryption_info_command* encCmd = (encryption_info_command*)cmd; + if ( encCmd->cryptid != 0 ) { + result = true; + stop = true; + } + } + }); + return result; +} + +bool MachOParser::hasWeakDefs() const +{ + return (header()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)); +} + +bool MachOParser::hasObjC() const +{ + __block bool result = false; + forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) { + if ( (strncmp(sectionName, "__objc_imageinfo", 16) == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) { + result = true; + stop = true; + } + if ( (header()->cputype == CPU_TYPE_I386) && (strcmp(sectionName, "__image_info") == 0) && (strcmp(segmentName, "__OBJC") == 0) ) { + result = true; + stop = true; + } + }); + return result; +} + +bool MachOParser::hasPlusLoadMethod(Diagnostics& diag) const +{ +#if 1 + __block bool result = false; + forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) { + if ( ( (flags & SECTION_TYPE) == S_CSTRING_LITERALS ) ) { + if (illegalSectionSize) { + diag.error("cstring section %s/%s extends beyond the end of the segment", segmentName, sectionName); + return; + } + const char* s = (char*)content; + const char* end = s + size; + while ( s < end ) { + if ( strcmp(s, "load") == 0 ) { + result = true; + stop = true; + return; + } + while (*s != '\0' ) + ++s; + ++s; + } + } + }); + return result; +#else + LayoutInfo layout; + getLayoutInfo(layout); + + __block bool hasSwift = false; + __block const void* classList = nullptr; + __block size_t classListSize = 0; + __block const void* objcData = nullptr; + __block size_t objcDataSize = 0; + __block const void* objcConstData = nullptr; + __block size_t objcConstDataSize = 0; + forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool& stop) { + if ( (strcmp(sectionName, "__objc_classlist") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) { + classList = content; + classListSize = size; + } + if ( (strcmp(sectionName, "__objc_imageinfo") == 0) && (strncmp(segmentName, "__DATA", 6) == 0) ) { + const uint32_t* info = (uint32_t*)content; + uint8_t swiftVersion = (info[1] >> 8) & 0xFF; + if ( swiftVersion != 0 ) + hasSwift = true; + } + }); + if ( classList == nullptr ) + return false; + // FIXME: might be objc and swift intermixed + if ( hasSwift ) + return true; + const bool p64 = is64(); + const uint32_t pointerSize = (p64 ? 8 : 4); + const uint64_t* classArray64 = (uint64_t*)classList; + const uint32_t* classArray32 = (uint32_t*)classList; + const uint32_t classListCount = (uint32_t)(classListSize/pointerSize); + for (uint32_t i=0; i < classListCount; ++i) { + if ( p64 ) { + uint64_t classObjAddr = classArray64[i]; + const uint64_t* classObjContent = (uint64_t*)getContentForVMAddr(layout, classObjAddr); + uint64_t classROAddr = classObjContent[4]; + uint64_t metaClassObjAddr = classObjContent[0]; + const uint64_t* metaClassObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassObjAddr); + uint64_t metaClassROObjAddr = metaClassObjContent[4]; + const uint64_t* metaClassROObjContent = (uint64_t*)getContentForVMAddr(layout, metaClassROObjAddr); + uint64_t metaClassMethodListAddr = metaClassROObjContent[4]; + if ( metaClassMethodListAddr != 0 ) { + const uint64_t* metaClassMethodListContent = (uint64_t*)getContentForVMAddr(layout, metaClassMethodListAddr); + const uint32_t methodListCount = ((uint32_t*)metaClassMethodListContent)[1]; + for (uint32_t m=0; m < methodListCount; ++m) { + uint64_t methodNameAddr = metaClassMethodListContent[m*3+1]; + const char* methodNameContent = (char*)getContentForVMAddr(layout, methodNameAddr); + if ( strcmp(methodNameContent, "load") == 0 ) { + return true; + } + } + } + } + else { + + } + } + + return false; +#endif +} + +bool MachOParser::getCDHash(uint8_t cdHash[20]) +{ + Diagnostics diag; + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() || (leInfo.codeSig == nullptr) ) + return false; + + return cdHashOfCodeSignature(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize, cdHash); + } + +bool MachOParser::usesLibraryValidation() const +{ + Diagnostics diag; + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() || (leInfo.codeSig == nullptr) ) + return false; + + const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(getLinkEditContent(leInfo.layout, leInfo.codeSig->dataoff), leInfo.codeSig->datasize); + if ( cd == nullptr ) + return false; + + // check for CS_REQUIRE_LV in CS_CodeDirectory.flags + return (htonl(cd->flags) & CS_REQUIRE_LV); + } + + +bool MachOParser::isRestricted() const +{ + __block bool result = false; + forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) { + if ( (strcmp(segName, "__RESTRICT") == 0) && (strcmp(sectionName, "__restrict") == 0) ) { + result = true; + stop = true; + } + + }); + return result; +} + +bool MachOParser::hasCodeSignature(uint32_t& fileOffset, uint32_t& size) +{ + fileOffset = 0; + size = 0; + + // ignore code signatures in macOS binaries built with pre-10.9 tools + Platform platform; + uint32_t minOS; + uint32_t sdk; + if ( getPlatformAndVersion(&platform, &minOS, &sdk) ) { + // if have LC_VERSION_MIN_MACOSX and it says SDK < 10.9, so ignore code signature + if ( (platform == Platform::macOS) && (sdk < 0x000A0900) ) + return false; + } + else { + switch ( header()->cputype ) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + // old binary with no LC_VERSION_*, assume intel binaries are old macOS binaries (ignore code signature) + return false; + } + } + + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_CODE_SIGNATURE ) { + const linkedit_data_command* sigCmd = (linkedit_data_command*)cmd; + fileOffset = sigCmd->dataoff; + size = sigCmd->datasize; + stop = true; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call + return (fileOffset != 0); +} + +bool MachOParser::getEntry(uint32_t& offset, bool& usesCRT) +{ + Diagnostics diag; + offset = 0; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_MAIN ) { + entry_point_command* mainCmd = (entry_point_command*)cmd; + usesCRT = false; + offset = (uint32_t)mainCmd->entryoff; + stop = true; + } + else if ( cmd->cmd == LC_UNIXTHREAD ) { + stop = true; + usesCRT = true; + const uint32_t* regs32 = (uint32_t*)(((char*)cmd) + 16); + const uint64_t* regs64 = (uint64_t*)(((char*)cmd) + 16); + uint64_t startAddress = 0; + switch ( header()->cputype ) { + case CPU_TYPE_I386: + startAddress = regs32[10]; // i386_thread_state_t.eip + break; + case CPU_TYPE_X86_64: + startAddress = regs64[16]; // x86_thread_state64_t.rip + break; + case CPU_TYPE_ARM: + startAddress = regs32[15]; // arm_thread_state_t.__pc + break; + case CPU_TYPE_ARM64: + startAddress = regs64[32]; // arm_thread_state64_t.__pc + break; + } + offset = (uint32_t)(startAddress - preferredLoadAddress()); + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call + // FIXME: validate offset is into executable segment + return (offset != 0); +} + +bool MachOParser::canBePlacedInDyldCache(const std::string& path) const { + std::set reasons; + return canBePlacedInDyldCache(path, reasons); +} + +bool MachOParser::canBePlacedInDyldCache(const std::string& path, std::set& reasons) const +{ + bool retval = true; + // only dylibs can go in cache + if ( fileType() != MH_DYLIB ) { + reasons.insert("Not MH_DYLIB"); + return false; // cannot continue, installName() will assert() if not a dylib + } + + // only dylibs built for /usr/lib or /System/Library can go in cache + const char* dylibName = installName(); + if ( (strncmp(dylibName, "/usr/lib/", 9) != 0) && (strncmp(dylibName, "/System/Library/", 16) != 0) ) { + retval = false; + reasons.insert("Not in '/usr/lib/' or '/System/Library/'"); + } + + // flat namespace files cannot go in cache + if ( (header()->flags & MH_TWOLEVEL) == 0 ) { + retval = false; + reasons.insert("Not built with two level namespaces"); + } + + // don't put debug variants into dyld cache + if ( endsWith(path, "_profile.dylib") || endsWith(path, "_debug.dylib") || endsWith(path, "_profile") || endsWith(path, "_debug") || endsWith(path, "/CoreADI") ) { + retval = false; + reasons.insert("Variant image"); + } + + // dylib must have extra info for moving DATA and TEXT segments apart + __block bool hasExtraInfo = false; + __block bool hasDyldInfo = false; + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_SEGMENT_SPLIT_INFO ) + hasExtraInfo = true; + if ( cmd->cmd == LC_DYLD_INFO_ONLY ) + hasDyldInfo = true; + }); + if ( !hasExtraInfo ) { + retval = false; + reasons.insert("Missing split seg info"); + } + if ( !hasDyldInfo ) { + retval = false; + reasons.insert("Old binary, missing dyld info"); + } + + // dylib can only depend on other dylibs in the shared cache + __block bool allDepPathsAreGood = true; + forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + if ( (strncmp(loadPath, "/usr/lib/", 9) != 0) && (strncmp(loadPath, "/System/Library/", 16) != 0) ) { + allDepPathsAreGood = false; + stop = true; + } + }); + if ( !allDepPathsAreGood ) { + retval = false; + reasons.insert("Depends on cache inelegible dylibs"); + } + + // dylibs with interposing info cannot be in cache + __block bool hasInterposing = false; + forEachInterposingTuple(diag, ^(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop) { + hasInterposing = true; + }); + if ( hasInterposing ) { + retval = false; + reasons.insert("Has interposing tuples"); + } + + return retval; +} + +bool MachOParser::isDynamicExecutable() const +{ + if ( fileType() != MH_EXECUTE ) + return false; + + // static executables do not have dyld load command + __block bool hasDyldLoad = false; + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_LOAD_DYLINKER ) { + hasDyldLoad = true; + stop = true; + } + }); + return hasDyldLoad; +} + + +bool MachOParser::isSlideable() const +{ + if ( header()->filetype == MH_DYLIB ) + return true; + if ( header()->filetype == MH_BUNDLE ) + return true; + if ( (header()->filetype == MH_EXECUTE) && (header()->flags & MH_PIE) ) + return true; + + return false; +} + + + +bool MachOParser::hasInitializer(Diagnostics& diag) const +{ + __block bool result = false; + forEachInitializer(diag, ^(uint32_t offset) { + result = true; + }); + return result; +} + +void MachOParser::forEachInitializer(Diagnostics& diag, void (^callback)(uint32_t offset)) const +{ + __block uint64_t textSegAddrStart = 0; + __block uint64_t textSegAddrEnd = 0; + + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( strcmp(segName, "__TEXT") == 0 ) { + textSegAddrStart = vmAddr; + textSegAddrEnd = vmAddr + vmSize; + stop = true; + } + }); + if ( textSegAddrStart == textSegAddrEnd ) { + diag.error("no __TEXT segment"); + return; + } + + // if dylib linked with -init linker option, that initializer is first + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_ROUTINES ) { + const routines_command* routines = (routines_command*)cmd; + uint64_t dashInit = routines->init_address; + if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) ) + callback((uint32_t)(dashInit - textSegAddrStart)); + else + diag.error("-init does not point within __TEXT segment"); + } + else if ( cmd->cmd == LC_ROUTINES_64 ) { + const routines_command_64* routines = (routines_command_64*)cmd; + uint64_t dashInit = routines->init_address; + if ( (textSegAddrStart < dashInit) && (dashInit < textSegAddrEnd) ) + callback((uint32_t)(dashInit - textSegAddrStart)); + else + diag.error("-init does not point within __TEXT segment"); + } + }); + + // next any function pointers in mod-init section + bool p64 = is64(); + unsigned pointerSize = p64 ? 8 : 4; + forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) { + if ( (flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS ) { + if ( (size % pointerSize) != 0 ) { + diag.error("initializer section %s/%s has bad size", segmentName, sectionName); + stop = true; + return; + } + if ( illegalSectionSize ) { + diag.error("initializer section %s/%s extends beyond the end of the segment", segmentName, sectionName); + stop = true; + return; + } + if ( ((long)content % pointerSize) != 0 ) { + diag.error("initializer section %s/%s is not pointer aligned", segmentName, sectionName); + stop = true; + return; + } + if ( p64 ) { + const uint64_t* initsStart = (uint64_t*)content; + const uint64_t* initsEnd = (uint64_t*)((uint8_t*)content + size); + for (const uint64_t* p=initsStart; p < initsEnd; ++p) { + uint64_t anInit = *p; + if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) { + diag.error("initializer 0x%0llX does not point within __TEXT segment", anInit); + stop = true; + break; + } + callback((uint32_t)(anInit - textSegAddrStart)); + } + } + else { + const uint32_t* initsStart = (uint32_t*)content; + const uint32_t* initsEnd = (uint32_t*)((uint8_t*)content + size); + for (const uint32_t* p=initsStart; p < initsEnd; ++p) { + uint32_t anInit = *p; + if ( (anInit <= textSegAddrStart) || (anInit > textSegAddrEnd) ) { + diag.error("initializer 0x%0X does not point within __TEXT segment", anInit); + stop = true; + break; + } + callback(anInit - (uint32_t)textSegAddrStart); + } + } + } + }); +} + +void MachOParser::forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const +{ + forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop) { + if ( ( (flags & SECTION_TYPE) == S_DTRACE_DOF ) && !illegalSectionSize ) { + callback((uint32_t)((uintptr_t)content - (uintptr_t)header())); + } + }); +} + + +uint32_t MachOParser::segmentCount() const +{ + __block uint32_t count = 0; + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + ++count; + }); + return count; +} + +void MachOParser::forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop)) const +{ + Diagnostics diag; + __block uint32_t segIndex = 0; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* segCmd = (segment_command_64*)cmd; + uint64_t sizeOfSections = segCmd->vmsize; + uint8_t p2align = 0; + const section_64* const sectionsStart = (section_64*)((char*)segCmd + sizeof(struct segment_command_64)); + const section_64* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const section_64* sect=sectionsStart; sect < sectionsEnd; ++sect) { + sizeOfSections = sect->addr + sect->size - segCmd->vmaddr; + if ( sect->align > p2align ) + p2align = sect->align; + } + callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop); + ++segIndex; + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* segCmd = (segment_command*)cmd; + uint64_t sizeOfSections = segCmd->vmsize; + uint8_t p2align = 0; + const section* const sectionsStart = (section*)((char*)segCmd + sizeof(struct segment_command)); + const section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + sizeOfSections = sect->addr + sect->size - segCmd->vmaddr; + if ( sect->align > p2align ) + p2align = sect->align; + } + callback(segCmd->segname, (uint32_t)segCmd->fileoff, (uint32_t)segCmd->filesize, segCmd->vmaddr, segCmd->vmsize, segCmd->initprot, segIndex, sizeOfSections, p2align, stop); + ++segIndex; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call +} + +void MachOParser::forEachExportedSymbol(Diagnostics diag, void (^handler)(const char* symbolName, uint64_t imageOffset, bool isReExport, bool& stop)) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return; + + if ( leInfo.dyldInfo != nullptr ) { + const uint8_t* trieStart = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->export_off); + const uint8_t* trieEnd = trieStart + leInfo.dyldInfo->export_size; + std::vector exports; + if ( !ExportInfoTrie::parseTrie(trieStart, trieEnd, exports) ) { + diag.error("malformed exports trie"); + return; + } + bool stop = false; + for (const ExportInfoTrie::Entry& exp : exports) { + bool isReExport = (exp.info.flags & EXPORT_SYMBOL_FLAGS_REEXPORT); + handler(exp.name.c_str(), exp.info.address, isReExport, stop); + if ( stop ) + break; + } + } +} + +bool MachOParser::invalidRebaseState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo, + bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const +{ + if ( !segIndexSet ) { + diag.error("%s missing preceding REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName); + return true; + } + if ( segmentIndex >= leInfo.layout.segmentCount ) { + diag.error("%s segment index %d too large", opcodeName, segmentIndex); + return true; + } + if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) { + diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize); + return true; + } + switch ( type ) { + case REBASE_TYPE_POINTER: + if ( !leInfo.layout.segments[segmentIndex].writable ) { + diag.error("%s pointer rebase is in non-writable segment", opcodeName); + return true; + } + if ( leInfo.layout.segments[segmentIndex].executable ) { + diag.error("%s pointer rebase is in executable segment", opcodeName); + return true; + } + break; + case REBASE_TYPE_TEXT_ABSOLUTE32: + case REBASE_TYPE_TEXT_PCREL32: + if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) { + diag.error("%s text rebase is in segment that does not support text relocations", opcodeName); + return true; + } + if ( leInfo.layout.segments[segmentIndex].writable ) { + diag.error("%s text rebase is in writable segment", opcodeName); + return true; + } + if ( !leInfo.layout.segments[segmentIndex].executable ) { + diag.error("%s pointer rebase is in non-executable segment", opcodeName); + return true; + } + break; + default: + diag.error("%s unknown rebase type %d", opcodeName, type); + return true; + } + return false; +} + +void MachOParser::forEachRebase(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return; + + if ( leInfo.dyldInfo != nullptr ) { + // work around linker bug that laid down rebase opcodes for lazy pointer section when -bind_at_load used + __block int lpSegIndex = 0; + __block uint64_t lpSegOffsetStart = 0; + __block uint64_t lpSegOffsetEnd = 0; + bool hasWeakBinds = (leInfo.dyldInfo->weak_bind_size != 0); + if ( leInfo.dyldInfo->lazy_bind_size == 0 ) { + __block uint64_t lpAddr = 0; + __block uint64_t lpSize = 0; + forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectStop) { + if ( (flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS ) { + lpAddr = addr; + lpSize = size; + sectStop = true; + } + }); + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& segStop) { + if ( (vmAddr <= lpAddr) && (vmAddr+vmSize >= lpAddr+lpSize) ) { + lpSegOffsetStart = lpAddr - vmAddr; + lpSegOffsetEnd = lpSegOffsetStart + lpSize; + segStop = true; + return; + } + ++lpSegIndex; + }); + } + // don't remove rebase if there is a weak-bind at pointer location + bool (^weakBindAt)(uint64_t segOffset) = ^(uint64_t segOffset) { + if ( !hasWeakBinds ) + return false; + __block bool result = false; + Diagnostics weakDiag; + forEachWeakDef(weakDiag, ^(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, uint64_t addend, const char* symbolName, bool& weakStop) { + if ( segOffset == dataSegOffset ) { + result = true; + weakStop = true; + } + }); + return result; + }; + + + const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->rebase_off); + const uint8_t* end = p + leInfo.dyldInfo->rebase_size; + const uint32_t pointerSize = (is64() ? 8 : 4); + uint8_t type = 0; + int segIndex = 0; + uint64_t segOffset = 0; + uint64_t count; + uint64_t skip; + bool segIndexSet = false; + bool stop = false; + while ( !stop && diag.noError() && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + stop = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(diag, p, end); + segIndexSet = true; + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(diag, p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*pointerSize; + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_IMM_TIMES", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) ) + return; + if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) ) + handler(segIndex, segOffset, type, stop); + segOffset += pointerSize; + } + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(diag, p, end); + for (uint32_t i=0; i < count; ++i) { + if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) ) + return; + if ( (segIndex != lpSegIndex) || (segOffset > lpSegOffsetEnd) || (segOffset < lpSegOffsetStart) || weakBindAt(segOffset) ) + handler(segIndex, segOffset, type, stop); + segOffset += pointerSize; + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) ) + return; + handler(segIndex, segOffset, type, stop); + segOffset += read_uleb128(diag, p, end) + pointerSize; + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(diag, p, end); + if ( diag.hasError() ) + break; + skip = read_uleb128(diag, p, end); + for (uint32_t i=0; i < count; ++i) { + if ( invalidRebaseState(diag, "REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, pointerSize, segIndex, segOffset, type) ) + return; + handler(segIndex, segOffset, type, stop); + segOffset += skip + pointerSize; + } + break; + default: + diag.error("unknown rebase opcode 0x%02X", opcode); + } + } + } + else { + // old binary + const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->locreloff); + const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nlocrel]; + bool stop = false; + const uint8_t relocSize = (is64() ? 3 : 2); + for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) { + if ( reloc->r_length != relocSize ) { + diag.error("local relocation has wrong r_length"); + break; + } + if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED + diag.error("local relocation has wrong r_type"); + break; + } + doLocalReloc(diag, reloc->r_address, stop, handler); + } + // then process indirect symbols + forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal, + const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) { + if ( !bind && !bindLazy ) + handler(segIndex, segOffset, REBASE_TYPE_POINTER, indStop); + }); + } +} + +bool MachOParser::doLocalReloc(Diagnostics& diag, uint32_t r_address, bool& stop, void (^handler)(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop)) const +{ + bool firstWritable = (header()->cputype == CPU_TYPE_X86_64); + __block uint64_t relocBaseAddress = 0; + __block bool baseFound = false; + __block uint32_t segIndex = 0; + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) { + if ( !baseFound ) { + if ( !firstWritable || (protections & VM_PROT_WRITE) ) { + baseFound = true; + relocBaseAddress = vmAddr; + } + } + if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) { + uint8_t type = REBASE_TYPE_POINTER; + uint64_t segOffset = relocBaseAddress + r_address - vmAddr; + handler(segIndex, segOffset, type, stop); + stopSeg = true; + } + ++segIndex; + }); + + return false; +} + +int MachOParser::libOrdinalFromDesc(uint16_t n_desc) const +{ + // -flat_namespace is always flat lookup + if ( (header()->flags & MH_TWOLEVEL) == 0 ) + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + // extract byte from undefined symbol entry + int libIndex = GET_LIBRARY_ORDINAL(n_desc); + switch ( libIndex ) { + case SELF_LIBRARY_ORDINAL: + return BIND_SPECIAL_DYLIB_SELF; + + case DYNAMIC_LOOKUP_ORDINAL: + return BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + + case EXECUTABLE_ORDINAL: + return BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE; + } + + return libIndex; +} + +bool MachOParser::doExternalReloc(Diagnostics& diag, uint32_t r_address, uint32_t r_symbolnum, LinkEditInfo& leInfo, bool& stop, + void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal, + uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const +{ + const bool firstWritable = (header()->cputype == CPU_TYPE_X86_64); + const bool is64Bit = is64(); + __block uint64_t relocBaseAddress = 0; + __block bool baseFound = false; + __block uint32_t segIndex = 0; + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stopSeg) { + if ( !baseFound ) { + if ( !firstWritable || (protections & VM_PROT_WRITE) ) { + baseFound = true; + relocBaseAddress = vmAddr; + } + } + if ( baseFound && (vmAddr < relocBaseAddress+r_address) && (relocBaseAddress+r_address < vmAddr+vmSize) ) { + uint8_t type = BIND_TYPE_POINTER; + uint64_t segOffset = relocBaseAddress + r_address - vmAddr; + const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff); + const struct nlist_64* symbols64 = (nlist_64*)symbolTable; + const struct nlist* symbols32 = (struct nlist*)symbolTable; + const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff); + uint32_t symCount = leInfo.symTab->nsyms; + uint32_t poolSize = leInfo.symTab->strsize; + if ( r_symbolnum < symCount ) { + uint16_t n_desc = is64Bit ? symbols64[r_symbolnum].n_desc : symbols32[r_symbolnum].n_desc; + uint32_t libOrdinal = libOrdinalFromDesc(n_desc); + uint32_t strOffset = is64Bit ? symbols64[r_symbolnum].n_un.n_strx : symbols32[r_symbolnum].n_un.n_strx; + if ( strOffset < poolSize ) { + const char* symbolName = stringPool + strOffset; + bool weakImport = (n_desc & N_WEAK_REF); + bool lazy = false; + uint64_t addend = is64Bit ? (*((uint64_t*)((char*)header()+fileOffset+segOffset))) : (*((uint32_t*)((char*)header()+fileOffset+segOffset))); + handler(segIndex, segOffset, type, libOrdinal, addend, symbolName, weakImport, lazy, stop); + stopSeg = true; + } + } + } + ++segIndex; + }); + + return false; +} + +bool MachOParser::invalidBindState(Diagnostics& diag, const char* opcodeName, const LinkEditInfo& leInfo, bool segIndexSet, bool libraryOrdinalSet, + uint32_t dylibCount, int libOrdinal, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const +{ + if ( !segIndexSet ) { + diag.error("%s missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB", opcodeName); + return true; + } + if ( segmentIndex >= leInfo.layout.segmentCount ) { + diag.error("%s segment index %d too large", opcodeName, segmentIndex); + return true; + } + if ( segmentOffset > (leInfo.layout.segments[segmentIndex].segSize-pointerSize) ) { + diag.error("%s current segment offset 0x%08llX beyond segment size (0x%08llX)", opcodeName, segmentOffset, leInfo.layout.segments[segmentIndex].segSize); + return true; + } + if ( symbolName == NULL ) { + diag.error("%s missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM", opcodeName); + return true; + } + if ( !libraryOrdinalSet ) { + diag.error("%s missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL", opcodeName); + return true; + } + if ( libOrdinal > (int)dylibCount ) { + diag.error("%s has library ordinal too large (%d) max (%d)", opcodeName, libOrdinal, dylibCount); + return true; + } + if ( libOrdinal < -2 ) { + diag.error("%s has unknown library special ordinal (%d)", opcodeName, libOrdinal); + return true; + } + switch ( type ) { + case BIND_TYPE_POINTER: + if ( !leInfo.layout.segments[segmentIndex].writable ) { + diag.error("%s pointer bind is in non-writable segment", opcodeName); + return true; + } + if ( leInfo.layout.segments[segmentIndex].executable ) { + diag.error("%s pointer bind is in executable segment", opcodeName); + return true; + } + break; + case BIND_TYPE_TEXT_ABSOLUTE32: + case BIND_TYPE_TEXT_PCREL32: + if ( !leInfo.layout.segments[segmentIndex].textRelocsAllowed ) { + diag.error("%s text bind is in segment that does not support text relocations", opcodeName); + return true; + } + if ( leInfo.layout.segments[segmentIndex].writable ) { + diag.error("%s text bind is in writable segment", opcodeName); + return true; + } + if ( !leInfo.layout.segments[segmentIndex].executable ) { + diag.error("%s pointer bind is in non-executable segment", opcodeName); + return true; + } + break; + default: + diag.error("%s unknown bind type %d", opcodeName, type); + return true; + } + return false; +} + +void MachOParser::forEachBind(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, + int libOrdinal, uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return; + const uint32_t dylibCount = dependentDylibCount(); + + if ( leInfo.dyldInfo != nullptr ) { + // process bind opcodes + const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->bind_off); + const uint8_t* end = p + leInfo.dyldInfo->bind_size; + const uint32_t pointerSize = (is64() ? 8 : 4); + uint8_t type = 0; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int libraryOrdinal = 0; + bool segIndexSet = false; + bool libraryOrdinalSet = false; + + int64_t addend = 0; + uint64_t count; + uint64_t skip; + bool weakImport = false; + bool done = false; + bool stop = false; + while ( !done && !stop && diag.noError() && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = (int)read_uleb128(diag, p, end); + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(diag, p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(diag, p, end); + segIndexSet = true; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segmentOffset += read_uleb128(diag, p, end); + break; + case BIND_OPCODE_DO_BIND: + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop); + segmentOffset += pointerSize; + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop); + segmentOffset += read_uleb128(diag, p, end) + pointerSize; + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop); + segmentOffset += immediate*pointerSize + pointerSize; + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(diag, p, end); + skip = read_uleb128(diag, p, end); + for (uint32_t i=0; i < count; ++i) { + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, false, stop); + segmentOffset += skip + pointerSize; + } + break; + default: + diag.error("bad bind opcode 0x%02X", *p); + } + } + if ( diag.hasError() || stop ) + return; + // process lazy bind opcodes + if ( leInfo.dyldInfo->lazy_bind_size != 0 ) { + p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->lazy_bind_off); + end = p + leInfo.dyldInfo->lazy_bind_size; + type = BIND_TYPE_POINTER; + segmentOffset = 0; + segmentIndex = 0; + symbolName = NULL; + libraryOrdinal = 0; + segIndexSet = false; + libraryOrdinalSet= false; + addend = 0; + weakImport = false; + stop = false; + while ( !stop && diag.noError() && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + // this opcode marks the end of each lazy pointer binding + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = (int)read_uleb128(diag, p, end); + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + weakImport = ( (immediate & BIND_SYMBOL_FLAGS_WEAK_IMPORT) != 0 ); + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(diag, p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(diag, p, end); + segIndexSet = true; + break; + case BIND_OPCODE_DO_BIND: + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, libraryOrdinalSet, dylibCount, libraryOrdinal, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(segmentIndex, segmentOffset, type, libraryOrdinal, addend, symbolName, weakImport, true, stop); + segmentOffset += pointerSize; + break; + case BIND_OPCODE_SET_TYPE_IMM: + case BIND_OPCODE_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + diag.error("bad lazy bind opcode 0x%02X", opcode); + break; + } + } + } + } + else { + // old binary, first process relocation + const relocation_info* const relocsStart = (relocation_info*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->extreloff); + const relocation_info* const relocsEnd = &relocsStart[leInfo.dynSymTab->nextrel]; + bool stop = false; + const uint8_t relocSize = (is64() ? 3 : 2); + for (const relocation_info* reloc=relocsStart; (reloc < relocsEnd) && !stop; ++reloc) { + if ( reloc->r_length != relocSize ) { + diag.error("external relocation has wrong r_length"); + break; + } + if ( reloc->r_type != 0 ) { // 0 == X86_64_RELOC_UNSIGNED == GENERIC_RELOC_VANILLA == ARM64_RELOC_UNSIGNED + diag.error("external relocation has wrong r_type"); + break; + } + doExternalReloc(diag, reloc->r_address, reloc->r_symbolnum, leInfo, stop, handler); + } + // then process indirect symbols + forEachIndirectPointer(diag, ^(uint32_t segIndex, uint64_t segOffset, bool bind, int bindLibOrdinal, + const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& indStop) { + if ( bind ) + handler(segIndex, segOffset, (selfModifyingStub ? BIND_TYPE_IMPORT_JMP_REL32 : BIND_TYPE_POINTER), bindLibOrdinal, 0, bindSymbolName, bindWeakImport, bindLazy, indStop); + }); + } +} + + +void MachOParser::forEachWeakDef(Diagnostics& diag, void (^handler)(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, + uint64_t addend, const char* symbolName, bool& stop)) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return; + + const uint32_t dylibCount = dependentDylibCount(); + if ( leInfo.dyldInfo != nullptr ) { + // process weak bind opcodes + const uint8_t* p = getLinkEditContent(leInfo.layout, leInfo.dyldInfo->weak_bind_off); + const uint8_t* end = p + leInfo.dyldInfo->weak_bind_size; + const uint32_t pointerSize = (is64() ? 8 : 4); + uint8_t type = 0; + uint64_t segmentOffset = 0; + uint8_t segmentIndex = 0; + const char* symbolName = NULL; + int64_t addend = 0; + uint64_t count; + uint64_t skip; + bool segIndexSet = false; + bool done = false; + bool stop = false; + while ( !done && !stop && diag.noError() && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + diag.error("unexpected dylib ordinal in weak binding info"); + return; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + while (*p != '\0') + ++p; + ++p; + if ( (immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) != 0 ) + handler(true, 0, 0, 0, symbolName, stop); + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(diag, p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + segmentOffset = read_uleb128(diag, p, end); + segIndexSet = true; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + segmentOffset += read_uleb128(diag, p, end); + break; + case BIND_OPCODE_DO_BIND: + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(false, segmentIndex, segmentOffset, addend, symbolName, stop); + segmentOffset += pointerSize; + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(false, segmentIndex, segmentOffset, addend, symbolName, stop); + segmentOffset += read_uleb128(diag, p, end) + pointerSize; + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(false, segmentIndex, segmentOffset, addend, symbolName, stop); + segmentOffset += immediate*pointerSize + pointerSize; + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(diag, p, end); + skip = read_uleb128(diag, p, end); + for (uint32_t i=0; i < count; ++i) { + if ( invalidBindState(diag, "BIND_OPCODE_DO_BIND", leInfo, segIndexSet, true, dylibCount, -2, pointerSize, segmentIndex, segmentOffset, type, symbolName) ) + return; + handler(false, segmentIndex, segmentOffset, addend, symbolName, stop); + segmentOffset += skip + pointerSize; + } + break; + default: + diag.error("bad weak bind opcode 0x%02X", *p); + } + } + if ( diag.hasError() || stop ) + return; + } + else { + // old binary + //assert(0 && "weak defs not supported for old binaries yet"); + } +} + + + +void MachOParser::forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, bool bind, int bindLibOrdinal, + const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const +{ + LinkEditInfo leInfo; + getLinkEditPointers(diag, leInfo); + if ( diag.hasError() ) + return; + + // find lazy and non-lazy pointer sections + const bool is64Bit = is64(); + const uint32_t* const indirectSymbolTable = (uint32_t*)getLinkEditContent(leInfo.layout, leInfo.dynSymTab->indirectsymoff); + const uint32_t indirectSymbolTableCount = leInfo.dynSymTab->nindirectsyms; + const uint32_t pointerSize = is64Bit ? 8 : 4; + const void* symbolTable = getLinkEditContent(leInfo.layout, leInfo.symTab->symoff); + const struct nlist_64* symbols64 = (nlist_64*)symbolTable; + const struct nlist* symbols32 = (struct nlist*)symbolTable; + const char* stringPool = (char*)getLinkEditContent(leInfo.layout, leInfo.symTab->stroff); + uint32_t symCount = leInfo.symTab->nsyms; + uint32_t poolSize = leInfo.symTab->strsize; + __block bool stop = false; + forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, + uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& sectionStop) { + uint8_t sectionType = (flags & SECTION_TYPE); + if ( (sectionType != S_LAZY_SYMBOL_POINTERS) && (sectionType != S_NON_LAZY_SYMBOL_POINTERS) && (sectionType != S_SYMBOL_STUBS) ) + return; + bool selfModifyingStub = (sectionType == S_SYMBOL_STUBS) && (flags & S_ATTR_SELF_MODIFYING_CODE) && (reserved2 == 5) && (header()->cputype == CPU_TYPE_I386); + if ( (flags & S_ATTR_SELF_MODIFYING_CODE) && !selfModifyingStub ) { + diag.error("S_ATTR_SELF_MODIFYING_CODE section type only valid in old i386 binaries"); + sectionStop = true; + return; + } + uint32_t elementSize = selfModifyingStub ? reserved2 : pointerSize; + uint32_t elementCount = (uint32_t)(size/elementSize); + if (greaterThanAddOrOverflow(reserved1, elementCount, indirectSymbolTableCount)) { + diag.error("section %s overflows indirect symbol table", sectionName); + sectionStop = true; + return; + } + __block uint32_t index = 0; + __block uint32_t segIndex = 0; + __block uint64_t sectionSegOffset; + forEachSegment(^(const char* segmentName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &segStop) { + if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) { + sectionSegOffset = addr - vmAddr; + segIndex = index; + segStop = true; + } + ++index; + }); + + for (int i=0; (i < elementCount) && !stop; ++i) { + uint32_t symNum = indirectSymbolTable[reserved1 + i]; + if ( symNum == INDIRECT_SYMBOL_ABS ) + continue; + uint64_t segOffset = sectionSegOffset+i*elementSize; + if ( symNum == INDIRECT_SYMBOL_LOCAL ) { + handler(segIndex, segOffset, false, 0, "", false, false, false, stop); + continue; + } + if ( symNum > symCount ) { + diag.error("indirect symbol[%d] = %d which is invalid symbol index", reserved1 + i, symNum); + sectionStop = true; + return; + } + uint16_t n_desc = is64Bit ? symbols64[symNum].n_desc : symbols32[symNum].n_desc; + uint32_t libOrdinal = libOrdinalFromDesc(n_desc); + uint32_t strOffset = is64Bit ? symbols64[symNum].n_un.n_strx : symbols32[symNum].n_un.n_strx; + if ( strOffset > poolSize ) { + diag.error("symbol[%d] string offset out of range", reserved1 + i); + sectionStop = true; + return; + } + const char* symbolName = stringPool + strOffset; + bool weakImport = (n_desc & N_WEAK_REF); + bool lazy = (sectionType == S_LAZY_SYMBOL_POINTERS); + handler(segIndex, segOffset, true, libOrdinal, symbolName, weakImport, lazy, selfModifyingStub, stop); + } + sectionStop = stop; + }); +} + +void MachOParser::forEachInterposingTuple(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop)) const +{ + const bool is64Bit = is64(); + const unsigned entrySize = is64Bit ? 16 : 8; + const unsigned pointerSize = is64Bit ? 8 : 4; + forEachSection(^(const char* segmentName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& secStop) { + if ( ((flags & SECTION_TYPE) == S_INTERPOSING) || ((strcmp(sectionName, "__interpose") == 0) && (strcmp(segmentName, "__DATA") == 0)) ) { + if ( (size % entrySize) != 0 ) { + diag.error("interposing section %s/%s has bad size", segmentName, sectionName); + secStop = true; + return; + } + if ( illegalSectionSize ) { + diag.error("interposing section %s/%s extends beyond the end of the segment", segmentName, sectionName); + secStop = true; + return; + } + if ( ((long)content % pointerSize) != 0 ) { + diag.error("interposing section %s/%s is not pointer aligned", segmentName, sectionName); + secStop = true; + return; + } + __block uint32_t sectionSegIndex = 0; + __block uint64_t sectionSegOffset = 0; + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& segStop) { + if ( (vmAddr <= addr) && (addr < vmAddr+vmSize) ) { + sectionSegIndex = segIndex; + sectionSegOffset = addr - vmAddr; + segStop = true; + } + }); + if ( sectionSegIndex == 0 ) { + diag.error("interposing section %s/%s is not in a segment", segmentName, sectionName); + secStop = true; + return; + } + uint32_t offset = 0; + bool tupleStop = false; + for (int i=0; i < (size/entrySize); ++i) { + uint64_t replacementContent = is64Bit ? (*(uint64_t*)((char*)content + offset)) : (*(uint32_t*)((char*)content + offset)); + handler(sectionSegIndex, sectionSegOffset+offset, sectionSegOffset+offset+pointerSize, replacementContent, tupleStop); + offset += entrySize; + if ( tupleStop ) + break; + } + } + }); +} + + +const void* MachOParser::content(uint64_t vmOffset) +{ + __block const void* result = nullptr; + __block uint32_t firstSegFileOffset = 0; + __block uint64_t firstSegVmAddr = 0; + if ( isRaw() ) { + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) { + if ( firstSegFileOffset == 0) { + if ( fileSize == 0 ) + return; // skip __PAGEZERO + firstSegFileOffset = fileOffset; + firstSegVmAddr = vmAddr; + } + uint64_t segVmOffset = vmAddr - firstSegVmAddr; + if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) { + result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset); + stop = true; + } + }); + } + else if ( inRawCache() ) { + forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool &stop) { + if ( firstSegFileOffset == 0 ) { + firstSegFileOffset = fileOffset; + firstSegVmAddr = vmAddr; + } + uint64_t segVmOffset = vmAddr - firstSegVmAddr; + if ( (vmOffset >= segVmOffset) && (vmOffset < segVmOffset+vmSize) ) { + result = (char*)(header()) + (fileOffset - firstSegFileOffset) + (vmOffset - segVmOffset); + stop = true; + } + }); + } + else { + // non-raw cache is easy + result = (char*)(header()) + vmOffset; + } + return result; +} + +#endif // !DYLD_IN_PROCESS + +bool MachOParser::isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) +{ + textOffset = 0; + size = 0; + Diagnostics diag; + forEachLoadCommand(diag, ^(const load_command* cmd, bool& stop) { + if ( (cmd->cmd == LC_ENCRYPTION_INFO) || (cmd->cmd == LC_ENCRYPTION_INFO_64) ) { + const encryption_info_command* encCmd = (encryption_info_command*)cmd; + if ( encCmd->cryptid == 1 ) { + // Note: cryptid is 0 in just-built apps. The iTunes App Store sets cryptid to 1 + textOffset = encCmd->cryptoff; + size = encCmd->cryptsize; + } + stop = true; + } + }); + diag.assertNoError(); // any malformations in the file should have been caught by earlier validate() call + return (textOffset != 0); +} + +bool MachOParser::cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]) +{ + const CS_CodeDirectory* cd = (const CS_CodeDirectory*)findCodeDirectoryBlob(codeSigStart, codeSignLen); + if ( cd == nullptr ) + return false; + + uint32_t cdLength = htonl(cd->length); + if ( cd->hashType == CS_HASHTYPE_SHA256 ) { + uint8_t digest[CC_SHA256_DIGEST_LENGTH]; + CC_SHA256(cd, cdLength, digest); + // cd-hash of sigs that use SHA256 is the first 20 bytes of the SHA256 of the code digest + memcpy(cdHash, digest, 20); + return true; + } + else if ( cd->hashType == CS_HASHTYPE_SHA1 ) { + // compute hash directly into return buffer + CC_SHA1(cd, cdLength, cdHash); + return true; + } + + return false; +} + +const void* MachOParser::findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen) +{ + // verify min length of overall code signature + if ( codeSignLen < sizeof(CS_SuperBlob) ) + return nullptr; + + // verify magic at start + const CS_SuperBlob* codeSuperBlob = (CS_SuperBlob*)codeSigStart; + if ( codeSuperBlob->magic != htonl(CSMAGIC_EMBEDDED_SIGNATURE) ) + return nullptr; + + // verify count of sub-blobs not too large + uint32_t subBlobCount = htonl(codeSuperBlob->count); + if ( (codeSignLen-sizeof(CS_SuperBlob))/sizeof(CS_BlobIndex) < subBlobCount ) + return nullptr; + + // walk each sub blob, looking at ones with type CSSLOT_CODEDIRECTORY + for (uint32_t i=0; i < subBlobCount; ++i) { + if ( codeSuperBlob->index[i].type != htonl(CSSLOT_CODEDIRECTORY) ) + continue; + uint32_t cdOffset = htonl(codeSuperBlob->index[i].offset); + // verify offset is not out of range + if ( cdOffset > (codeSignLen - sizeof(CS_CodeDirectory)) ) + return nullptr; + const CS_CodeDirectory* cd = (CS_CodeDirectory*)((uint8_t*)codeSuperBlob + cdOffset); + uint32_t cdLength = htonl(cd->length); + // verify code directory length not out of range + if ( cdLength > (codeSignLen - cdOffset) ) + return nullptr; + if ( cd->magic == htonl(CSMAGIC_CODEDIRECTORY) ) + return cd; + } + return nullptr; +} + + + + +} // namespace dyld3 + diff --git a/dyld/dyld3/MachOParser.h b/dyld/dyld3/MachOParser.h new file mode 100644 index 0000000..73b5ead --- /dev/null +++ b/dyld/dyld3/MachOParser.h @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2017 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 MachOParser_h +#define MachOParser_h + +#include +#include +#include + +#include +#include +#include + +#include "Diagnostics.h" + + +#define BIND_TYPE_IMPORT_JMP_REL32 4 + +namespace dyld3 { + +// Note, this should make PLATFORM_* values in +enum class Platform { + unknown = 0, + macOS = 1, + iOS = 2, + tvOS = 3, + watchOS = 4, + bridgeOS = 5 +}; + +struct VIS_HIDDEN UUID { + UUID() {} + UUID(const UUID& other) { uuid_copy(&_bytes[0], &other._bytes[0]); } + UUID(const uuid_t other_uuid) { uuid_copy(&_bytes[0], other_uuid); } + bool operator<(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) < 0; } + bool operator==(const UUID& other) const { return uuid_compare(&_bytes[0], &other._bytes[0]) == 0; } + bool operator!=(const UUID& other) const { return !(*this == other); } + + size_t hash() const + { + size_t retval = 0; + for (auto i = 0; i < 16 / sizeof(size_t); ++i) { + retval ^= ((size_t*)(&_bytes[0]))[i]; + } + return retval; + } + const unsigned char* get() const { return &_bytes[0]; }; +private: + std::array _bytes; +}; + +class VIS_HIDDEN MachOParser +{ +public: +#if !DYLD_IN_PROCESS + static bool isValidMachO(Diagnostics& diag, const std::string& archName, Platform platform, const void* fileContent, size_t fileLength, const std::string& pathOpened, bool ignoreMainExecutables); + static bool isArch(const mach_header* mh, const std::string& archName); + static std::string archName(uint32_t cputype, uint32_t cpusubtype); + static std::string platformName(Platform platform); + static std::string versionString(uint32_t packedVersion); + static uint32_t cpuTypeFromArchName(const std::string& archName); + static uint32_t cpuSubtypeFromArchName(const std::string& archName); +#else + static bool isMachO(Diagnostics& diag, const void* fileContent, size_t fileLength); + static bool wellFormedMachHeaderAndLoadCommands(const mach_header* mh); +#endif + MachOParser(const mach_header* mh, bool dyldCacheIsRaw=false); + bool valid(Diagnostics& diag); + + const mach_header* header() const; + uint32_t fileType() const; + std::string archName() const; + bool is64() const; + bool inDyldCache() const; + bool hasThreadLocalVariables() const; + Platform platform() const; + uint64_t preferredLoadAddress() const; + UUID uuid() const; + bool getUuid(uuid_t uuid) const; + bool getPlatformAndVersion(Platform* platform, uint32_t* minOS, uint32_t* sdk) const; + bool isSimulatorBinary() const; + bool getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const; + const char* installName() const; + uint32_t dependentDylibCount() const; + const char* dependentDylibLoadPath(uint32_t depIndex) const; + void forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const; + void forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, const void* content, size_t size, bool illegalSectionSize, bool& stop)) const; + void forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop)) const; + void forEachGlobalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const; + void forEachLocalSymbol(Diagnostics& diag, void (^callback)(const char* symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool& stop)) const; + void forEachRPath(void (^callback)(const char* rPath, bool& stop)) const; + void forEachSection(void (^callback)(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, + uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const; + + struct FoundSymbol { + enum class Kind { headerOffset, absolute, resolverOffset }; + Kind kind; + bool isThreadLocal; + const mach_header* foundInDylib; + void* foundExtra; + uint64_t value; + uint32_t resolverFuncOffset; + const char* foundSymbolName; + }; + + typedef bool (^DependentFinder)(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra); + + bool findExportedSymbol(Diagnostics& diag, const char* symbolName, void* extra, FoundSymbol& foundInfo, DependentFinder finder) const; + bool findClosestSymbol(uint64_t unSlidAddr, const char** symbolName, uint64_t* symbolUnslidAddr) const; + bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size); + +#if DYLD_IN_PROCESS + intptr_t getSlide() const; + bool hasExportedSymbol(const char* symbolName, DependentFinder finder, void** result) const; + bool findClosestSymbol(const void* addr, const char** symbolName, const void** symbolAddress) const; + const char* segmentName(uint32_t segIndex) const; +#else + + bool uses16KPages() const; + bool hasObjC() const; + bool hasWeakDefs() const; + bool isEncrypted() const; + bool hasPlusLoadMethod(Diagnostics& diag) const; + bool hasInitializer(Diagnostics& diag) const; + bool getCDHash(uint8_t cdHash[20]); + bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size); + bool usesLibraryValidation() const; + bool isRestricted() const; + bool getEntry(uint32_t& offset, bool& usesCRT); + bool canBePlacedInDyldCache(const std::string& path) const; + bool canBePlacedInDyldCache(const std::string& path, std::set& reasons) const; + bool isDynamicExecutable() const; + bool isSlideable() const; + void forEachInitializer(Diagnostics& diag, void (^callback)(uint32_t offset)) const; + void forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const; + uint32_t segmentCount() const; + void forEachExportedSymbol(Diagnostics diag, void (^callback)(const char* symbolName, uint64_t imageOffset, bool isReExport, bool& stop)) const; + void forEachSegment(void (^callback)(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop)) const; + void forEachRebase(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, bool& stop)) const; + void forEachBind(Diagnostics& diag, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal, + uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const; + void forEachWeakDef(Diagnostics& diag, void (^callback)(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, + uint64_t addend, const char* symbolName, bool& stop)) const; + void forEachIndirectPointer(Diagnostics& diag, void (^handler)(uint32_t dataSegIndex, uint64_t dataSegOffset, bool bind, int bindLibOrdinal, + const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const; + void forEachInterposingTuple(Diagnostics& diag, void (^handler)(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& stop)) const; + const void* content(uint64_t vmOffset); +#endif + + static const uint8_t* trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol); + static uint64_t read_uleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end); + static int64_t read_sleb128(Diagnostics& diag, const uint8_t*& p, const uint8_t* end); + static bool cdHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen, uint8_t cdHash[20]); + static Platform currentPlatform(); + +private: + struct LayoutInfo { +#if DYLD_IN_PROCESS + uintptr_t slide; + uintptr_t textUnslidVMAddr; + uintptr_t linkeditUnslidVMAddr; + uint32_t linkeditFileOffset; +#else + uint32_t segmentCount; + uint32_t linkeditSegIndex; + struct { + uint64_t mappingOffset; + uint64_t fileOffset; + uint64_t fileSize; + uint64_t segUnslidAddress; + uint64_t writable : 1, + executable : 1, + textRelocsAllowed : 1, // segment supports text relocs (i386 only) + segSize : 61; + } segments[128]; +#endif + }; + + struct LinkEditInfo + { + const dyld_info_command* dyldInfo; + const symtab_command* symTab; + const dysymtab_command* dynSymTab; + const linkedit_data_command* splitSegInfo; + const linkedit_data_command* functionStarts; + const linkedit_data_command* dataInCode; + const linkedit_data_command* codeSig; + LayoutInfo layout; + }; + + void getLinkEditPointers(Diagnostics& diag, LinkEditInfo&) const; + void getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const; + void getLayoutInfo(LayoutInfo&) const; + const uint8_t* getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const; + +#if !DYLD_IN_PROCESS + struct ArchInfo + { + const char* name; + uint32_t cputype; + uint32_t cpusubtype; + }; + static const ArchInfo _s_archInfos[]; + + const uint8_t* getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const; + bool doLocalReloc(Diagnostics& diag, uint32_t r_address, bool& stop, void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, bool& stop)) const; + uint8_t relocPointerType() const; + int libOrdinalFromDesc(uint16_t n_desc) const; + bool doExternalReloc(Diagnostics& diag, uint32_t r_address, uint32_t r_symbolnum, LinkEditInfo& leInfo, bool& stop, + void (^callback)(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal, + uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop)) const; + bool validLoadCommands(Diagnostics& diag, size_t fileLen); + bool validEmbeddedPaths(Diagnostics& diag); + bool validSegments(Diagnostics& diag, size_t fileLen); + bool validLinkeditLayout(Diagnostics& diag); + bool invalidBindState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo, bool segIndexSet, bool libraryOrdinalSet, + uint32_t dylibCount, int libOrdinal, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, const char* symbolName) const; + bool invalidRebaseState(Diagnostics& diag, const char* opcodeName, const MachOParser::LinkEditInfo& leInfo, bool segIndexSet, + uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const; +#endif + static const void* findCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen); + void forEachSection(void (^callback)(const char* segName, uint32_t segIndex, uint64_t segVMAddr, const char* sectionName, uint32_t sectFlags, + uint64_t sectAddr, uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop)) const; + + void forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const; + bool isRaw() const; + bool inRawCache() const; + + long _data; // if low bit true, then this is raw file (not loaded image) +}; + + + +class VIS_HIDDEN FatUtil +{ +public: + static bool isFatFile(const void* fileStart); + static void forEachSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop)); +#if !DYLD_IN_PROCESS + static bool isFatFileWithSlice(Diagnostics& diag, const void* fileContent, size_t fileLen, const std::string& archName, size_t& sliceOffset, size_t& sliceLen, bool& missingSlice); +#endif +}; + + +} // namespace dyld3 + +namespace std { +template <> +struct hash { + size_t operator()(const dyld3::UUID& x) const + { + return x.hash(); + } +}; +} + +#endif // MachOParser_h diff --git a/dyld/dyld3/PathOverrides.cpp b/dyld/dyld3/PathOverrides.cpp new file mode 100644 index 0000000..2516175 --- /dev/null +++ b/dyld/dyld3/PathOverrides.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PathOverrides.h" + + + +namespace dyld3 { + +#if BUILDING_LIBDYLD +PathOverrides gPathOverrides; +#endif + + +// based on ANSI-C strstr() +static const char* strrstr(const char* str, const char* sub) +{ + const size_t sublen = strlen(sub); + for(const char* p = &str[strlen(str)]; p != str; --p) { + if ( strncmp(p, sub, sublen) == 0 ) + return p; + } + return NULL; +} + + +#if DYLD_IN_PROCESS +void PathOverrides::setEnvVars(const char* envp[]) +{ + for (const char** p = envp; *p != NULL; p++) { + addEnvVar(*p); + } +} + +#else +PathOverrides::PathOverrides(const std::vector& env) +{ + for (const std::string& envVar : env) { + addEnvVar(envVar.c_str()); + } +} +#endif + +#if !BUILDING_LIBDYLD +// libdyld is never unloaded +PathOverrides::~PathOverrides() +{ + freeArray(_dylibPathOverrides); + freeArray(_frameworkPathOverrides); + freeArray(_frameworkPathFallbacks); + freeArray(_dylibPathFallbacks); +} +#endif + + +void PathOverrides::handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const +{ + if ( value == nullptr ) + return; + size_t allocSize = strlen(key) + strlen(value) + 2; + char buffer[allocSize]; + strlcpy(buffer, key, allocSize); + strlcat(buffer, "=", allocSize); + strlcat(buffer, value, allocSize); + handler(buffer); +} + +void PathOverrides::handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const +{ + if ( list == nullptr ) + return; + size_t allocSize = strlen(key) + 2; + for (const char** lp=list; *lp != nullptr; ++lp) + allocSize += strlen(*lp)+1; + char buffer[allocSize]; + strlcpy(buffer, key, allocSize); + strlcat(buffer, "=", allocSize); + bool needColon = false; + for (const char** lp=list; *lp != nullptr; ++lp) { + if ( needColon ) + strlcat(buffer, ":", allocSize); + strlcat(buffer, *lp, allocSize); + needColon = true; + } + handler(buffer); +} + +void PathOverrides::forEachEnvVar(void (^handler)(const char* envVar)) const +{ + handleListEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverrides, handler); + handleListEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverrides, handler); + handleListEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacks, handler); + handleListEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacks, handler); + handleListEnvVar("DYLD_INSERT_LIBRARIES", _insertedDylibs, handler); + handleEnvVar( "DYLD_IMAGE_SUFFIX", _imageSuffix, handler); + handleEnvVar( "DYLD_ROOT_PATH", _rootPath, handler); +} + +uint32_t PathOverrides::envVarCount() const +{ + uint32_t count = 0; + if ( _dylibPathOverrides != nullptr ) + ++count; + if ( _frameworkPathOverrides != nullptr ) + ++count; + if ( _frameworkPathFallbacks != nullptr ) + ++count; + if ( _dylibPathFallbacks != nullptr ) + ++count; + if ( _insertedDylibs != nullptr ) + ++count; + if ( _imageSuffix != nullptr ) + ++count; + if ( _rootPath != nullptr ) + ++count; + return count; +} + +void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath)) const +{ + if ( _insertedDylibs == nullptr ) + return; + for (const char** lp=_insertedDylibs; *lp != nullptr; ++lp) + handler(*lp); +} + +void PathOverrides::addEnvVar(const char* keyEqualsValue) +{ + const char* equals = strchr(keyEqualsValue, '='); + if ( equals != NULL ) { + const char* value = &equals[1]; + const size_t keyLen = equals-keyEqualsValue; + char key[keyLen+1]; + strncpy(key, keyEqualsValue, keyLen); + key[keyLen] = '\0'; + if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) { + _dylibPathOverrides = parseColonListIntoArray(value); + } + else if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) { + _frameworkPathOverrides = parseColonListIntoArray(value); + } + else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) { + _frameworkPathFallbacks = parseColonListIntoArray(value); + } + else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) { + _dylibPathFallbacks = parseColonListIntoArray(value); + } + else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) { + _insertedDylibs = parseColonListIntoArray(value); + } + else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) { + _imageSuffix = value; + } + else if ( strcmp(key, "DYLD_ROOT_PATH") == 0 ) { + _rootPath = value; + } + } +} + +void PathOverrides::forEachInColonList(const char* list, void (^handler)(const char* path)) +{ + char buffer[strlen(list)+1]; + const char* t = list; + for (const char* s=list; *s != '\0'; ++s) { + if (*s != ':') + continue; + size_t len = s - t; + memcpy(buffer, t, len); + buffer[len] = '\0'; + handler(buffer); + t = s+1; + } + handler(t); +} + +const char** PathOverrides::parseColonListIntoArray(const char* list) +{ + __block int count = 1; + forEachInColonList(list, ^(const char* path) { + ++count; + }); + const char** array = (const char**)malloc(count*sizeof(char*)); + __block const char** p = array; + forEachInColonList(list, ^(const char* path) { + *p++ = strdup(path); + }); + *p = nullptr; + return array; +} + +void PathOverrides::freeArray(const char** array) +{ + if ( array == nullptr ) + return; + + for (const char** p=array; *p != nullptr; ++p) { + free((void*)*p); + } + free(array); +} + +void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const +{ + bool stop = false; + if ( _dylibPathFallbacks != nullptr ) { + for (const char** fp=_dylibPathFallbacks; *fp != nullptr; ++fp) { + handler(*fp, stop); + if ( stop ) + return; + } + } + else { + switch ( platform ) { + case Platform::macOS: + // "$HOME/lib" + handler("/usr/local/lib", stop); // FIXME: not for restricted processes + if ( !stop ) + handler("/usr/lib", stop); + break; + case Platform::iOS: + case Platform::watchOS: + case Platform::tvOS: + case Platform::bridgeOS: + case Platform::unknown: + handler("/usr/local/lib", stop); + if ( !stop ) + handler("/usr/lib", stop); + break; + } + } +} + +void PathOverrides::forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const +{ + bool stop = false; + if ( _frameworkPathFallbacks != nullptr ) { + for (const char** fp=_frameworkPathFallbacks; *fp != nullptr; ++fp) { + handler(*fp, stop); + if ( stop ) + return; + } + } + else { + switch ( platform ) { + case Platform::macOS: + // "$HOME/Library/Frameworks" + handler("/Library/Frameworks", stop); // FIXME: not for restricted processes + // "/Network/Library/Frameworks" + if ( !stop ) + handler("/System/Library/Frameworks", stop); + break; + case Platform::iOS: + case Platform::watchOS: + case Platform::tvOS: + case Platform::bridgeOS: + case Platform::unknown: + handler("/System/Library/Frameworks", stop); + break; + } + } +} + +void PathOverrides::forEachPathVariant(const char* initialPath, +#if !DYLD_IN_PROCESS + Platform platform, +#endif + void (^handler)(const char* possiblePath, bool& stop)) const +{ +#if DYLD_IN_PROCESS + Platform platform = MachOParser::currentPlatform(); +#endif + __block bool stop = false; + + // check for overrides + const char* frameworkPartialPath = getFrameworkPartialPath(initialPath); + if ( frameworkPartialPath != nullptr ) { + const size_t frameworkPartialPathLen = strlen(frameworkPartialPath); + // look at each DYLD_FRAMEWORK_PATH directory + if ( _frameworkPathOverrides != nullptr ) { + for (const char** fp=_frameworkPathOverrides; *fp != nullptr; ++fp) { + char npath[strlen(*fp)+frameworkPartialPathLen+8]; + strcpy(npath, *fp); + strcat(npath, "/"); + strcat(npath, frameworkPartialPath); + handler(npath, stop); + if ( stop ) + return; + } + } + } + else { + const char* libraryLeafName = getLibraryLeafName(initialPath); + const size_t libraryLeafNameLen = strlen(libraryLeafName); + // look at each DYLD_LIBRARY_PATH directory + if ( _dylibPathOverrides != nullptr ) { + for (const char** lp=_dylibPathOverrides; *lp != nullptr; ++lp) { + char libpath[strlen(*lp)+libraryLeafNameLen+8]; + strcpy(libpath, *lp); + strcat(libpath, "/"); + strcat(libpath, libraryLeafName); + handler(libpath, stop); + if ( stop ) + return; + } + } + } + + // try original path + handler(initialPath, stop); + if ( stop ) + return; + + // check fallback paths + if ( frameworkPartialPath != nullptr ) { + const size_t frameworkPartialPathLen = strlen(frameworkPartialPath); + // look at each DYLD_FALLBACK_FRAMEWORK_PATH directory + forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) { + char npath[strlen(dir)+frameworkPartialPathLen+8]; + strcpy(npath, dir); + strcat(npath, "/"); + strcat(npath, frameworkPartialPath); + handler(npath, innerStop); + if ( innerStop ) + stop = innerStop; + }); + + } + else { + const char* libraryLeafName = getLibraryLeafName(initialPath); + const size_t libraryLeafNameLen = strlen(libraryLeafName); + // look at each DYLD_FALLBACK_LIBRARY_PATH directory + forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) { + char libpath[strlen(dir)+libraryLeafNameLen+8]; + strcpy(libpath, dir); + strcat(libpath, "/"); + strcat(libpath, libraryLeafName); + handler(libpath, innerStop); + if ( innerStop ) + stop = innerStop; + }); + } +} + + +// +// Find framework path +// +// /path/foo.framework/foo => foo.framework/foo +// /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo +// /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar +// /path/foo.framework/Libraries/bar.dylb => NULL +// /path/foo.framework/bar => NULL +// +// Returns nullptr if not a framework path +// +const char* PathOverrides::getFrameworkPartialPath(const char* path) const +{ + const char* dirDot = strrstr(path, ".framework/"); + if ( dirDot != nullptr ) { + const char* dirStart = dirDot; + for ( ; dirStart >= path; --dirStart) { + if ( (*dirStart == '/') || (dirStart == path) ) { + const char* frameworkStart = &dirStart[1]; + if ( dirStart == path ) + --frameworkStart; + size_t len = dirDot - frameworkStart; + char framework[len+1]; + strncpy(framework, frameworkStart, len); + framework[len] = '\0'; + const char* leaf = strrchr(path, '/'); + if ( leaf != nullptr ) { + if ( strcmp(framework, &leaf[1]) == 0 ) { + return frameworkStart; + } + if ( _imageSuffix != nullptr ) { + // some debug frameworks have install names that end in _debug + if ( strncmp(framework, &leaf[1], len) == 0 ) { + if ( strcmp( _imageSuffix, &leaf[len+1]) == 0 ) + return frameworkStart; + } + } + } + } + } + } + return nullptr; +} + + +const char* PathOverrides::getLibraryLeafName(const char* path) +{ + const char* start = strrchr(path, '/'); + if ( start != nullptr ) + return &start[1]; + else + return path; +} + +} // namespace dyld3 + + + + + diff --git a/dyld/dyld3/PathOverrides.h b/dyld/dyld3/PathOverrides.h new file mode 100644 index 0000000..4ae7407 --- /dev/null +++ b/dyld/dyld3/PathOverrides.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 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_PATH_OVERRIDES_H__ +#define __DYLD_PATH_OVERRIDES_H__ + +#include + +#if !DYLD_IN_PROCESS +#include +#include +#endif + +#include "Logging.h" +#include "MachOParser.h" + + +namespace dyld3 { + +class VIS_HIDDEN PathOverrides +{ +public: +#if !BUILDING_LIBDYLD + // libdyld is never unloaded + ~PathOverrides(); +#endif + +#if DYLD_IN_PROCESS + void setEnvVars(const char* envp[]); + void forEachPathVariant(const char* initialPath, void (^handler)(const char* possiblePath, bool& stop)) const; +#else + PathOverrides(const std::vector& env); + void forEachPathVariant(const char* initialPath, Platform platform, void (^handler)(const char* possiblePath, bool& stop)) const; +#endif + + uint32_t envVarCount() const; + void forEachEnvVar(void (^handler)(const char* envVar)) const; + void forEachInsertedDylib(void (^handler)(const char* dylibPath)) const; + +private: + void forEachInColonList(const char* list, void (^callback)(const char* path)); + const char** parseColonListIntoArray(const char* list); + void freeArray(const char** array); + void addEnvVar(const char* keyEqualsValue); + const char* getFrameworkPartialPath(const char* path) const; + static const char* getLibraryLeafName(const char* path); + void handleListEnvVar(const char* key, const char** list, void (^handler)(const char* envVar)) const; + void handleEnvVar(const char* key, const char* value, void (^handler)(const char* envVar)) const; + void forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const; + void forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const; + + const char** _dylibPathOverrides = nullptr; + const char** _frameworkPathOverrides = nullptr; + const char** _dylibPathFallbacks = nullptr; + const char** _frameworkPathFallbacks = nullptr; + const char** _insertedDylibs = nullptr; + const char* _imageSuffix = nullptr; + const char* _rootPath = nullptr; // simulator only +}; + +#if BUILDING_LIBDYLD +extern PathOverrides gPathOverrides; +#endif + + +} // namespace dyld3 + +#endif // __DYLD_PATH_OVERRIDES_H__ + + diff --git a/dyld/dyld3/SharedCacheRuntime.cpp b/dyld/dyld3/SharedCacheRuntime.cpp new file mode 100644 index 0000000..d1c821b --- /dev/null +++ b/dyld/dyld3/SharedCacheRuntime.cpp @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_cache_format.h" +#include "SharedCacheRuntime.h" +#include "LaunchCache.h" +#include "LaunchCacheFormat.h" +#include "Loading.h" + +#define ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE 1024 + +// should be in mach/shared_region.h +extern "C" int __shared_region_check_np(uint64_t* startaddress); +extern "C" int __shared_region_map_and_slide_np(int fd, uint32_t count, const shared_file_mapping_np mappings[], long slide, const dyld_cache_slide_info2* slideInfo, size_t slideInfoSize); + + +namespace dyld { + extern int my_stat(const char* path, struct stat* buf); + extern int my_open(const char* path, int flag, int other); + extern void log(const char*, ...); +} + + +namespace dyld3 { + + +struct CacheInfo +{ + int fd; + shared_file_mapping_np mappings[3]; + uint64_t slideInfoAddressUnslid; + size_t slideInfoSize; + uint64_t cachedDylibsGroupUnslid; + uint64_t sharedRegionStart; + uint64_t sharedRegionSize; + uint64_t maxSlide; +}; + + + + +#if __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 ARCH_NAME_H "x86_64h" + #define ARCH_CACHE_MAGIC_H "dyld_v1 x86_64h" +#elif __ARM_ARCH_7K__ + #define ARCH_NAME "armv7k" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7k" +#elif __ARM_ARCH_7A__ + #define ARCH_NAME "armv7" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7" +#elif __ARM_ARCH_7S__ + #define ARCH_NAME "armv7s" + #define ARCH_CACHE_MAGIC "dyld_v1 armv7s" +#elif __arm64e__ + #define ARCH_NAME "arm64e" + #define ARCH_CACHE_MAGIC "dyld_v1 arm64e" +#elif __arm64__ + #define ARCH_NAME "arm64" + #define ARCH_CACHE_MAGIC "dyld_v1 arm64" +#endif + + + +static void rebaseChain(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info2* slideInfo) +{ + const uintptr_t deltaMask = (uintptr_t)(slideInfo->delta_mask); + const uintptr_t valueMask = ~deltaMask; + const uintptr_t valueAdd = (uintptr_t)(slideInfo->value_add); + const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2; + + uint32_t pageOffset = startOffset; + uint32_t delta = 1; + while ( delta != 0 ) { + uint8_t* loc = pageContent + pageOffset; + uintptr_t rawValue = *((uintptr_t*)loc); + delta = (uint32_t)((rawValue & deltaMask) >> deltaShift); + uintptr_t value = (rawValue & valueMask); + if ( value != 0 ) { + value += valueAdd; + value += slideAmount; + } + *((uintptr_t*)loc) = value; + //dyld::log(" pageOffset=0x%03X, loc=%p, org value=0x%08llX, new value=0x%08llX, delta=0x%X\n", pageOffset, loc, (uint64_t)rawValue, (uint64_t)value, delta); + pageOffset += delta; + } +} + + +static void getCachePath(const SharedCacheOptions& options, size_t pathBufferSize, char pathBuffer[]) +{ + // set cache dir + if ( options.cacheDirOverride != nullptr ) { + strlcpy(pathBuffer, options.cacheDirOverride, pathBufferSize); + } + else { +#if __IPHONE_OS_VERSION_MIN_REQUIRED + strlcpy(pathBuffer, IPHONE_DYLD_SHARED_CACHE_DIR, sizeof(IPHONE_DYLD_SHARED_CACHE_DIR)); +#else + strlcpy(pathBuffer, MACOSX_DYLD_SHARED_CACHE_DIR, sizeof(MACOSX_DYLD_SHARED_CACHE_DIR)); +#endif + } + + // append file component of cache file + if ( pathBuffer[strlen(pathBuffer)-1] != '/' ) + strlcat(pathBuffer, "/", pathBufferSize); +#if __x86_64__ && !__IPHONE_OS_VERSION_MIN_REQUIRED + if ( options.useHaswell ) { + size_t len = strlen(pathBuffer); + struct stat haswellStatBuf; + strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME_H, pathBufferSize); + if ( dyld::my_stat(pathBuffer, &haswellStatBuf) == 0 ) + return; + // no haswell cache file, use regular x86_64 cache + pathBuffer[len] = '\0'; + } +#endif + strlcat(pathBuffer, DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, pathBufferSize); + +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // use .development cache if it exists + struct stat enableStatBuf; + struct stat devCacheStatBuf; + struct stat optCacheStatBuf; + bool enableFileExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR "enable-dylibs-to-override-cache", &enableStatBuf) == 0); + bool devCacheExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME DYLD_SHARED_CACHE_DEVELOPMENT_EXT, &devCacheStatBuf) == 0); + bool optCacheExists = (dyld::my_stat(IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME ARCH_NAME, &optCacheStatBuf) == 0); + if ( (enableFileExists && (enableStatBuf.st_size < ENABLE_DYLIBS_TO_OVERRIDE_CACHE_SIZE) && devCacheExists) || !optCacheExists ) + strlcat(pathBuffer, DYLD_SHARED_CACHE_DEVELOPMENT_EXT, pathBufferSize); +#endif + +} + + +int openSharedCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results) +{ + getCachePath(options, sizeof(results->path), results->path); + return dyld::my_open(results->path, O_RDONLY, 0); +} + +static bool validMagic(const SharedCacheOptions& options, const DyldSharedCache* cache) +{ + if ( strcmp(cache->header.magic, ARCH_CACHE_MAGIC) == 0 ) + return true; + +#if __x86_64__ + if ( options.useHaswell ) { + if ( strcmp(cache->header.magic, ARCH_CACHE_MAGIC_H) == 0 ) + return true; + } +#endif + return false; +} + + +static bool validPlatform(const SharedCacheOptions& options, const DyldSharedCache* cache) +{ + // grandfather in old cache that does not have platform in header + if ( cache->header.mappingOffset < 0xE0 ) + return true; + + if ( cache->header.platform != (uint32_t)MachOParser::currentPlatform() ) + return false; + +#if TARGET_IPHONE_SIMULATOR + if ( cache->header.simulator == 0 ) + return false; +#else + if ( cache->header.simulator != 0 ) + return false; +#endif + + return true; +} + + +static void verboseSharedCacheMappings(const shared_file_mapping_np mappings[3]) +{ + for (int i=0; i < 3; ++i) { + dyld::log(" 0x%08llX->0x%08llX init=%x, max=%x %s%s%s\n", + mappings[i].sfm_address, mappings[i].sfm_address+mappings[i].sfm_size-1, + mappings[i].sfm_init_prot, mappings[i].sfm_init_prot, + ((mappings[i].sfm_init_prot & VM_PROT_READ) ? "read " : ""), + ((mappings[i].sfm_init_prot & VM_PROT_WRITE) ? "write " : ""), + ((mappings[i].sfm_init_prot & VM_PROT_EXECUTE) ? "execute " : "")); + } +} + +static bool preflightCacheFile(const SharedCacheOptions& options, SharedCacheLoadInfo* results, CacheInfo* info) +{ + // find and open shared cache file + int fd = openSharedCacheFile(options, results); + if ( fd == -1 ) { + results->errorMessage = "shared cache file cannot be opened"; + return false; + } + struct stat cacheStatBuf; + if ( dyld::my_stat(results->path, &cacheStatBuf) != 0 ) { + results->errorMessage = "shared cache file cannot be stat()ed"; + ::close(fd); + return false; + } + size_t cacheFileLength = (size_t)(cacheStatBuf.st_size); + + // sanity check header and mappings + uint8_t firstPage[0x4000]; + if ( ::pread(fd, firstPage, sizeof(firstPage), 0) != sizeof(firstPage) ) { + results->errorMessage = "shared cache header could not be read"; + ::close(fd); + return false; + } + const DyldSharedCache* cache = (DyldSharedCache*)firstPage; + if ( !validMagic(options, cache) ) { + results->errorMessage = "shared cache file has wrong magic"; + ::close(fd); + return false; + } + if ( !validPlatform(options, cache) ) { + results->errorMessage = "shared cache file is for a different platform"; + ::close(fd); + return false; + } + const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)&firstPage[cache->header.mappingOffset]; + if ( (cache->header.mappingCount != 3) + || (cache->header.mappingOffset > 0x120) + || (fileMappings[0].fileOffset != 0) + || ((fileMappings[0].address + fileMappings[0].size) > fileMappings[1].address) + || ((fileMappings[1].address + fileMappings[1].size) > fileMappings[2].address) + || ((fileMappings[0].fileOffset + fileMappings[0].size) != fileMappings[1].fileOffset) + || ((fileMappings[1].fileOffset + fileMappings[1].size) != fileMappings[2].fileOffset) + || ((cache->header.codeSignatureOffset + cache->header.codeSignatureSize) != cacheFileLength) + || (fileMappings[0].maxProt != (VM_PROT_READ|VM_PROT_EXECUTE)) + || (fileMappings[1].maxProt != (VM_PROT_READ|VM_PROT_WRITE)) + || (fileMappings[2].maxProt != VM_PROT_READ) ) { + results->errorMessage = "shared cache file mappings are invalid"; + ::close(fd); + return false; + } + + if ( cache->header.mappingOffset >= 0xF8 ) { + if ( (fileMappings[0].address != cache->header.sharedRegionStart) || ((fileMappings[2].address + fileMappings[2].size) > (cache->header.sharedRegionStart+cache->header.sharedRegionSize)) ) { + results->errorMessage = "shared cache file mapping addressses invalid"; + ::close(fd); + return false; + } + } + else { + if ( (fileMappings[0].address != SHARED_REGION_BASE) || ((fileMappings[2].address + fileMappings[2].size) > (SHARED_REGION_BASE+SHARED_REGION_SIZE)) ) { + results->errorMessage = "shared cache file mapping addressses invalid"; + ::close(fd); + return false; + } + } + + // register code signature of cache file + fsignatures_t siginfo; + siginfo.fs_file_start = 0; // cache always starts at beginning of file + siginfo.fs_blob_start = (void*)cache->header.codeSignatureOffset; + siginfo.fs_blob_size = (size_t)(cache->header.codeSignatureSize); + int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); + if ( result == -1 ) { + results->errorMessage = "code signature registration for shared cache failed"; + ::close(fd); + return false; + } + + // validate code signature covers entire shared cache + uint64_t codeSignedLength = siginfo.fs_file_start; + if ( codeSignedLength < cache->header.codeSignatureOffset ) { + results->errorMessage = "code signature does not cover entire shared cache file"; + ::close(fd); + return false; + } + void* mappedData = ::mmap(NULL, sizeof(firstPage), PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); + if ( mappedData == MAP_FAILED ) { + results->errorMessage = "first page of shared cache not mmap()able"; + ::close(fd); + return false; + } + if ( memcmp(mappedData, firstPage, sizeof(firstPage)) != 0 ) { + results->errorMessage = "first page of shared cache not mmap()able"; + ::close(fd); + return false; + } + ::munmap(mappedData, sizeof(firstPage)); + + // fill out results + info->fd = fd; + for (int i=0; i < 3; ++i) { + info->mappings[i].sfm_address = fileMappings[i].address; + info->mappings[i].sfm_size = fileMappings[i].size; + info->mappings[i].sfm_file_offset = fileMappings[i].fileOffset; + info->mappings[i].sfm_max_prot = fileMappings[i].maxProt; + info->mappings[i].sfm_init_prot = fileMappings[i].initProt; + } + info->mappings[1].sfm_max_prot |= VM_PROT_SLIDE; + info->mappings[1].sfm_init_prot |= VM_PROT_SLIDE; + info->slideInfoAddressUnslid = fileMappings[2].address + cache->header.slideInfoOffset - fileMappings[2].fileOffset; + info->slideInfoSize = (long)cache->header.slideInfoSize; + if ( cache->header.mappingOffset > 0xD0 ) + info->cachedDylibsGroupUnslid = cache->header.dylibsImageGroupAddr; + else + info->cachedDylibsGroupUnslid = 0; + if ( cache->header.mappingOffset >= 0xf8 ) { + info->sharedRegionStart = cache->header.sharedRegionStart; + info->sharedRegionSize = cache->header.sharedRegionSize; + info->maxSlide = cache->header.maxSlide; + } + else { + info->sharedRegionStart = SHARED_REGION_BASE; + info->sharedRegionSize = SHARED_REGION_SIZE; + info->maxSlide = SHARED_REGION_SIZE - (fileMappings[2].address + fileMappings[2].size - fileMappings[0].address); + } + return true; +} + +#if !TARGET_IPHONE_SIMULATOR +static bool reuseExistingCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results) +{ + uint64_t cacheBaseAddress; +#if __i386__ + if ( syscall(294, &cacheBaseAddress) == 0 ) { +#else + if ( __shared_region_check_np(&cacheBaseAddress) == 0 ) { +#endif + const DyldSharedCache* existingCache = (DyldSharedCache*)cacheBaseAddress; + if ( validMagic(options, existingCache) ) { + const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)(cacheBaseAddress + existingCache->header.mappingOffset); + results->loadAddress = existingCache; + results->slide = (long)(cacheBaseAddress - fileMappings[0].address); + if ( (existingCache->header.mappingOffset > 0xD0) && (existingCache->header.dylibsImageGroupAddr != 0) ) + results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(existingCache->header.dylibsImageGroupAddr + results->slide); + else + results->cachedDylibsGroup = nullptr; + // we don't know the path this cache was previously loaded from, assume default + getCachePath(options, sizeof(results->path), results->path); + if ( options.verbose ) { + const shared_file_mapping_np* const mappings = (shared_file_mapping_np*)(cacheBaseAddress + existingCache->header.mappingOffset); + dyld::log("re-using existing shared cache (%s):\n", results->path); + shared_file_mapping_np slidMappings[3]; + for (int i=0; i < 3; ++i) { + slidMappings[i] = mappings[i]; + slidMappings[i].sfm_address += results->slide; + } + verboseSharedCacheMappings(slidMappings); + } + } + else { + results->errorMessage = "existing shared cache in memory is not compatible"; + } + return true; + } + return false; +} + +static long pickCacheASLR(CacheInfo& info) +{ + // choose new random slide +#if __IPHONE_OS_VERSION_MIN_REQUIRED + // change shared cache slide for 32-bit arm to always be 16k aligned + long slide = ((arc4random() % info.maxSlide) & (-16384)); +#else + long slide = ((arc4random() % info.maxSlide) & (-4096)); +#endif + + // respect -disable_aslr boot-arg + if ( dyld3::loader::bootArgsContains("-disable_aslr") ) + slide = 0; + + // update mappings + for (uint32_t i=0; i < 3; ++i) { + info.mappings[i].sfm_address += slide; + } + + return slide; +} + +static bool mapCacheSystemWide(const SharedCacheOptions& options, SharedCacheLoadInfo* results) +{ + CacheInfo info; + if ( !preflightCacheFile(options, results, &info) ) + return false; + + const dyld_cache_slide_info2* slideInfo = nullptr; + if ( info.slideInfoSize != 0 ) { + results->slide = pickCacheASLR(info); + slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide); + } + if ( info.cachedDylibsGroupUnslid != 0 ) + results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(info.cachedDylibsGroupUnslid + results->slide); + else + results->cachedDylibsGroup = nullptr; + + int result = __shared_region_map_and_slide_np(info.fd, 3, info.mappings, results->slide, slideInfo, info.slideInfoSize); + ::close(info.fd); + if ( result == 0 ) { + results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address); + } + else { + // could be another process beat us to it + if ( reuseExistingCache(options, results) ) + return true; + // if cache does not exist, then really is an error + results->errorMessage = "syscall to map cache into shared region failed"; + return false; + } + + if ( options.verbose ) { + dyld::log("mapped dyld cache file system wide: %s\n", results->path); + verboseSharedCacheMappings(info.mappings); + } + return true; +} +#endif + +static bool mapCachePrivate(const SharedCacheOptions& options, SharedCacheLoadInfo* results) +{ + // open and validate cache file + CacheInfo info; + if ( !preflightCacheFile(options, results, &info) ) + return false; + + // compute ALSR slide + results->slide = 0; + const dyld_cache_slide_info2* slideInfo = nullptr; +#if !TARGET_IPHONE_SIMULATOR // simulator caches do not support sliding + if ( info.slideInfoSize != 0 ) { + results->slide = pickCacheASLR(info); + slideInfo = (dyld_cache_slide_info2*)(info.slideInfoAddressUnslid + results->slide); + } +#endif + results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address); + if ( info.cachedDylibsGroupUnslid != 0 ) + results->cachedDylibsGroup = (const launch_cache::binary_format::ImageGroup*)(info.cachedDylibsGroupUnslid + results->slide); + else + results->cachedDylibsGroup = nullptr; + + // remove the shared region sub-map + vm_deallocate(mach_task_self(), (vm_address_t)info.sharedRegionStart, (vm_size_t)info.sharedRegionSize); + + // map cache just for this process with mmap() + for (int i=0; i < 3; ++i) { + void* mmapAddress = (void*)(uintptr_t)(info.mappings[i].sfm_address); + size_t size = (size_t)(info.mappings[i].sfm_size); + //dyld::log("dyld: mapping address %p with size 0x%08lX\n", mmapAddress, size); + int protection = 0; + if ( info.mappings[i].sfm_init_prot & VM_PROT_EXECUTE ) + protection |= PROT_EXEC; + if ( info.mappings[i].sfm_init_prot & VM_PROT_READ ) + protection |= PROT_READ; + if ( info.mappings[i].sfm_init_prot & VM_PROT_WRITE ) + protection |= PROT_WRITE; + off_t offset = info.mappings[i].sfm_file_offset; + if ( ::mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, info.fd, offset) != mmapAddress ) { + // failed to map some chunk of this shared cache file + // clear shared region + vm_deallocate(mach_task_self(), (vm_address_t)info.sharedRegionStart, (vm_size_t)info.sharedRegionSize); + // return failure + results->loadAddress = nullptr; + results->cachedDylibsGroup = nullptr; + results->errorMessage = "could not mmap() part of dyld cache"; + return false; + } + } + + // update all __DATA pages with slide info + const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo; + if ( slideInfoHeader != nullptr ) { + if ( slideInfoHeader->version != 2 ) { + results->errorMessage = "invalide slide info in cache file"; + return false; + } + const dyld_cache_slide_info2* slideHeader = (dyld_cache_slide_info2*)slideInfo; + const uint32_t page_size = slideHeader->page_size; + const uint16_t* page_starts = (uint16_t*)((long)(slideInfo) + slideHeader->page_starts_offset); + const uint16_t* page_extras = (uint16_t*)((long)(slideInfo) + slideHeader->page_extras_offset); + const uintptr_t dataPagesStart = (uintptr_t)info.mappings[1].sfm_address; + for (int i=0; i < slideHeader->page_starts_count; ++i) { + uint8_t* page = (uint8_t*)(long)(dataPagesStart + (page_size*i)); + uint16_t pageEntry = page_starts[i]; + //dyld::log("page[%d]: page_starts[i]=0x%04X\n", i, pageEntry); + if ( pageEntry == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) + continue; + if ( pageEntry & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + uint16_t chainIndex = (pageEntry & 0x3FFF); + bool done = false; + while ( !done ) { + uint16_t pInfo = page_extras[chainIndex]; + uint16_t pageStartOffset = (pInfo & 0x3FFF)*4; + //dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset); + rebaseChain(page, pageStartOffset, results->slide, slideInfo); + done = (pInfo & DYLD_CACHE_SLIDE_PAGE_ATTR_END); + ++chainIndex; + } + } + else { + uint32_t pageOffset = pageEntry * 4; + //dyld::log(" start pageOffset=0x%03X\n", pageOffset); + rebaseChain(page, pageOffset, results->slide, slideInfo); + } + } + } + + if ( options.verbose ) { + dyld::log("mapped dyld cache file private to process (%s):\n", results->path); + verboseSharedCacheMappings(info.mappings); + } + return true; +} + + + +bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results) +{ + results->loadAddress = 0; + results->slide = 0; + results->cachedDylibsGroup = nullptr; + results->errorMessage = nullptr; + +#if TARGET_IPHONE_SIMULATOR + // simulator only supports mmap()ing cache privately into process + return mapCachePrivate(options, results); +#else + if ( options.forcePrivate ) { + // mmap cache into this process only + return mapCachePrivate(options, results); + } + else { + // fast path: when cache is already mapped into shared region + if ( reuseExistingCache(options, results) ) + return (results->errorMessage != nullptr); + + // slow path: this is first process to load cache + return mapCacheSystemWide(options, results); + } +#endif +} + + +bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results) +{ + if ( loadInfo.loadAddress == nullptr ) + return false; + + // HACK: temp support for old caches + if ( (loadInfo.cachedDylibsGroup == nullptr) || (loadInfo.loadAddress->header.formatVersion != launch_cache::binary_format::kFormatVersion) ) { + const dyld_cache_image_info* const start = (dyld_cache_image_info*)((uint8_t*)loadInfo.loadAddress + loadInfo.loadAddress->header.imagesOffset); + const dyld_cache_image_info* const end = &start[loadInfo.loadAddress->header.imagesCount]; + for (const dyld_cache_image_info* p = start; p != end; ++p) { + const char* aPath = (char*)loadInfo.loadAddress + p->pathFileOffset; + if ( strcmp(aPath, dylibPathToFind) == 0 ) { + results->mhInCache = (const mach_header*)(p->address+loadInfo.slide); + results->pathInCache = aPath; + results->slideInCache = loadInfo.slide; + results->imageData = nullptr; + return true; + } + } + return false; + } + // HACK: end + + launch_cache::ImageGroup dylibsGroup(loadInfo.cachedDylibsGroup); + uint32_t foundIndex; + const launch_cache::binary_format::Image* imageData = dylibsGroup.findImageByPath(dylibPathToFind, foundIndex); +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // handle symlink to cached dylib + if ( imageData == nullptr ) { + char resolvedPath[PATH_MAX]; + if ( realpath(dylibPathToFind, resolvedPath) != nullptr ) + imageData = dylibsGroup.findImageByPath(resolvedPath, foundIndex); + } +#endif + if ( imageData == nullptr ) + return false; + + launch_cache::Image image(imageData); + results->mhInCache = (const mach_header*)((uintptr_t)loadInfo.loadAddress + image.cacheOffset()); + results->pathInCache = image.path(); + results->slideInCache = loadInfo.slide; + results->imageData = imageData; + return true; +} + + +bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind) +{ + if ( (loadInfo.loadAddress == nullptr) || (loadInfo.cachedDylibsGroup == nullptr) || (loadInfo.loadAddress->header.formatVersion != launch_cache::binary_format::kFormatVersion) ) + return false; + + launch_cache::ImageGroup dylibsGroup(loadInfo.cachedDylibsGroup); + uint32_t foundIndex; + const launch_cache::binary_format::Image* imageData = dylibsGroup.findImageByPath(dylibPathToFind, foundIndex); + return (imageData != nullptr); +} + + +} // namespace dyld3 + diff --git a/dyld/dyld3/SharedCacheRuntime.h b/dyld/dyld3/SharedCacheRuntime.h new file mode 100644 index 0000000..dbd4595 --- /dev/null +++ b/dyld/dyld3/SharedCacheRuntime.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2017 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_RUNTIME_H__ +#define __DYLD_SHARED_CACHE_RUNTIME_H__ + +#include +#include + +#include "DyldSharedCache.h" + +namespace dyld3 { + +struct SharedCacheOptions { + const char* cacheDirOverride; + bool forcePrivate; + bool useHaswell; + bool verbose; +}; + +struct SharedCacheLoadInfo { + const DyldSharedCache* loadAddress; + long slide; + const launch_cache::binary_format::ImageGroup* cachedDylibsGroup; + const char* errorMessage; + char path[256]; +}; + +bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results); + +struct SharedCacheFindDylibResults { + const mach_header* mhInCache; + const char* pathInCache; + long slideInCache; + const launch_cache::binary_format::Image* imageData; +}; + + +bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results); + +bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind); + + +} // namespace dyld3 + +#endif // __DYLD_SHARED_CACHE_RUNTIME_H__ + + diff --git a/dyld/dyld3/Tracing.cpp b/dyld/dyld3/Tracing.cpp new file mode 100644 index 0000000..ff15311 --- /dev/null +++ b/dyld/dyld3/Tracing.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include + +#include +#include + +#include "Tracing.h" + +namespace { +VIS_HIDDEN +static uint64_t elapsed(const time_value_t start, const time_value_t end) { + uint64_t duration; + duration = 1000000*(end.seconds - start.seconds); + duration += (end.microseconds - start.microseconds); + return duration; +} +} + +namespace dyld3 { + +VIS_HIDDEN +void kdebug_trace_dyld_image(const uint32_t code, + const uuid_t* uuid_bytes, + const fsobj_id_t fsobjid, + const fsid_t fsid, + const mach_header* load_addr) +{ +#if __LP64__ + uint64_t *uuid = (uint64_t *)uuid_bytes[0]; + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code), uuid[0], + uuid[1], (uint64_t)load_addr, + (uint64_t)fsid.val[0] | ((uint64_t)fsid.val[1] << 32)); + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 1), + (uint64_t)fsobjid.fid_objno | + ((uint64_t)fsobjid.fid_generation << 32), + 0, 0, 0); +#else /* __LP64__ */ + uint32_t *uuid = (uint32_t *)uuid_bytes[0]; + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 2), uuid[0], + uuid[1], uuid[2], uuid[3]); + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 3), + (uint32_t)load_addr, fsid.val[0], fsid.val[1], + fsobjid.fid_objno); + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, code + 4), + fsobjid.fid_generation, 0, 0, 0); +#endif /* __LP64__ */ +} + +VIS_HIDDEN +void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2) { + if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_SIGNPOST, code))) { + task_thread_times_info info; + mach_msg_type_number_t infoSize = sizeof(task_thread_times_info); + (void)task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&info, &infoSize); + uint64_t user_duration = elapsed({0,0}, info.user_time); + uint64_t system_duration = elapsed({0,0}, info.system_time); + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_SIGNPOST, code), user_duration, system_duration, data1, data2); + } +} + +static std::atomic trace_pair_id(0); + +VIS_HIDDEN +void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)()) { + //FIXME: We should assert here, but it is verified on our current platforms + //Re-enabled when we move to C++17 and can use constexpr is_lock_always_free() + //assert(std::atomic{}.is_lock_free()); + if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code))) { + uint64_t current_trace_id = trace_pair_id++; + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code) | DBG_FUNC_START, current_trace_id, 0, data1, data2); + block(); + kdebug_trace(KDBG_CODE(DBG_DYLD, DBG_DYLD_TIMING, code) | DBG_FUNC_END, current_trace_id, 0, data1, data2); + } else { + block(); + } +} + +void kdebug_trace_print(const uint32_t code, const char *string) { + if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_PRINT, code))) { + kdebug_trace_string(KDBG_CODE(DBG_DYLD, DBG_DYLD_PRINT, code), 0, string); + } +} +}; diff --git a/dyld/dyld3/Tracing.h b/dyld/dyld3/Tracing.h new file mode 100644 index 0000000..b127aa9 --- /dev/null +++ b/dyld/dyld3/Tracing.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 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 Tracing_h +#define Tracing_h + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DBG_DYLD_SIGNPOST + #define DBG_DYLD_SIGNPOST (6) +#endif + +#ifndef DBG_DYLD_TIMING + #define DBG_DYLD_TIMING (7) +#endif + +#ifndef DBG_DYLD_PRINT + #define DBG_DYLD_PRINT (8) +#endif + +#ifndef DBG_DYLD_SIGNPOST_START_DYLD + #define DBG_DYLD_SIGNPOST_START_DYLD (0) +#endif + +#ifndef DBG_DYLD_SIGNPOST_START_MAIN + #define DBG_DYLD_SIGNPOST_START_MAIN (1) +#endif + +#ifndef DBG_DYLD_SIGNPOST_START_MAIN_DYLD2 + #define DBG_DYLD_SIGNPOST_START_MAIN_DYLD2 (2) +#endif + +#ifndef DBG_DYLD_TIMING_STATIC_INITIALIZER + #define DBG_DYLD_TIMING_STATIC_INITIALIZER (0) +#endif + +#ifndef DBG_DYLD_PRINT_GENERIC + #define DBG_DYLD_PRINT_GENERIC (0) +#endif + + +#define VIS_HIDDEN __attribute__((visibility("hidden"))) + +namespace dyld3 { + +VIS_HIDDEN +void kdebug_trace_dyld_image(const uint32_t code, + const uuid_t* uuid_bytes, + const fsobj_id_t fsobjid, + const fsid_t fsid, + const mach_header* load_addr); + +VIS_HIDDEN +void kdebug_trace_dyld_signpost(const uint32_t code, uint64_t data1, uint64_t data2); + +VIS_HIDDEN +void kdebug_trace_dyld_duration(const uint32_t code, uint64_t data1, uint64_t data2, void (^block)()); + +VIS_HIDDEN +void kdebug_trace_print(const uint32_t code, const char *string); +} + +#endif /* Tracing_h */ diff --git a/dyld/dyld3/closured/closured.cpp b/dyld/dyld3/closured/closured.cpp new file mode 100644 index 0000000..1e239e8 --- /dev/null +++ b/dyld/dyld3/closured/closured.cpp @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for bootstrap_check_in() +#include +#include +#include + +#include +#include +#include + +#include "dyld_priv.h" +#include "ImageProxy.h" +#include "DyldSharedCache.h" +#include "FileUtils.h" + +extern "C" { + #include "closuredProtocolServer.h" +} + + +static os_log_t sLog = os_log_create("com.apple.dyld.closured", "closured"); + +static char sCrashMessageBuffer[1024]; + + +kern_return_t +do_CreateClosure( + mach_port_t port, + task_t requestor, + vm_address_t buffer, + mach_msg_type_number_t bufferCnt, + vm_address_t* returnData, + mach_msg_type_number_t* returnDataCnt) +{ + dyld3::ClosureBuffer clsBuff((void*)buffer, bufferCnt); + const char* imagePath = clsBuff.targetPath(); + os_log(sLog, "request to build closure for %s\n", imagePath); + + // set crash log message in case there is an assert during processing + strlcpy(sCrashMessageBuffer, "building closure for: ", sizeof(sCrashMessageBuffer)); + strlcat(sCrashMessageBuffer, imagePath, sizeof(sCrashMessageBuffer)); + CRSetCrashLogMessage(sCrashMessageBuffer); + + Diagnostics diag; + const dyld3::launch_cache::binary_format::Closure* cls = dyld3::ImageProxyGroup::makeClosure(diag, clsBuff, requestor); + + os_log_info(sLog, "finished closure build, closure=%p\n", cls); + for (const std::string& message: diag.warnings()) + os_log(sLog, "Image generated warning: %s\n", message.c_str()); + + if ( diag.noError() ) { + // on success return the closure binary in the "returnData" buffer + dyld3::ClosureBuffer result(cls); + *returnData = result.vmBuffer(); + *returnDataCnt = result.vmBufferSize(); + } + else { + // on failure return the error message in the "returnData" buffer + os_log_error(sLog, "failed to create ImageGroup: %s\n", diag.errorMessage().c_str()); + dyld3::ClosureBuffer err(diag.errorMessage().c_str()); + *returnData = err.vmBuffer(); + *returnDataCnt = err.vmBufferSize(); + } + + CRSetCrashLogMessage(nullptr); + return KERN_SUCCESS; +} + +kern_return_t +do_CreateImageGroup( + mach_port_t port, + task_t requestor, + vm_address_t buffer, + mach_msg_type_number_t bufferCnt, + vm_address_t* returnData, + mach_msg_type_number_t* returnDataCnt) +{ + dyld3::ClosureBuffer clsBuff((void*)buffer, bufferCnt); + const char* imagePath = clsBuff.targetPath(); + int requestorPid; + char requestorName[MAXPATHLEN]; + if ( pid_for_task(requestor, &requestorPid) == 0 ) { + int nameLen = proc_name(requestorPid, requestorName, sizeof(requestorName)); + if ( nameLen <= 0 ) + strcpy(requestorName, "???"); + os_log(sLog, "request from %d (%s) to build dlopen ImageGroup for %s\n", requestorPid, requestorName, imagePath); + } + + // set crash log message in case there is an assert during processing + strlcpy(sCrashMessageBuffer, "building ImageGroup for dlopen(", sizeof(sCrashMessageBuffer)); + strlcat(sCrashMessageBuffer, imagePath, sizeof(sCrashMessageBuffer)); + strlcat(sCrashMessageBuffer, ") requested by ", sizeof(sCrashMessageBuffer)); + strlcat(sCrashMessageBuffer, requestorName, sizeof(sCrashMessageBuffer)); + CRSetCrashLogMessage(sCrashMessageBuffer); + + uuid_string_t uuidStr; + dyld3::ClosureBuffer::CacheIdent cacheIdent = clsBuff.cacheIndent(); + uuid_unparse(cacheIdent.cacheUUID, uuidStr); + os_log_info(sLog, "findDyldCache(): cache addr=0x%llX, size=0x%0llX, uuid = %s\n", cacheIdent.cacheAddress, cacheIdent.cacheMappedSize, uuidStr); + + Diagnostics diag; + const dyld3::launch_cache::binary_format::ImageGroup* imageGroup = dyld3::ImageProxyGroup::makeDlopenGroup(diag, clsBuff, requestor, {""}); + + os_log(sLog, "finished ImageGroup build, imageGroup=%p\n", imageGroup); + for (const std::string& message: diag.warnings()) { + os_log(sLog, "Image generated warning: %s\n", message.c_str()); + } + + // delete incoming out-of-line data + vm_deallocate(mach_task_self(), buffer, bufferCnt); + + if ( diag.noError() ) { + // on success return the ImageGroup binary in the "returnData" buffer + dyld3::ClosureBuffer result(imageGroup); + os_log_info(sLog, "returning closure buffer: 0x%lX, size=0x%X\n", (long)result.vmBuffer(), result.vmBufferSize()); + *returnData = result.vmBuffer(); + *returnDataCnt = result.vmBufferSize(); + free((void*)imageGroup); + } + else { + // on failure return the error message in the "returnData" buffer + os_log_error(sLog, "failed to create ImageGroup: %s\n", diag.errorMessage().c_str()); + dyld3::ClosureBuffer err(diag.errorMessage().c_str()); + *returnData = err.vmBuffer(); + *returnDataCnt = err.vmBufferSize(); + } + + CRSetCrashLogMessage(nullptr); + return KERN_SUCCESS; +} + + + + +// /usr/libexec/closured -create_closure /Applications/TextEdit.app -pipefd 4 -env DYLD_FOO=1 -cache_uuid C153F90A-69F2-323E-AC9F-2BBAE48ABAF7 +int runAsTool(int argc, const char* argv[]) +{ + const char* progPath = nullptr; + int pipeNum = -1; + bool verbose = false; + std::vector dyldEnvVars; + + dyld3::ClosureBuffer::CacheIdent cacheIdent; + bzero(&cacheIdent, sizeof(cacheIdent)); + + // set crash log message in case there is an assert during processing + sCrashMessageBuffer[0] = '\0'; + for (int i=0; i < argc; ++i) { + strlcat(sCrashMessageBuffer, argv[i], sizeof(sCrashMessageBuffer)); + strlcat(sCrashMessageBuffer, " ", sizeof(sCrashMessageBuffer)); + } + CRSetCrashLogMessage(sCrashMessageBuffer); + + for (int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( strcmp(arg, "-create_closure") == 0 ) { + progPath = argv[++i]; + if ( progPath == nullptr ) { + fprintf(stderr, "-create_closure option requires a path to follow\n"); + return 1; + } + } + else if ( strcmp(arg, "-cache_uuid") == 0 ) { + const char* uuidStr = argv[++i]; + if ( uuidStr == nullptr ) { + fprintf(stderr, "-cache_uuid option requires a path to follow\n"); + return 1; + } + uuid_parse(uuidStr, cacheIdent.cacheUUID); + } + else if ( strcmp(arg, "-cache_address") == 0 ) { + const char* cacheAddr = argv[++i]; + if ( cacheAddr == nullptr ) { + fprintf(stderr, "-cache_address option requires a path to follow\n"); + return 1; + } + char *end; + cacheIdent.cacheAddress = strtol(cacheAddr, &end, 0); + } + else if ( strcmp(arg, "-cache_size") == 0 ) { + const char* cacheSize = argv[++i]; + if ( cacheSize == nullptr ) { + fprintf(stderr, "-cache_size option requires a path to follow\n"); + return 1; + } + char *end; + cacheIdent.cacheMappedSize = strtol(cacheSize, &end, 0); + } + else if ( strcmp(arg, "-pipefd") == 0 ) { + const char* numStr = argv[++i]; + if ( numStr == nullptr ) { + fprintf(stderr, "-pipefd option requires an file descriptor number to follow\n"); + return 1; + } + char *end; + pipeNum = (int)strtol(numStr, &end, 0); + if ( (pipeNum == 0) && (errno != 0) ) { + fprintf(stderr, "bad value (%s) for -pipefd option %d\n", numStr, pipeNum); + return 1; + } + } + else if ( strcmp(arg, "-env") == 0 ) { + const char* var = argv[++i]; + if ( var == nullptr ) { + fprintf(stderr, "-env option requires a following VAR=XXX\n"); + return 1; + } + dyldEnvVars.push_back(var); + } + else { + fprintf(stderr, "unknown option: %s\n", arg); + return 1; + } + } + if ( progPath == nullptr ) { + fprintf(stderr, "missing required -create_closure option\n"); + return 1; + } + if ( pipeNum == -1 ) { + fprintf(stderr, "missing required -pipefd option\n"); + return 1; + } + + if ( verbose ) { + fprintf(stderr, "closured: runAsTool()\n"); + for (int i=1; i < argc; ++i) + fprintf(stderr, " argv[%d] = %s\n", i, argv[i]); + } + + os_log(sLog, "fork/exec request to build closure for %s\n", progPath); + SocketBasedClousureHeader header; + + // find dyld cache for requested arch + size_t currentCacheSize; + const DyldSharedCache* currentCache = (const DyldSharedCache*)_dyld_get_shared_cache_range(¤tCacheSize); + if ( currentCache == nullptr ) { + os_log_error(sLog, "closured is running without a dyld cache\n"); + return 1; + } + uuid_t currentCacheUUID; + currentCache->getUUID(currentCacheUUID); + if ( memcmp(currentCacheUUID, cacheIdent.cacheUUID, 16) != 0 ) { + const char* errorString = "closured is running with a different dyld cache than client"; + os_log_error(sLog, "%s\n", errorString); + header.success = 0; + header.length = (int)strlen(errorString) + 1; + write(pipeNum, &header, sizeof(SocketBasedClousureHeader)); + write(pipeNum, errorString, header.length); + close(pipeNum); + return 0; + } + dyld3::DyldCacheParser cacheParser(currentCache, false); + + Diagnostics diag; + os_log_info(sLog, "starting closure build\n"); + const dyld3::launch_cache::BinaryClosureData* cls = dyld3::ImageProxyGroup::makeClosure(diag, cacheParser, progPath, false, {""}, dyldEnvVars); + os_log_info(sLog, "finished closure build, cls=%p\n", cls); + if ( diag.noError() ) { + // on success write the closure binary after the header to the socket + dyld3::launch_cache::Closure closure(cls); + os_log(sLog, "returning closure, size=%lu\n", closure.size()); + header.success = 1; + header.length = (uint32_t)closure.size(); + write(pipeNum, &header, sizeof(SocketBasedClousureHeader)); + write(pipeNum, cls, closure.size()); + } + else { + // on failure write the error message after the header to the socket + const std::string& message = diag.errorMessage(); + os_log_error(sLog, "closure could not be created: %s\n", message.c_str()); + header.success = 0; + header.length = (uint32_t)message.size() + 1; + write(pipeNum, &header, sizeof(SocketBasedClousureHeader)); + write(pipeNum, message.c_str(), header.length); + } + + close(pipeNum); + + return 0; +} + + +union MaxMsgSize { + union __RequestUnion__do_closured_subsystem req; + union __ReplyUnion__do_closured_subsystem rep; +}; + +int main(int argc, const char* argv[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // establish sandbox around process + char* errMsg = nullptr; + if ( sandbox_init_with_parameters("com.apple.dyld.closured", SANDBOX_NAMED, nullptr, &errMsg) != 0 ) { + os_log_error(sLog, "Failed to enter sandbox: %{public}s", errMsg); + exit(EXIT_FAILURE); + } +#endif + + if ( argc != 1 ) + return runAsTool(argc, argv);\ + + mach_port_t serverPort = MACH_PORT_NULL; + kern_return_t kr = bootstrap_check_in(bootstrap_port, CLOSURED_SERVICE_NAME, &serverPort); + if (kr != KERN_SUCCESS) + exit(-1); + + dispatch_source_t machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, serverPort, 0, dispatch_get_main_queue()); + if (machSource == nullptr) + exit(-1); + + dispatch_source_set_event_handler(machSource, ^{ + dispatch_mig_server(machSource, sizeof(union MaxMsgSize), closured_server); + }); + dispatch_source_set_cancel_handler(machSource, ^{ + mach_port_t port = (mach_port_t)dispatch_source_get_handle(machSource); + kern_return_t kr = mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); + if (kr != KERN_SUCCESS) + exit(-1); + }); + dispatch_resume(machSource); + dispatch_main(); + + return 0; +} + diff --git a/dyld/dyld3/closured/closuredProtocol.defs b/dyld/dyld3/closured/closuredProtocol.defs new file mode 100644 index 0000000..565c8bc --- /dev/null +++ b/dyld/dyld3/closured/closuredProtocol.defs @@ -0,0 +1,29 @@ + + +#include +#include + +import "closuredtypes.h"; + +subsystem closured 6000; + +userprefix closured_; // Routine prefixes for user access +serverprefix do_; // Routine prefixes for internal server access + +type OutOfLineBuffer_t = ^array[] of MACH_MSG_TYPE_BYTE ctype: vm_address_t; + +// used at launch +routine CreateClosure ( + port : mach_port_t; + in requestor : task_t; + in buffer : OutOfLineBuffer_t; + out returnData : OutOfLineBuffer_t, dealloc +); + +// used in dlopen()cl +routine CreateImageGroup ( + port : mach_port_t; + in requestor : task_t; + in buffer : OutOfLineBuffer_t; + out returnData : OutOfLineBuffer_t, dealloc +); diff --git a/dyld/dyld3/closured/closured_entitlements.plist b/dyld/dyld3/closured/closured_entitlements.plist new file mode 100644 index 0000000..5a352d6 --- /dev/null +++ b/dyld/dyld3/closured/closured_entitlements.plist @@ -0,0 +1,12 @@ + + + + + seatbelt-profiles + + closured + + platform-application + + + diff --git a/dyld/dyld3/closured/closuredtypes.h b/dyld/dyld3/closured/closuredtypes.h new file mode 100644 index 0000000..6acc727 --- /dev/null +++ b/dyld/dyld3/closured/closuredtypes.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 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 + +#undef __MigTypeCheck +#undef USING_VOUCHERS + + +typedef void* ClosureBufferPtr; +typedef void* ClosureBufferConstPtr; + +struct SocketBasedClousureHeader +{ + uint32_t success; // 1 => rest of buffer is closure, 0 => rest of buffer is error string + uint32_t length; +}; + +#define CLOSURED_SERVICE_NAME "com.apple.dyld.closured" + +#define mig_external __private_extern__ + diff --git a/dyld/dyld3/closured/com.apple.dyld.closured.plist b/dyld/dyld3/closured/com.apple.dyld.closured.plist new file mode 100644 index 0000000..e19d134 --- /dev/null +++ b/dyld/dyld3/closured/com.apple.dyld.closured.plist @@ -0,0 +1,25 @@ + + + + + ProcessType + Adaptive + EnableTransactions + + EnablePressuredExit + + Label + com.apple.dyld.closured + MachServices + + com.apple.dyld.closured + + + TimeOut + 60 + ProgramArguments + + /usr/libexec/closured + + + diff --git a/dyld/dyld3/closured/com.apple.dyld.closured.sb b/dyld/dyld3/closured/com.apple.dyld.closured.sb new file mode 100644 index 0000000..bd89f9c --- /dev/null +++ b/dyld/dyld3/closured/com.apple.dyld.closured.sb @@ -0,0 +1,22 @@ +;;; Copyright (c) 2017 Apple Inc. All Rights reserved. +;;; +;;; WARNING: The sandbox rules in this file currently constitute +;;; Apple System Private Interface and are subject to change at any time and +;;; without notice. +;;; +(version 1) + +(deny default) +(deny file-map-executable iokit-get-properties process-info* nvram*) +(deny dynamic-code-generation) + +(import "system.sb") + +;; For reading dylibs +(allow file-read*) + +;; For resolving symlinks, realpath(3), and equivalents. +(allow file-read-metadata) + +;; for logging name of client +(allow process-info-pidinfo) diff --git a/dyld/dyld3/dyld-potential-framework-overrides b/dyld/dyld3/dyld-potential-framework-overrides new file mode 100644 index 0000000..998a298 --- /dev/null +++ b/dyld/dyld3/dyld-potential-framework-overrides @@ -0,0 +1,7 @@ +/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation +/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation +/System/Library/Frameworks/MediaToolbox.framework/Versions/A/MediaToolbox +/System/Library/PrivateFrameworks/MetalTools.framework/Versions/A/MetalTools +/System/Library/Frameworks/WebKit.framework/Versions/A/WebKit +/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/JavaScriptCore + diff --git a/dyld/dyld3/libclosured-stub.cpp b/dyld/dyld3/libclosured-stub.cpp new file mode 100644 index 0000000..90820e4 --- /dev/null +++ b/dyld/dyld3/libclosured-stub.cpp @@ -0,0 +1,12 @@ + +namespace dyld3 { + +struct ClosureBuffer { int x; }; + +ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input) +{ + return ClosureBuffer(); +} + + +} // namespace dyld3 diff --git a/dyld/dyld3/libdyldEntryVector.cpp b/dyld/dyld3/libdyldEntryVector.cpp new file mode 100644 index 0000000..9735df2 --- /dev/null +++ b/dyld/dyld3/libdyldEntryVector.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +#include "dyld_priv.h" +#include "libdyldEntryVector.h" +#include "AllImages.h" +#include "Logging.h" +#include "PathOverrides.h" +#include "LaunchCacheFormat.h" + +extern "C" void start(); + + +VIS_HIDDEN const char** appleParams; + +extern bool gUseDyld3; + +namespace dyld3 { + + +AllImages::ProgramVars sVars; +static void (*sChildForkFunction)(); + +static const char* leafName(const char* argv0) +{ + if ( argv0 == nullptr ) + return ""; + + if ( const char* lastSlash = strrchr(argv0, '/') ) + return lastSlash+1; + else + return argv0; +} + +static void entry_setVars(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + NXArgc = argc; + NXArgv = argv; + environ = (char**)envp; + appleParams = apple; + __progname = leafName(argv[0]); + + sVars.mh = mainMH; + sVars.NXArgcPtr = &NXArgc; + sVars.NXArgvPtr = &NXArgv; + sVars.environPtr = (const char***)&environ; + sVars.__prognamePtr = &__progname; + gAllImages.setProgramVars(&sVars); + + gUseDyld3 = true; + + setLoggingFromEnvs(envp); +} + +static void entry_setHaltFunction(void (*func)(const char* message) __attribute__((noreturn)) ) +{ + setHaltFunction(func); +} + +static void entry_setLogFunction(void (*logFunction)(const char* format, va_list list)) +{ + setLoggingFunction(logFunction); +} + +static void entry_setOldAllImageInfo(dyld_all_image_infos* old) +{ + gAllImages.setOldAllImageInfo(old); +} + +static void entry_setInitialImageList(const launch_cache::binary_format::Closure* closure, + const void* dyldCacheLoadAddress, const char* dyldCachePath, + const dyld3::launch_cache::DynArray& initialImages, + const mach_header* libSystemMH, const launch_cache::binary_format::Image* libSystemImage) +{ + gAllImages.init(closure, dyldCacheLoadAddress, dyldCachePath, initialImages); + gAllImages.applyInterposingToDyldCache(closure, initialImages); + + const char* mainPath = _simple_getenv(appleParams, "executable_path"); + if ( (mainPath != nullptr) && (mainPath[0] == '/') ) + gAllImages.setMainPath(mainPath); + + // ok to call before malloc is ready because 4 slots are reserved. + gAllImages.setInitialGroups(); + + // run initializer for libSytem.B.dylib + // this calls back into _dyld_initializer which calls gAllIimages.addImages() + gAllImages.runLibSystemInitializer(libSystemMH, libSystemImage); + + // now that malloc is available, parse DYLD_ env vars + gPathOverrides.setEnvVars((const char**)environ); +} + +static void entry_runInitialzersBottomUp(const mach_header* mainExecutableImageLoadAddress) +{ + gAllImages.runInitialzersBottomUp(mainExecutableImageLoadAddress); + gAllImages.notifyMonitorMain(); +} + +static void entry_setChildForkFunction(void (*func)() ) +{ + sChildForkFunction = func; +} + +const LibDyldEntryVector entryVectorForDyld = { + LibDyldEntryVector::kCurrentVectorVersion, + launch_cache::binary_format::kFormatVersion, + &entry_setVars, + &entry_setHaltFunction, + &entry_setOldAllImageInfo, + &entry_setInitialImageList, + &entry_runInitialzersBottomUp, + &start, + &entry_setChildForkFunction, + &entry_setLogFunction, +}; + +VIS_HIDDEN void _dyld_fork_child() +{ + (*sChildForkFunction)(); +} + + +} // namespace dyld3 + diff --git a/dyld/dyld3/libdyldEntryVector.h b/dyld/dyld3/libdyldEntryVector.h new file mode 100644 index 0000000..3c61df2 --- /dev/null +++ b/dyld/dyld3/libdyldEntryVector.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017 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_ENTRY_VECTOR_H__ +#define __DYLD_ENTRY_VECTOR_H__ + +#include + +#include "LaunchCache.h" +#include "Loading.h" + +struct dyld_all_image_infos; + +namespace dyld3 { + +struct LibDyldEntryVector +{ + enum { kCurrentVectorVersion = 4 }; + + uint32_t vectorVersion; // should be kCurrentVectorVersion + uint32_t binaryFormatVersion; // should be launch_cache::binary_format::kFormatVersion + void (*setVars)(const mach_header* mainMH, int argc, const char* argv[], const char* envp[], const char* apple[]); + void (*setHaltFunction)(void (*func)(const char* message) __attribute__((noreturn)) ); + void (*setOldAllImageInfo)(dyld_all_image_infos*); + void (*setInitialImageList)(const launch_cache::BinaryClosureData* closure, + const void* dyldCacheLoadAddress, const char* dyldCachePath, + const dyld3::launch_cache::DynArray& initialImages, + const mach_header* libSystemMH, const launch_cache::BinaryImageData* libSystemImage); + void (*runInitialzersBottomUp)(const mach_header* topImageLoadAddress); + void (*startFunc)(); + // added in version 3 + void (*setChildForkFunction)(void (*func)()); + // added in version 4 + void (*setLogFunction)(void (*logFunction)(const char* format, va_list list)); +}; + +extern const LibDyldEntryVector entryVectorForDyld; + +} // namespace dyld3 + + +#endif // __DYLD_ENTRY_VECTOR_H__ + + + + diff --git a/dyld/dyld3/shared-cache/AdjustDylibSegments.cpp b/dyld/dyld3/shared-cache/AdjustDylibSegments.cpp new file mode 100644 index 0000000..9ab3bb8 --- /dev/null +++ b/dyld/dyld3/shared-cache/AdjustDylibSegments.cpp @@ -0,0 +1,1084 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "CacheBuilder.h" +#include "Diagnostics.h" +#include "DyldSharedCache.h" +#include "Trie.hpp" +#include "MachOFileAbstraction.hpp" + + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + +namespace { + +template +class Adjustor { +public: + Adjustor(DyldSharedCache* cacheBuffer, macho_header

* mh, const std::vector& mappingInfo, Diagnostics& diag); + void adjustImageForNewSegmentLocations(std::vector& pointersForASLR); + +private: + void adjustReferencesUsingInfoV2(std::vector& pointersForASLR); + void adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, int64_t adjust, int64_t targetSlide, + uint64_t imageStartAddress, uint64_t imageEndAddress, std::vector& pointersForASLR, uint32_t*& lastMappedAddr32, + uint32_t& lastKind, uint64_t& lastToNewAddress); + void adjustDataPointers(std::vector& pointersForASLR); + void slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersForASLR); + void adjustSymbolTable(); + void adjustExportsTrie(std::vector& newTrieBytes); + void rebuildLinkEdit(); + void adjustCode(); + void adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta); + void rebuildLinkEditAndLoadCommands(); + uint64_t slideForOrigAddress(uint64_t addr); + + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + DyldSharedCache* _cacheBuffer; + macho_header

* _mh; + Diagnostics& _diagnostics; + const uint8_t* _linkeditBias = nullptr; + int64_t _linkeditAdjust = 0; + unsigned _linkeditSegIndex = 0; + bool _maskPointers = false; + bool _splitSegInfoV2 = false; + const char* _installName = nullptr; + macho_symtab_command

* _symTabCmd = nullptr; + macho_dysymtab_command

* _dynSymTabCmd = nullptr; + macho_dyld_info_command

* _dyldInfo = nullptr; + macho_linkedit_data_command

* _splitSegInfoCmd = nullptr; + macho_linkedit_data_command

* _functionStartsCmd = nullptr; + macho_linkedit_data_command

* _dataInCodeCmd = nullptr; + std::vector _segOrigStartAddresses; + std::vector _segSlides; + std::vector*> _segCmds; + const std::vector& _mappingInfo; +}; + +template +Adjustor

::Adjustor(DyldSharedCache* cacheBuffer, macho_header

* mh, const std::vector& mappingInfo, Diagnostics& diag) + : _cacheBuffer(cacheBuffer), _mh(mh), _diagnostics(diag), _mappingInfo(mappingInfo) +{ + assert((mh->magic() == MH_MAGIC) || (mh->magic() == MH_MAGIC_64)); + macho_segment_command

* segCmd; + const macho_load_command

* const cmds = (macho_load_command

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

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

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

*)cmd)->name(); + break; + case LC_SYMTAB: + _symTabCmd = (macho_symtab_command

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

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

*)cmd; + break; + case LC_SEGMENT_SPLIT_INFO: + _splitSegInfoCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_FUNCTION_STARTS: + _functionStartsCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_DATA_IN_CODE: + _dataInCodeCmd = (macho_linkedit_data_command

*)cmd; + break; + case macho_segment_command

::CMD: + segCmd = (macho_segment_command

*)cmd; + _segCmds.push_back(segCmd); + _segOrigStartAddresses.push_back(segCmd->vmaddr()); + _segSlides.push_back(_mappingInfo[segIndex].dstCacheAddress - segCmd->vmaddr()); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + _linkeditAdjust = _mappingInfo[segIndex].dstCacheOffset - segCmd->fileoff(); + _linkeditBias = (uint8_t*)cacheBuffer + _linkeditAdjust; + _linkeditSegIndex = segIndex; + } + ++segIndex; + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + _maskPointers = (P::E::get32(mh->cputype()) == CPU_TYPE_ARM64); + if ( _splitSegInfoCmd != NULL ) { + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + _splitSegInfoV2 = (*infoStart == DYLD_CACHE_ADJ_V2_FORMAT); + } + else { + _diagnostics.error("missing LC_SEGMENT_SPLIT_INFO in %s", _installName); + } +} + +template +void Adjustor

::adjustImageForNewSegmentLocations(std::vector& pointersForASLR) +{ + if ( _diagnostics.hasError() ) + return; + if ( _splitSegInfoV2 ) { + adjustReferencesUsingInfoV2(pointersForASLR); + } + else { + adjustDataPointers(pointersForASLR); + adjustCode(); + } + if ( _diagnostics.hasError() ) + return; + adjustSymbolTable(); + if ( _diagnostics.hasError() ) + return; + rebuildLinkEditAndLoadCommands(); +} + +template +uint64_t Adjustor

::slideForOrigAddress(uint64_t addr) +{ + for (unsigned i=0; i < _segOrigStartAddresses.size(); ++i) { + if ( (_segOrigStartAddresses[i] <= addr) && (addr < (_segOrigStartAddresses[i]+_segCmds[i]->vmsize())) ) + return _segSlides[i]; + } + // On arm64, high nibble of pointers can have extra bits + if ( _maskPointers && (addr & 0xF000000000000000) ) { + return slideForOrigAddress(addr & 0x0FFFFFFFFFFFFFFF); + } + _diagnostics.error("slide not known for dylib address 0x%llX in %s", addr, _installName); + return 0; +} + +template +void Adjustor

::rebuildLinkEditAndLoadCommands() +{ + // Exports trie is only data structure in LINKEDIT that might grow + std::vector newTrieBytes; + adjustExportsTrie(newTrieBytes); + + // Remove: code signature, rebase info, code-sign-dirs, split seg info + uint32_t bindOffset = 0; + uint32_t bindSize = _dyldInfo->bind_size(); + uint32_t lazyBindOffset = bindOffset + bindSize; + uint32_t lazyBindSize = _dyldInfo->lazy_bind_size(); + uint32_t weakBindOffset = lazyBindOffset + lazyBindSize; + uint32_t weakBindSize = _dyldInfo->weak_bind_size(); + uint32_t exportOffset = weakBindOffset + weakBindSize; + uint32_t exportSize = (uint32_t)newTrieBytes.size(); + uint32_t splitSegInfoOffset = exportOffset + exportSize; + uint32_t splitSegInfosSize = (_splitSegInfoCmd ? _splitSegInfoCmd->datasize() : 0); + uint32_t funcStartsOffset = splitSegInfoOffset + splitSegInfosSize; + uint32_t funcStartsSize = (_functionStartsCmd ? _functionStartsCmd->datasize() : 0); + uint32_t dataInCodeOffset = funcStartsOffset + funcStartsSize; + uint32_t dataInCodeSize = (_dataInCodeCmd ? _dataInCodeCmd->datasize() : 0); + uint32_t symbolTableOffset = dataInCodeOffset + dataInCodeSize; + uint32_t symbolTableSize = _symTabCmd->nsyms() * sizeof(macho_nlist

); + uint32_t indirectTableOffset = symbolTableOffset + symbolTableSize; + uint32_t indirectTableSize = _dynSymTabCmd->nindirectsyms() * sizeof(uint32_t); + uint32_t symbolStringsOffset = indirectTableOffset + indirectTableSize; + uint32_t symbolStringsSize = _symTabCmd->strsize(); + uint32_t newLinkEditSize = symbolStringsOffset + symbolStringsSize; + + size_t linkeditBufferSize = align(_segCmds[_linkeditSegIndex]->vmsize(), 12); + if ( linkeditBufferSize < newLinkEditSize ) { + _diagnostics.error("LINKEDIT overflow in %s", _installName); + return; + } + + uint32_t linkeditStartOffset = (uint32_t)_mappingInfo[_linkeditSegIndex].dstCacheOffset; + uint8_t* newLinkeditBufer = (uint8_t*)::calloc(linkeditBufferSize, 1); + if ( bindSize ) + memcpy(&newLinkeditBufer[bindOffset], &_linkeditBias[_dyldInfo->bind_off()], bindSize); + if ( lazyBindSize ) + memcpy(&newLinkeditBufer[lazyBindOffset], &_linkeditBias[_dyldInfo->lazy_bind_off()], lazyBindSize); + if ( weakBindSize ) + memcpy(&newLinkeditBufer[weakBindOffset], &_linkeditBias[_dyldInfo->weak_bind_off()], weakBindSize); + if ( exportSize ) + memcpy(&newLinkeditBufer[exportOffset], &newTrieBytes[0], exportSize); + if ( splitSegInfosSize ) + memcpy(&newLinkeditBufer[splitSegInfoOffset], &_linkeditBias[_splitSegInfoCmd->dataoff()], splitSegInfosSize); + if ( funcStartsSize ) + memcpy(&newLinkeditBufer[funcStartsOffset], &_linkeditBias[_functionStartsCmd->dataoff()], funcStartsSize); + if ( dataInCodeSize ) + memcpy(&newLinkeditBufer[dataInCodeOffset], &_linkeditBias[_dataInCodeCmd->dataoff()], dataInCodeSize); + if ( symbolTableSize ) + memcpy(&newLinkeditBufer[symbolTableOffset], &_linkeditBias[_symTabCmd->symoff()], symbolTableSize); + if ( indirectTableSize ) + memcpy(&newLinkeditBufer[indirectTableOffset], &_linkeditBias[_dynSymTabCmd->indirectsymoff()], indirectTableSize); + if ( symbolStringsSize ) + memcpy(&newLinkeditBufer[symbolStringsOffset], &_linkeditBias[_symTabCmd->stroff()], symbolStringsSize); + + memcpy((uint8_t*)_cacheBuffer+linkeditStartOffset, newLinkeditBufer, newLinkEditSize); + ::bzero((uint8_t*)_cacheBuffer+linkeditStartOffset+newLinkEditSize, linkeditBufferSize-newLinkEditSize); + ::free(newLinkeditBufer); + + // updates load commands and removed ones no longer needed + macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)_mh + sizeof(macho_header

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

* cmd = cmds; + const unsigned origLoadCommandsSize = _mh->sizeofcmds(); + unsigned bytesRemaining = origLoadCommandsSize; + unsigned removedCount = 0; + unsigned segIndex = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + macho_symtab_command

* symTabCmd; + macho_dysymtab_command

* dynSymTabCmd; + macho_dyld_info_command

* dyldInfo; + macho_linkedit_data_command

* functionStartsCmd; + macho_linkedit_data_command

* dataInCodeCmd; + macho_linkedit_data_command

* splitSegInfoCmd; + macho_segment_command

* segCmd; + macho_routines_command

* routinesCmd; + macho_dylib_command

* dylibIDCmd; + uint32_t cmdSize = cmd->cmdsize(); + int32_t segFileOffsetDelta; + bool remove = false; + switch ( cmd->cmd() ) { + case LC_ID_DYLIB: + dylibIDCmd = (macho_dylib_command

*)cmd; + dylibIDCmd->set_timestamp(2); // match what static linker sets in LC_LOAD_DYLIB + break; + case LC_SYMTAB: + symTabCmd = (macho_symtab_command

*)cmd; + symTabCmd->set_symoff(linkeditStartOffset+symbolTableOffset); + symTabCmd->set_stroff(linkeditStartOffset+symbolStringsOffset); + break; + case LC_DYSYMTAB: + dynSymTabCmd = (macho_dysymtab_command

*)cmd; + dynSymTabCmd->set_indirectsymoff(linkeditStartOffset+indirectTableOffset); + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (macho_dyld_info_command

*)cmd; + dyldInfo->set_rebase_off(0); + dyldInfo->set_rebase_size(0); + dyldInfo->set_bind_off(bindSize ? linkeditStartOffset+bindOffset : 0); + dyldInfo->set_bind_size(bindSize); + dyldInfo->set_weak_bind_off(weakBindSize ? linkeditStartOffset+weakBindOffset : 0); + dyldInfo->set_weak_bind_size(weakBindSize); + dyldInfo->set_lazy_bind_off(lazyBindSize ? linkeditStartOffset+lazyBindOffset : 0); + dyldInfo->set_lazy_bind_size(lazyBindSize); + dyldInfo->set_export_off(exportSize ? linkeditStartOffset+exportOffset : 0); + dyldInfo->set_export_size(exportSize); + break; + case LC_FUNCTION_STARTS: + functionStartsCmd = (macho_linkedit_data_command

*)cmd; + functionStartsCmd->set_dataoff(linkeditStartOffset+funcStartsOffset); + break; + case LC_DATA_IN_CODE: + dataInCodeCmd = (macho_linkedit_data_command

*)cmd; + dataInCodeCmd->set_dataoff(linkeditStartOffset+dataInCodeOffset); + break; + case macho_routines_command

::CMD: + routinesCmd = (macho_routines_command

*)cmd; + routinesCmd->set_init_address(routinesCmd->init_address()+slideForOrigAddress(routinesCmd->init_address())); + break; + case macho_segment_command

::CMD: + segCmd = (macho_segment_command

*)cmd; + segFileOffsetDelta = (int32_t)(_mappingInfo[segIndex].dstCacheOffset - segCmd->fileoff()); + segCmd->set_vmaddr(_mappingInfo[segIndex].dstCacheAddress); + segCmd->set_vmsize(_mappingInfo[segIndex].dstCacheSegmentSize); + segCmd->set_fileoff(_mappingInfo[segIndex].dstCacheOffset); + segCmd->set_filesize(_mappingInfo[segIndex].copySegmentSize); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + segCmd->set_vmsize(linkeditBufferSize); + if ( segCmd->nsects() > 0 ) { + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + sect->set_addr(sect->addr() + _segSlides[segIndex]); + if ( sect->offset() != 0 ) + sect->set_offset(sect->offset() + segFileOffsetDelta); + } + } + ++segIndex; + break; + case LC_RPATH: + _diagnostics.warning("dyld shared cache does not support LC_RPATH found in %s", _installName); + remove = true; + break; + case LC_SEGMENT_SPLIT_INFO: + splitSegInfoCmd = (macho_linkedit_data_command

*)cmd; + splitSegInfoCmd->set_dataoff(linkeditStartOffset+splitSegInfoOffset); + break; + case LC_CODE_SIGNATURE: + case LC_DYLIB_CODE_SIGN_DRS: + remove = true; + break; + default: + break; + } + macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); + if ( remove ) { + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + ++removedCount; + } + else { + bytesRemaining -= cmdSize; + cmd = nextCmd; + } + } + // zero out stuff removed + ::bzero((void*)cmd, bytesRemaining); + // update header + _mh->set_ncmds(cmd_count-removedCount); + _mh->set_sizeofcmds(origLoadCommandsSize-bytesRemaining); + _mh->set_flags(_mh->flags() | 0x80000000); +} + + +template +void Adjustor

::adjustSymbolTable() +{ + macho_nlist

* symbolTable = (macho_nlist

*)&_linkeditBias[_symTabCmd->symoff()]; + + // adjust global symbol table entries + macho_nlist

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

* entry = &symbolTable[_dynSymTabCmd->iextdefsym()]; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) == N_SECT ) + entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value())); + } + + // adjust local symbol table entries + macho_nlist

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

* entry = &symbolTable[_dynSymTabCmd->ilocalsym()]; entry < lastLocal; ++entry) { + if ( (entry->n_sect() != NO_SECT) && ((entry->n_type() & N_STAB) == 0) ) + entry->set_n_value(entry->n_value() + slideForOrigAddress(entry->n_value())); + } +} + +template +void Adjustor

::slidePointer(int segIndex, uint64_t segOffset, uint8_t type, std::vector& pointersForASLR) +{ + pint_t* mappedAddrP = (pint_t*)((uint8_t*)_cacheBuffer + _mappingInfo[segIndex].dstCacheOffset + segOffset); + uint32_t* mappedAddr32 = (uint32_t*)mappedAddrP; + pint_t valueP; + uint32_t value32; + switch ( type ) { + case REBASE_TYPE_POINTER: + valueP = (pint_t)P::getP(*mappedAddrP); + P::setP(*mappedAddrP, valueP + slideForOrigAddress(valueP)); + pointersForASLR.push_back(mappedAddrP); + break; + + case REBASE_TYPE_TEXT_ABSOLUTE32: + value32 = P::E::get32(*mappedAddr32); + P::E::set32(*mappedAddr32, value32 + (uint32_t)slideForOrigAddress(value32)); + break; + + case REBASE_TYPE_TEXT_PCREL32: + // general text relocs not support + default: + _diagnostics.error("unknown rebase type 0x%02X in %s", type, _installName); + } +} + + +static bool isThumbMovw(uint32_t instruction) +{ + return ( (instruction & 0x8000FBF0) == 0x0000F240 ); +} + +static bool isThumbMovt(uint32_t instruction) +{ + return ( (instruction & 0x8000FBF0) == 0x0000F2C0 ); +} + +static uint16_t getThumbWord(uint32_t instruction) +{ + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + return ((imm4 << 12) | (i << 11) | (imm3 << 8) | imm8); +} + +static uint32_t setThumbWord(uint32_t instruction, uint16_t word) { + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t i = (word & 0x0800) >> 11; + uint32_t imm3 = (word & 0x0700) >> 8; + uint32_t imm8 = word & 0x00FF; + return (instruction & 0x8F00FBF0) | imm4 | (i << 10) | (imm3 << 28) | (imm8 << 16); +} + +static bool isArmMovw(uint32_t instruction) +{ + return (instruction & 0x0FF00000) == 0x03000000; +} + +static bool isArmMovt(uint32_t instruction) +{ + return (instruction & 0x0FF00000) == 0x03400000; +} + +static uint16_t getArmWord(uint32_t instruction) +{ + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + return (imm4 << 12) | imm12; +} + +static uint32_t setArmWord(uint32_t instruction, uint16_t word) { + uint32_t imm4 = (word & 0xF000) >> 12; + uint32_t imm12 = word & 0x0FFF; + return (instruction & 0xFFF0F000) | (imm4 << 16) | imm12; +} + +template +void Adjustor

::adjustReference(uint32_t kind, uint8_t* mappedAddr, uint64_t fromNewAddress, uint64_t toNewAddress, + int64_t adjust, int64_t targetSlide, uint64_t imageStartAddress, uint64_t imageEndAddress, + std::vector& pointersForASLR, uint32_t*& lastMappedAddr32, uint32_t& lastKind, uint64_t& lastToNewAddress) +{ + uint64_t value64; + uint64_t* mappedAddr64 = 0; + uint32_t value32; + uint32_t* mappedAddr32 = 0; + uint32_t instruction; + int64_t offsetAdjust; + int64_t delta; + switch ( kind ) { + case DYLD_CACHE_ADJ_V2_DELTA_32: + mappedAddr32 = (uint32_t*)mappedAddr; + value32 = P::E::get32(*mappedAddr32); + delta = (int32_t)value32; + delta += adjust; + if ( (delta > 0x80000000) || (-delta > 0x80000000) ) { + _diagnostics.error("DYLD_CACHE_ADJ_V2_DELTA_32 can't be adjust by 0x%016llX in %s", adjust, _installName); + return; + } + P::E::set32(*mappedAddr32, (int32_t)delta); + break; + case DYLD_CACHE_ADJ_V2_POINTER_32: + mappedAddr32 = (uint32_t*)mappedAddr; + if ( toNewAddress != (E::get32(*mappedAddr32) + targetSlide) ) { + _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_32 value not as expected at address 0x%llX in %s", fromNewAddress, _installName); + return; + } + E::set32(*mappedAddr32, (uint32_t)toNewAddress); + pointersForASLR.push_back(mappedAddr); + break; + case DYLD_CACHE_ADJ_V2_POINTER_64: + mappedAddr64 = (uint64_t*)mappedAddr; + if ( toNewAddress != (E::get64(*mappedAddr64) + targetSlide) ) { + _diagnostics.error("bad DYLD_CACHE_ADJ_V2_POINTER_64 value not as expected at address 0x%llX in %s", fromNewAddress, _installName); + return; + } + E::set64(*mappedAddr64, toNewAddress); + pointersForASLR.push_back(mappedAddr); + break; + case DYLD_CACHE_ADJ_V2_DELTA_64: + mappedAddr64 = (uint64_t*)mappedAddr; + value64 = P::E::get64(*mappedAddr64); + E::set64(*mappedAddr64, value64 + adjust); + break; + case DYLD_CACHE_ADJ_V2_IMAGE_OFF_32: + if ( adjust == 0 ) + break; + mappedAddr32 = (uint32_t*)mappedAddr; + value32 = P::E::get32(*mappedAddr32); + value64 = toNewAddress - imageStartAddress; + if ( value64 > imageEndAddress ) { + _diagnostics.error("DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 can't be adjust to 0x%016llX in %s", toNewAddress, _installName); + return; + } + P::E::set32(*mappedAddr32, (uint32_t)value64); + break; + case DYLD_CACHE_ADJ_V2_ARM64_ADRP: + mappedAddr32 = (uint32_t*)mappedAddr; + instruction = P::E::get32(*mappedAddr32); + if ( (instruction & 0x9F000000) == 0x90000000 ) { + int64_t pageDistance = ((toNewAddress & ~0xFFF) - (fromNewAddress & ~0xFFF)); + int64_t newPage21 = pageDistance >> 12; + if ( (newPage21 > 2097151) || (newPage21 < -2097151) ) { + _diagnostics.error("DYLD_CACHE_ADJ_V2_ARM64_ADRP can't be adjusted that far in %s", _installName); + return; + } + instruction = (instruction & 0x9F00001F) | ((newPage21 << 29) & 0x60000000) | ((newPage21 << 3) & 0x00FFFFE0); + P::E::set32(*mappedAddr32, instruction); + } + else { + // ADRP instructions are sometimes optimized to other instructions (e.g. ADR) after the split-seg-info is generated + } + break; + case DYLD_CACHE_ADJ_V2_ARM64_OFF12: + mappedAddr32 = (uint32_t*)mappedAddr; + instruction = P::E::get32(*mappedAddr32); + offsetAdjust = (adjust & 0xFFF); + if ( offsetAdjust == 0 ) + break; + if ( (instruction & 0x3B000000) == 0x39000000 ) { + // LDR/STR imm12 + if ( offsetAdjust != 0 ) { + uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); + uint32_t newAddend = 0; + switch ( instruction & 0xC0000000 ) { + case 0x00000000: + if ( (instruction & 0x04800000) == 0x04800000 ) { + if ( offsetAdjust & 0xF ) { + _diagnostics.error("can't adjust off12 scale=16 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + return; + } + if ( encodedAddend*16 >= 4096 ) { + _diagnostics.error("off12 scale=16 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + } + newAddend = (encodedAddend + offsetAdjust/16) % 256; + } + else { + // scale=1 + newAddend = (encodedAddend + (int32_t)offsetAdjust) % 4096; + } + break; + case 0x40000000: + if ( offsetAdjust & 1 ) { + _diagnostics.error("can't adjust off12 scale=2 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + return; + } + if ( encodedAddend*2 >= 4096 ) { + _diagnostics.error("off12 scale=2 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + return; + } + newAddend = (encodedAddend + offsetAdjust/2) % 2048; + break; + case 0x80000000: + if ( offsetAdjust & 3 ) { + _diagnostics.error("can't adjust off12 scale=4 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + return; + } + if ( encodedAddend*4 >= 4096 ) { + _diagnostics.error("off12 scale=4 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + return; + } + newAddend = (encodedAddend + offsetAdjust/4) % 1024; + break; + case 0xC0000000: + if ( offsetAdjust & 7 ) { + _diagnostics.error("can't adjust off12 scale=8 instruction by %lld bytes at mapped address=%p in %s", offsetAdjust, mappedAddr, _installName); + return; + } + if ( encodedAddend*8 >= 4096 ) { + _diagnostics.error("off12 scale=8 instruction points outside its page at mapped address=%p in %s", mappedAddr, _installName); + return; + } + newAddend = (encodedAddend + offsetAdjust/8) % 512; + break; + } + uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); + P::E::set32(*mappedAddr32, newInstruction); + } + } + else if ( (instruction & 0xFFC00000) == 0x91000000 ) { + // ADD imm12 + if ( instruction & 0x00C00000 ) { + _diagnostics.error("ADD off12 uses shift at mapped address=%p in %s", mappedAddr, _installName); + return; + } + uint32_t encodedAddend = ((instruction & 0x003FFC00) >> 10); + uint32_t newAddend = (encodedAddend + offsetAdjust) & 0xFFF; + uint32_t newInstruction = (instruction & 0xFFC003FF) | (newAddend << 10); + P::E::set32(*mappedAddr32, newInstruction); + } + else if ( instruction != 0xD503201F ) { + // ignore imm12 instructions optimized into a NOP, but warn about others + _diagnostics.error("unknown off12 instruction 0x%08X at 0x%0llX in %s", instruction, fromNewAddress, _installName); + return; + } + break; + case DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT: + mappedAddr32 = (uint32_t*)mappedAddr; + // to update a movw/movt pair we need to extract the 32-bit they will make, + // add the adjust and write back the new movw/movt pair. + if ( lastKind == kind ) { + if ( lastToNewAddress == toNewAddress ) { + uint32_t instruction1 = P::E::get32(*lastMappedAddr32); + uint32_t instruction2 = P::E::get32(*mappedAddr32); + if ( isThumbMovw(instruction1) && isThumbMovt(instruction2) ) { + uint16_t high = getThumbWord(instruction2); + uint16_t low = getThumbWord(instruction1); + uint32_t full = high << 16 | low; + full += adjust; + instruction1 = setThumbWord(instruction1, full & 0xFFFF); + instruction2 = setThumbWord(instruction2, full >> 16); + } + else if ( isThumbMovt(instruction1) && isThumbMovw(instruction2) ) { + uint16_t high = getThumbWord(instruction1); + uint16_t low = getThumbWord(instruction2); + uint32_t full = high << 16 | low; + full += adjust; + instruction2 = setThumbWord(instruction2, full & 0xFFFF); + instruction1 = setThumbWord(instruction1, full >> 16); + } + else { + _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but not paried in %s", _installName); + return; + } + P::E::set32(*lastMappedAddr32, instruction1); + P::E::set32(*mappedAddr32, instruction2); + kind = 0; + } + else { + _diagnostics.error("two DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT in a row but target different addresses in %s", _installName); + return; + } + } + break; + case DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT: + mappedAddr32 = (uint32_t*)mappedAddr; + // to update a movw/movt pair we need to extract the 32-bit they will make, + // add the adjust and write back the new movw/movt pair. + if ( lastKind == kind ) { + if ( lastToNewAddress == toNewAddress ) { + uint32_t instruction1 = P::E::get32(*lastMappedAddr32); + uint32_t instruction2 = P::E::get32(*mappedAddr32); + if ( isArmMovw(instruction1) && isArmMovt(instruction2) ) { + uint16_t high = getArmWord(instruction2); + uint16_t low = getArmWord(instruction1); + uint32_t full = high << 16 | low; + full += adjust; + instruction1 = setArmWord(instruction1, full & 0xFFFF); + instruction2 = setArmWord(instruction2, full >> 16); + } + else if ( isArmMovt(instruction1) && isArmMovw(instruction2) ) { + uint16_t high = getArmWord(instruction1); + uint16_t low = getArmWord(instruction2); + uint32_t full = high << 16 | low; + full += adjust; + instruction2 = setArmWord(instruction2, full & 0xFFFF); + instruction1 = setArmWord(instruction1, full >> 16); + } + else { + _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but not paired in %s", _installName); + return; + } + P::E::set32(*lastMappedAddr32, instruction1); + P::E::set32(*mappedAddr32, instruction2); + kind = 0; + } + else { + _diagnostics.error("two DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT in a row but target different addresses in %s", _installName); + return; + } + } + break; + case DYLD_CACHE_ADJ_V2_ARM64_BR26: + case DYLD_CACHE_ADJ_V2_THUMB_BR22: + case DYLD_CACHE_ADJ_V2_ARM_BR24: + // nothing to do with calls to stubs + break; + default: + _diagnostics.error("unknown split seg kind=%d in %s", kind, _installName); + return; + } + lastKind = kind; + lastToNewAddress = toNewAddress; + lastMappedAddr32 = mappedAddr32; +} + +template +void Adjustor

::adjustReferencesUsingInfoV2(std::vector& pointersForASLR) +{ + static const bool log = false; + + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()]; + if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) { + _diagnostics.error("malformed split seg info in %s", _installName); + return; + } + // build section arrays of slide and mapped address for each section + std::vector sectionSlides; + std::vector sectionNewAddress; + std::vector sectionMappedAddress; + sectionSlides.reserve(16); + sectionNewAddress.reserve(16); + sectionMappedAddress.reserve(16); + // section index 0 refers to mach_header + sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _mappingInfo[0].dstCacheOffset); + sectionSlides.push_back(_segSlides[0]); + sectionNewAddress.push_back(_mappingInfo[0].dstCacheAddress); + // section 1 and later refer to real sections + unsigned sectionIndex = 0; + for (unsigned segmentIndex=0; segmentIndex < _segCmds.size(); ++segmentIndex) { + macho_segment_command

* segCmd = _segCmds[segmentIndex]; + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + sectionMappedAddress.push_back((uint8_t*)_cacheBuffer + _mappingInfo[segmentIndex].dstCacheOffset + sect->addr() - segCmd->vmaddr()); + sectionSlides.push_back(_segSlides[segmentIndex]); + sectionNewAddress.push_back(_mappingInfo[segmentIndex].dstCacheAddress + sect->addr() - segCmd->vmaddr()); + if (log) { + fprintf(stderr, " %s/%s, sectIndex=%d, mapped at=%p\n", + sect->segname(), sect->sectname(), sectionIndex, sectionMappedAddress.back()); + } + ++sectionIndex; + } + } + + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + const uint8_t* p = infoStart; + uint64_t sectionCount = read_uleb128(p, infoEnd); + for (uint64_t i=0; i < sectionCount; ++i) { + uint32_t* lastMappedAddr32 = NULL; + uint32_t lastKind = 0; + uint64_t lastToNewAddress = 0; + uint64_t fromSectionIndex = read_uleb128(p, infoEnd); + uint64_t toSectionIndex = read_uleb128(p, infoEnd); + uint64_t toOffsetCount = read_uleb128(p, infoEnd); + uint64_t fromSectionSlide = sectionSlides[fromSectionIndex]; + uint64_t fromSectionNewAddress = sectionNewAddress[fromSectionIndex]; + uint8_t* fromSectionMappedAddress = sectionMappedAddress[fromSectionIndex]; + uint64_t toSectionSlide = sectionSlides[toSectionIndex]; + uint64_t toSectionNewAddress = sectionNewAddress[toSectionIndex]; + if (log) printf(" from sect=%lld (mapped=%p), to sect=%lld (new addr=0x%llX):\n", fromSectionIndex, fromSectionMappedAddress, toSectionIndex, toSectionNewAddress); + uint64_t toSectionOffset = 0; + for (uint64_t j=0; j < toOffsetCount; ++j) { + uint64_t toSectionDelta = read_uleb128(p, infoEnd); + uint64_t fromOffsetCount = read_uleb128(p, infoEnd); + toSectionOffset += toSectionDelta; + for (uint64_t k=0; k < fromOffsetCount; ++k) { + uint64_t kind = read_uleb128(p, infoEnd); + if ( kind > 12 ) { + _diagnostics.error("bad kind value (%llu) in %s", kind, _installName); + return; + } + uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); + uint64_t fromSectionOffset = 0; + for (uint64_t l=0; l < fromSectDeltaCount; ++l) { + uint64_t delta = read_uleb128(p, infoEnd); + fromSectionOffset += delta; + int64_t deltaAdjust = toSectionSlide - fromSectionSlide; + //if (log) printf(" kind=%lld, from offset=0x%0llX, to offset=0x%0llX, adjust=0x%llX, targetSlide=0x%llX\n", kind, fromSectionOffset, toSectionOffset, deltaAdjust, toSectionSlide); + uint8_t* fromMappedAddr = fromSectionMappedAddress + fromSectionOffset; + uint64_t toNewAddress = toSectionNewAddress + toSectionOffset; + uint64_t fromNewAddress = fromSectionNewAddress + fromSectionOffset; + uint64_t imageStartAddress = sectionNewAddress.front(); + uint64_t imageEndAddress = sectionNewAddress.back(); + if ( toSectionIndex != 255 ) + adjustReference((uint32_t)kind, fromMappedAddr, fromNewAddress, toNewAddress, deltaAdjust, toSectionSlide, imageStartAddress, imageEndAddress, pointersForASLR, lastMappedAddr32, lastKind, lastToNewAddress); + if ( _diagnostics.hasError() ) + return; + } + } + } + } + +} + +template +void Adjustor

::adjustDataPointers(std::vector& pointersForASLR) +{ + const uint8_t* p = &_linkeditBias[_dyldInfo->rebase_off()]; + const uint8_t* end = &p[_dyldInfo->rebase_size()]; + + uint8_t type = 0; + int segIndex = 0; + uint64_t segOffset = 0; + uint64_t count; + uint64_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + segOffset += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + segOffset += immediate*sizeof(pint_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + slidePointer(segIndex, segOffset, type, pointersForASLR); + 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) { + slidePointer(segIndex, segOffset, type, pointersForASLR); + segOffset += sizeof(pint_t); + } + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + slidePointer(segIndex, segOffset, type, pointersForASLR); + 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) { + slidePointer(segIndex, segOffset, type, pointersForASLR); + segOffset += skip + sizeof(pint_t); + } + break; + default: + _diagnostics.error("unknown rebase opcode 0x%02X in %s", opcode, _installName); + done = true; + break; + } + } +} + + +template +void Adjustor

::adjustInstruction(uint8_t kind, uint64_t cacheOffset, uint64_t codeToDataDelta) +{ + uint8_t* fixupLoc = (uint8_t*)_cacheBuffer + cacheOffset; + uint32_t* fixupLoc32 = (uint32_t*)fixupLoc; + uint64_t* fixupLoc64 = (uint64_t*)fixupLoc; + uint32_t instruction; + uint32_t value32; + uint64_t value64; + + switch (kind) { + case 1: // 32-bit pointer (including x86_64 RIP-rel) + value32 = P::E::get32(*fixupLoc32); + value32 += codeToDataDelta; + P::E::set32(*fixupLoc32, value32); + break; + case 2: // 64-bit pointer + value64 = P::E::get64(*fixupLoc64); + value64 += codeToDataDelta; + P::E::set64(*fixupLoc64, value64); + break; + case 4: // only used for i386, a reference to something in the IMPORT segment + break; + case 5: // used by thumb2 movw + instruction = P::E::get32(*fixupLoc32); + // slide is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value32 = (instruction & 0x0000000F) + ((uint32_t)codeToDataDelta >> 12); + instruction = (instruction & 0xFFFFFFF0) | (value32 & 0x0000000F); + P::E::set32(*fixupLoc32, instruction); + break; + case 6: // used by ARM movw + instruction = P::E::get32(*fixupLoc32); + // slide is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value32 = ((instruction & 0x000F0000) >> 16) + ((uint32_t)codeToDataDelta >> 12); + instruction = (instruction & 0xFFF0FFFF) | ((value32 <<16) & 0x000F0000); + P::E::set32(*fixupLoc32, instruction); + break; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + // used by thumb2 movt (low nibble of kind is high 4-bits of paired movw) + { + instruction = P::E::get32(*fixupLoc32); + assert((instruction & 0x8000FBF0) == 0x0000F2C0); + // extract 16-bit value from instruction + uint32_t i = ((instruction & 0x00000400) >> 10); + uint32_t imm4 = (instruction & 0x0000000F); + uint32_t imm3 = ((instruction & 0x70000000) >> 28); + uint32_t imm8 = ((instruction & 0x00FF0000) >> 16); + uint32_t imm16 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; + // combine with codeToDataDelta and kind nibble + uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); + uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta; + // construct new bits slices + uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; + uint32_t i_ = (newTargetValue & 0x08000000) >> 27; + uint32_t imm3_ = (newTargetValue & 0x07000000) >> 24; + uint32_t imm8_ = (newTargetValue & 0x00FF0000) >> 16; + // update instruction to match codeToDataDelta + uint32_t newInstruction = (instruction & 0x8F00FBF0) | imm4_ | (i_ << 10) | (imm3_ << 28) | (imm8_ << 16); + P::E::set32(*fixupLoc32, newInstruction); + } + break; + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + // used by arm movt (low nibble of kind is high 4-bits of paired movw) + { + instruction = P::E::get32(*fixupLoc32); + // extract 16-bit value from instruction + uint32_t imm4 = ((instruction & 0x000F0000) >> 16); + uint32_t imm12 = (instruction & 0x00000FFF); + uint32_t imm16 = (imm4 << 12) | imm12; + // combine with codeToDataDelta and kind nibble + uint32_t targetValue = (imm16 << 16) | ((kind & 0xF) << 12); + uint32_t newTargetValue = targetValue + (uint32_t)codeToDataDelta; + // construct new bits slices + uint32_t imm4_ = (newTargetValue & 0xF0000000) >> 28; + uint32_t imm12_ = (newTargetValue & 0x0FFF0000) >> 16; + // update instruction to match codeToDataDelta + uint32_t newInstruction = (instruction & 0xFFF0F000) | (imm4_ << 16) | imm12_; + P::E::set32(*fixupLoc32, newInstruction); + } + break; + case 3: // used for arm64 ADRP + instruction = P::E::get32(*fixupLoc32); + if ( (instruction & 0x9F000000) == 0x90000000 ) { + // codeToDataDelta is always a multiple of 4096, so only top 4 bits of lo16 will ever need adjusting + value64 = ((instruction & 0x60000000) >> 17) | ((instruction & 0x00FFFFE0) << 9); + value64 += codeToDataDelta; + instruction = (instruction & 0x9F00001F) | ((value64 << 17) & 0x60000000) | ((value64 >> 9) & 0x00FFFFE0); + P::E::set32(*fixupLoc32, instruction); + } + break; + default: + break; + } +} + +template +void Adjustor

::adjustCode() +{ + // find compressed info on how code needs to be updated + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()];; + + // This encoding only works if all data segments slide by the same amount + uint64_t codeToDataDelta = _segSlides[1] - _segSlides[0]; + + // compressed data is: [ [uleb128-delta]+ <0> ] + <0> + for (const uint8_t* p = infoStart; (*p != 0) && (p < infoEnd);) { + uint8_t kind = *p++; + uint64_t cacheOffset = _mappingInfo[0].dstCacheOffset; + while (uint64_t delta = read_uleb128(p, infoEnd)) { + cacheOffset += delta; + adjustInstruction(kind, cacheOffset, codeToDataDelta); + } + } +} + + +template +void Adjustor

::adjustExportsTrie(std::vector& newTrieBytes) +{ + // if no export info, nothing to adjust + if ( _dyldInfo->export_size() == 0 ) + return; + + // since export info addresses are offsets from mach_header, everything in __TEXT is fine + // only __DATA addresses need to be updated + const uint8_t* start = &_linkeditBias[_dyldInfo->export_off()]; + const uint8_t* end = &start[_dyldInfo->export_size()]; + std::vector originalExports; + if ( !ExportInfoTrie::parseTrie(start, end, originalExports) ) { + _diagnostics.error("malformed exports trie in %s", _installName); + return; + } + + std::vector newExports; + newExports.reserve(originalExports.size()); + uint64_t baseAddress = _segOrigStartAddresses[0]; + uint64_t baseAddressSlide = slideForOrigAddress(baseAddress); + for (auto& entry: originalExports) { + // remove symbols used by the static linker only + if ( (strncmp(entry.name.c_str(), "$ld$", 4) == 0) + || (strncmp(entry.name.c_str(), ".objc_class_name",16) == 0) + || (strncmp(entry.name.c_str(), ".objc_category_name",19) == 0) ) { + continue; + } + // adjust symbols in slid segments + if ( (entry.info.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE ) + entry.info.address += (slideForOrigAddress(entry.info.address + baseAddress) - baseAddressSlide); + newExports.push_back(entry); + } + + // rebuild export trie + newTrieBytes.reserve(_dyldInfo->export_size()); + + ExportInfoTrie(newExports).emit(newTrieBytes); + // align + while ( (newTrieBytes.size() % sizeof(pint_t)) != 0 ) + newTrieBytes.push_back(0); +} + + +} // anonymous namespace + + +void adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector& mappingInfo, std::vector& pointersForASLR, Diagnostics& diag) +{ + if ( is64 ) { + Adjustor> adjustor64(cache, (macho_header>*)mhInCache, mappingInfo, diag); + adjustor64.adjustImageForNewSegmentLocations(pointersForASLR); + } + else { + Adjustor> adjustor32(cache, (macho_header>*)mhInCache, mappingInfo, diag); + adjustor32.adjustImageForNewSegmentLocations(pointersForASLR); + } +} + + + + + diff --git a/dyld/dyld3/shared-cache/BuilderUtils.h b/dyld/dyld3/shared-cache/BuilderUtils.h new file mode 100644 index 0000000..713d0bd --- /dev/null +++ b/dyld/dyld3/shared-cache/BuilderUtils.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 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 BuilderUtils_h +#define BuilderUtils_h + +dispatch_group_t buildGroup(); +void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot); +bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, bool skipWrites, bool agileChooseSHA256CdHash); + +#endif /* BuilderUtils_h */ diff --git a/dyld/dyld3/shared-cache/BuilderUtils.mm b/dyld/dyld3/shared-cache/BuilderUtils.mm new file mode 100644 index 0000000..ef2a675 --- /dev/null +++ b/dyld/dyld3/shared-cache/BuilderUtils.mm @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include // std::setfill, std::setw +#include +#include +#include + +#include +#include +#include +#include + +#include "Manifest.h" +#include "Diagnostics.h" +#include "FileUtils.h" + +#include "BuilderUtils.h" + +static dispatch_queue_t write_queue = dispatch_queue_create("com.apple.dyld.cache-builder.write", DISPATCH_QUEUE_CONCURRENT); +static dispatch_group_t build_group = dispatch_group_create(); + +dispatch_group_t buildGroup() { + return build_group; +} + +void insertFileInBom(const std::string& path, BOMBom bom) +{ + std::vector components; + std::vector processed_components; + std::stringstream ss(path); + std::string item; + + while (std::getline(ss, item, '/')) { + if (!item.empty()) { + components.push_back(item); + } + } + + std::string partialPath = "."; + std::string lastComponent = components.back(); + components.pop_back(); + BOMFSObject fso = BOMFSObjectNew(BOMDirectoryType); + BOMFSObjectSetFlags(fso, B_PATHONLY); + BOMFSObjectSetPathName(fso, ".", true); + BOMFSObjectSetShortName(fso, ".", true); + (void)BOMBomInsertFSObject(bom, fso, false); + BOMFSObjectFree(fso); + + for (const auto& component : components) { + partialPath = partialPath + "/" + component; + fso = BOMFSObjectNew(BOMDirectoryType); + BOMFSObjectSetFlags(fso, B_PATHONLY); + BOMFSObjectSetPathName(fso, partialPath.c_str(), true); + BOMFSObjectSetShortName(fso, component.c_str(), true); + (void)BOMBomInsertFSObject(bom, fso, false); + BOMFSObjectFree(fso); + } + + partialPath = partialPath + "/" + lastComponent; + fso = BOMFSObjectNew(BOMFileType); + BOMFSObjectSetFlags(fso, B_PATHONLY); + BOMFSObjectSetPathName(fso, partialPath.c_str(), true); + BOMFSObjectSetShortName(fso, lastComponent.c_str(), true); + (void)BOMBomInsertFSObject(bom, fso, false); + BOMFSObjectFree(fso); +} + +void makeBoms(dyld3::Manifest& manifest, const std::string& masterDstRoot) +{ + mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755); + + manifest.forEachConfiguration([&manifest, &masterDstRoot](const std::string& configName) { + auto config = manifest.configuration(configName); + std::vector prodBomPaths; + std::vector devBomPaths; + + std::string runtimePath = "/System/Library/Caches/com.apple.dyld/"; + if (manifest.platform() == dyld3::Platform::macOS) { + runtimePath = "/private/var/db/dyld/"; + } + + for (auto& arch : config.architectures) { + std::string cachePath = "dyld_shared_cache_" + arch.first; + prodBomPaths.push_back(cachePath); + if (manifest.platform() != dyld3::Platform::macOS) { + cachePath += ".development"; + } + devBomPaths.push_back(cachePath); + char buffer[MAXPATHLEN]; + sprintf(buffer, "%s/Boms/%s.prod.bom", masterDstRoot.c_str(), configName.c_str()); + BOMBom bom = BOMBomNew(buffer); + for (auto& path : prodBomPaths) { + insertFileInBom(runtimePath + path, bom); + } + BOMBomFree(bom); + + sprintf(buffer, "%s/Boms/%s.dev.bom", masterDstRoot.c_str(), configName.c_str()); + bom = BOMBomNew(buffer); + for (auto& path : devBomPaths) { + insertFileInBom(runtimePath + path, bom); + } + BOMBomFree(bom); + + sprintf(buffer, "%s/Boms/%s.full.bom", masterDstRoot.c_str(), configName.c_str()); + bom = BOMBomNew(buffer); + for (auto& path : prodBomPaths) { + insertFileInBom(runtimePath + path, bom); + } + for (auto& path : devBomPaths) { + insertFileInBom(runtimePath + path, bom); + } + BOMBomFree(bom); + } + }); +} + +bool build(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& masterDstRoot, bool dedupe, bool verbose, + bool skipWrites, bool agileChooseSHA256CdHash) +{ + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_queue_t warningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", DISPATCH_QUEUE_SERIAL); + std::vector> dedupedCacheSets; + if (dedupe) { + manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) { + auto config = manifest.configuration(configName); + bool dupeFound = false; + + for (auto& cacheSet : dedupedCacheSets) { + if (config == manifest.configuration(*cacheSet.begin())) { + cacheSet.insert(configName); + dupeFound = true; + break; + } + } + + if (!dupeFound) { + std::set temp; + temp.insert(configName); + dedupedCacheSets.push_back(temp); + } + }); + } else { + manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) { + std::set temp; + temp.insert(configName); + dedupedCacheSets.push_back(temp); + }); + } + + std::vector buildQueue; + + for (auto& cacheSet : dedupedCacheSets) { + //FIXME we may want to consider moving to hashes of UUID sets + std::string setName; + + for (auto& archName : cacheSet) { + if (!setName.empty()) { + setName += "|"; + } + setName += archName; + } + + std::stringstream fileNameStream; + std::array digest = { 0 }; + CC_SHA1(setName.c_str(), (unsigned int)setName.length(), &digest[0]); + + fileNameStream << std::hex << std::uppercase << std::setfill('0'); + for (int c : digest) { + fileNameStream << std::setw(2) << c; + } + + std::string fileName(fileNameStream.str()); + + if (dedupe) { + for (auto& config : cacheSet) { + if (!skipWrites) { + int err = symlink(("DedupedConfigs/" + fileName).c_str(), (masterDstRoot + "/" + config).c_str()); + if (err) { + diags.warning("Could not create symlink '%s' -> 'DedupedConfigs/%s' (%d)", config.c_str(), fileName.c_str(), err); + } + } + } + } + + manifest.configuration(*cacheSet.begin()).forEachArchitecture([&masterDstRoot, &dedupe, &fileName, &setName, &manifest, &buildQueue, &cacheSet, verbose](const std::string& arch) { + std::string configPath; + std::string runtimePath = "/System/Library/Caches/com.apple.dyld/"; + if (manifest.platform() == dyld3::Platform::macOS) { + runtimePath = "/private/var/db/dyld/"; + } + if (dedupe) { + configPath = masterDstRoot + "/DedupedConfigs/" + fileName + runtimePath; + } else { + configPath = masterDstRoot + runtimePath; + } + + if (manifest.platform() == dyld3::Platform::macOS) { + buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch, verbose)); + } else { + buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch, verbose)); + buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch, verbose)); + } + }); + } + + __block bool cacheBuildFailure = false; + __block std::set warnings; + __block std::set errors; + + dispatch_sync(warningQueue, ^{ + auto manifestWarnings = diags.warnings(); + warnings.insert(manifestWarnings.begin(), manifestWarnings.end()); + }); + + dispatch_apply(buildQueue.size(), queue, ^(size_t index) { + auto queueEntry = buildQueue[index]; + pthread_setname_np(queueEntry.options.loggingPrefix.substr(0, MAXTHREADNAMESIZE - 1).c_str()); + + DyldSharedCache::CreateResults results; + while (1) { + results = DyldSharedCache::create(queueEntry.options, queueEntry.dylibsForCache, queueEntry.otherDylibsAndBundles, queueEntry.mainExecutables); + if (!results.overflowed) + break; + auto evicted = manifest.removeLargestLeafDylib(queueEntry.configNames, queueEntry.options.archName); + if (evicted.empty()) + break; + queueEntry = manifest.makeQueueEntry(queueEntry.outputPath, queueEntry.configNames, queueEntry.options.archName, queueEntry.options.optimizeStubs, queueEntry.options.loggingPrefix, queueEntry.options.verbose); + dispatch_sync(warningQueue, ^{ + warnings.insert("[WARNING] CACHE OVERFLOW: " + queueEntry.options.loggingPrefix + " evicted dylib: " + evicted); + }); + } + dispatch_sync(warningQueue, ^{ + warnings.insert(results.warnings.begin(), results.warnings.end()); + bool chooseSecondCdHash = agileChooseSHA256CdHash; + if (agileChooseSHA256CdHash && !results.agileSignature) { + // Ignore this option for caches that are not signed agile (which is the majority). + chooseSecondCdHash = false; + } + for (const auto& configName : queueEntry.configNames) { + manifest.configuration(configName).architecture(queueEntry.options.archName).results.warnings = results.warnings; + if (queueEntry.options.optimizeStubs) { + manifest.configuration(configName).architecture(queueEntry.options.archName) + .results.developmentCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst; + } else { + manifest.configuration(configName).architecture(queueEntry.options.archName) + .results.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst; + } + } + }); + if (!results.errorMessage.empty()) { + fprintf(stderr, "[%s] ERROR: %s\n", queueEntry.options.loggingPrefix.c_str(), results.errorMessage.c_str()); + } else if (!skipWrites) { + dispatch_sync(write_queue, ^{ + // save new cache file to disk and write new .map file + assert(results.cacheContent != nullptr); + mkpath_np(dirPath(queueEntry.outputPath).c_str(), 0755); + if (!safeSave(results.cacheContent, results.cacheLength, queueEntry.outputPath)) { + cacheBuildFailure = true; + fprintf(stderr, "[%s] ERROR: Could not write cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str()); + } else { + fprintf(stderr, "[%s] Wrote cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str()); + std::string mapStr = results.cacheContent->mapFile(); + std::string outFileMap = queueEntry.outputPath + ".map"; + safeSave(mapStr.c_str(), mapStr.size(), outFileMap); + } + // free created cache buffer + vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength); + }); + } else { + fprintf(stderr, "[%s] Skipped writing cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str()); + vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength); + } + }); + + // print any warnings + for (const std::string& warn : warnings) { + fprintf(stderr, "[WARNING] %s\n", warn.c_str()); + } + + int err = sync_volume_np(masterDstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT); + if (err) { + fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err)); + } + + return !cacheBuildFailure; +} diff --git a/dyld/dyld3/shared-cache/CacheBuilder.cpp b/dyld/dyld3/shared-cache/CacheBuilder.cpp new file mode 100644 index 0000000..b4fe35a --- /dev/null +++ b/dyld/dyld3/shared-cache/CacheBuilder.cpp @@ -0,0 +1,1784 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "MachOParser.h" +#include "CodeSigningTypes.h" +#include "DyldSharedCache.h" +#include "CacheBuilder.h" +#include "FileAbstraction.hpp" +#include "LaunchCacheWriter.h" +#include "Trie.hpp" +#include "Diagnostics.h" +#include "ImageProxy.h" + +#if __has_include("dyld_cache_config.h") + #include "dyld_cache_config.h" +#else + #define ARM_SHARED_REGION_START 0x1A000000ULL + #define ARM_SHARED_REGION_SIZE 0x26000000ULL + #define ARM64_SHARED_REGION_START 0x180000000ULL + #define ARM64_SHARED_REGION_SIZE 0x40000000ULL +#endif + +const CacheBuilder::ArchLayout CacheBuilder::_s_archLayout[] = { + { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x40000000, 0xFFFF000000000000, "x86_64", 0, 0, 0, 12, true, true }, + { 0x7FFF20000000ULL, 0xEFE00000ULL, 0x40000000, 0xFFFF000000000000, "x86_64h", 0, 0, 0, 12, true, true }, + { SHARED_REGION_BASE_I386, SHARED_REGION_SIZE_I386, 0x00200000, 0x0, "i386", 0, 0, 0, 12, false, false }, + { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, 0x00FFFF0000000000, "arm64", 0x0000C000, 0x00100000, 0x07F00000, 14, false, true }, + { ARM64_SHARED_REGION_START, ARM64_SHARED_REGION_SIZE, 0x02000000, 0x00FFFF0000000000, "arm64e", 0x0000C000, 0x00100000, 0x07F00000, 14, false, true }, + { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x02000000, 0xE0000000, "armv7s", 0, 0, 0, 14, false, false }, + { ARM_SHARED_REGION_START, ARM_SHARED_REGION_SIZE, 0x00400000, 0xE0000000, "armv7k", 0, 0, 0, 14, false, false }, + { 0x40000000, 0x40000000, 0x02000000, 0x0, "sim-x86", 0, 0, 0, 14, false, false } +}; + + +// These are dylibs that may be interposed, so stubs calling into them should never be bypassed +const char* const CacheBuilder::_s_neverStubEliminate[] = { + "/usr/lib/system/libdispatch.dylib", + nullptr +}; + + +CacheBuilder::CacheBuilder(const DyldSharedCache::CreateOptions& options) + : _options(options) + , _buffer(nullptr) + , _diagnostics(options.loggingPrefix, options.verbose) + , _archLayout(nullptr) + , _aliasCount(0) + , _slideInfoFileOffset(0) + , _slideInfoBufferSizeAllocated(0) + , _allocatedBufferSize(0) + , _currentFileSize(0) + , _vmSize(0) + , _branchPoolsLinkEditStartAddr(0) +{ + + std::string targetArch = options.archName; + if ( options.forSimulator && (options.archName == "i386") ) + targetArch = "sim-x86"; + + for (const ArchLayout& layout : _s_archLayout) { + if ( layout.archName == targetArch ) { + _archLayout = &layout; + break; + } + } +} + + +std::string CacheBuilder::errorMessage() +{ + return _diagnostics.errorMessage(); +} + +const std::set CacheBuilder::warnings() +{ + return _diagnostics.warnings(); +} + +void CacheBuilder::deleteBuffer() +{ + vm_deallocate(mach_task_self(), (vm_address_t)_buffer, _allocatedBufferSize); + _buffer = nullptr; + _allocatedBufferSize = 0; +} + +std::vector +CacheBuilder::makeSortedDylibs(const std::vector& dylibs, const std::unordered_map sortOrder) +{ + std::vector sortedDylibs = dylibs; + + std::sort(sortedDylibs.begin(), sortedDylibs.end(), [&](const DyldSharedCache::MappedMachO& a, const DyldSharedCache::MappedMachO& b) { + const auto& orderA = sortOrder.find(a.runtimePath); + const auto& orderB = sortOrder.find(b.runtimePath); + bool foundA = (orderA != sortOrder.end()); + bool foundB = (orderB != sortOrder.end()); + + // Order all __DATA_DIRTY segments specified in the order file first, in + // the order specified in the file, followed by any other __DATA_DIRTY + // segments in lexicographic order. + if ( foundA && foundB ) + return orderA->second < orderB->second; + else if ( foundA ) + return true; + else if ( foundB ) + return false; + else + return a.runtimePath < b.runtimePath; + }); + + return sortedDylibs; +} + + +inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) +{ + return (uint32_t)(abstime/1000/1000); +} + +struct DylibAndSize +{ + const char* installName; + uint64_t size; +}; + +bool CacheBuilder::cacheOverflow(const dyld_cache_mapping_info regions[3]) +{ + if ( _archLayout->sharedRegionsAreDiscontiguous ) { + // for macOS x86_64 cache, need to check each region for overflow + return ( (regions[0].size > 0x60000000) || (regions[1].size > 0x40000000) || (regions[2].size > 0x3FE00000) ); + } + else { + return (_vmSize > _archLayout->sharedMemorySize); + } +} + +bool CacheBuilder::build(const std::vector& dylibs, + const std::vector& otherOsDylibsInput, + const std::vector& osExecutables) +{ + // error out instead of crash if cache has no dylibs + // FIXME: plist should specify required vs optional dylibs + if ( dylibs.size() < 30 ) { + _diagnostics.error("missing required minimum set of dylibs"); + return false; + } + uint64_t t1 = mach_absolute_time(); + + + // make copy of dylib list and sort + std::vector sortedDylibs = makeSortedDylibs(dylibs, _options.dylibOrdering); + std::vector otherOsDylibs = otherOsDylibsInput; + + // assign addresses for each segment of each dylib in new cache + dyld_cache_mapping_info regions[3]; + SegmentMapping segmentMapping = assignSegmentAddresses(sortedDylibs, regions); + if ( cacheOverflow(regions) ) { + if ( !_options.evictLeafDylibsOnOverflow ) { + _diagnostics.error("cache overflow: %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024); + return false; + } + // find all leaf (not referenced by anything else in cache) dylibs + + // build count of how many references there are to each dylib + __block std::map referenceCount; + for (const DyldSharedCache::MappedMachO& dylib : sortedDylibs) { + dyld3::MachOParser parser(dylib.mh); + parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + referenceCount[loadPath] += 1; + }); + } + + // find all dylibs not referenced + std::vector unreferencedDylibs; + for (const DyldSharedCache::MappedMachO& dylib : sortedDylibs) { + dyld3::MachOParser parser(dylib.mh); + const char* installName = parser.installName(); + if ( referenceCount.count(installName) == 0 ) { + // conservative: sum up all segments except LINKEDIT + __block uint64_t segsSize = 0; + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stop) { + if ( strcmp(segName, "__LINKEDIT") != 0 ) + segsSize += vmSize; + }); + unreferencedDylibs.push_back({installName, segsSize}); + } + } + // sort leaf dylibs by size + std::sort(unreferencedDylibs.begin(), unreferencedDylibs.end(), [&](const DylibAndSize& a, const DylibAndSize& b) { + return ( a.size > b.size ); + }); + + // build set of dylibs that if removed will allow cache to build + uint64_t reductionTarget = _vmSize - _archLayout->sharedMemorySize; + std::set toRemove; + for (DylibAndSize& dylib : unreferencedDylibs) { + if ( _options.verbose ) + _diagnostics.warning("to prevent cache overflow, not caching %s", dylib.installName); + toRemove.insert(dylib.installName); + if ( dylib.size > reductionTarget ) + break; + reductionTarget -= dylib.size; + } + // transfer overflow dylibs from cached vector to other vector + for (const std::string& installName : toRemove) { + for (std::vector::iterator it=sortedDylibs.begin(); it != sortedDylibs.end(); ++it) { + dyld3::MachOParser parser(it->mh); + if ( installName == parser.installName() ) { + otherOsDylibs.push_back(*it); + sortedDylibs.erase(it); + break; + } + } + } + // re-layout cache + segmentMapping = assignSegmentAddresses(sortedDylibs, regions); + if ( cacheOverflow(regions) ) { + _diagnostics.error("cache overflow, tried evicting %ld leaf daylibs, but still too big: %lluMB (max %lluMB)", + toRemove.size(), _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024); + return false; + } + } + + // allocate buffer for new cache + _allocatedBufferSize = std::max(_currentFileSize, (uint64_t)0x100000)*1.1; // add 10% to allocation to support large closures + if ( vm_allocate(mach_task_self(), (vm_address_t*)&_buffer, _allocatedBufferSize, VM_FLAGS_ANYWHERE) != 0 ) { + _diagnostics.error("could not allocate buffer"); + return false; + } + _currentFileSize = _allocatedBufferSize; + + // write unoptimized cache + writeCacheHeader(regions, sortedDylibs, segmentMapping); + copyRawSegments(sortedDylibs, segmentMapping); + adjustAllImagesForNewSegmentLocations(sortedDylibs, segmentMapping); + if ( _diagnostics.hasError() ) + return false; + + bindAllImagesInCacheFile(regions); + if ( _diagnostics.hasError() ) + return false; + + // optimize ObjC + if ( _options.optimizeObjC ) + optimizeObjC(_buffer, _archLayout->is64, _options.optimizeStubs, _pointersForASLR, _diagnostics); + if ( _diagnostics.hasError() ) + return false; + + // optimize away stubs + std::vector branchPoolOffsets; + uint64_t cacheStartAddress = _archLayout->sharedMemoryStart; + if ( _options.optimizeStubs ) { + std::vector branchPoolStartAddrs; + const uint64_t* p = (uint64_t*)((uint8_t*)_buffer + _buffer->header.branchPoolsOffset); + for (int i=0; i < _buffer->header.branchPoolsCount; ++i) { + uint64_t poolAddr = p[i]; + branchPoolStartAddrs.push_back(poolAddr); + branchPoolOffsets.push_back(poolAddr - cacheStartAddress); + } + bypassStubs(_buffer, branchPoolStartAddrs, _s_neverStubEliminate, _diagnostics); + } + uint64_t t2 = mach_absolute_time(); + + // FIPS seal corecrypto, This must be done after stub elimination (so that + // __TEXT,__text is not changed after sealing), but before LINKEDIT + // optimization (so that we still have access to local symbols) + fipsSign(); + + // merge and compact LINKEDIT segments + dyld_cache_local_symbols_info* localsInfo = nullptr; + if ( dylibs.size() == 0 ) + _currentFileSize = 0x1000; + else + _currentFileSize = optimizeLinkedit(_buffer, _archLayout->is64, _options.excludeLocalSymbols, _options.optimizeStubs, branchPoolOffsets, _diagnostics, &localsInfo); + + uint64_t t3 = mach_absolute_time(); + + // add ImageGroup for all dylibs in cache + __block std::vector cachedDylibs; + std::unordered_map mapIntoSortedDylibs; + for (const DyldSharedCache::MappedMachO& entry : sortedDylibs) { + mapIntoSortedDylibs[entry.runtimePath] = &entry; + } + _buffer->forEachImage(^(const mach_header* mh, const char* installName) { + auto pos = mapIntoSortedDylibs.find(installName); + if ( pos != mapIntoSortedDylibs.end() ) { + DyldSharedCache::MappedMachO newEntry = *(pos->second); + newEntry.mh = mh; + cachedDylibs.push_back(newEntry); + } + else { + bool found = false; + for (const std::string& prefix : _options.pathPrefixes) { + std::string fullPath = prefix + installName; + char resolvedPath[PATH_MAX]; + if ( realpath(fullPath.c_str(), resolvedPath) != nullptr ) { + std::string resolvedUnPrefixed = &resolvedPath[prefix.size()]; + pos = mapIntoSortedDylibs.find(resolvedUnPrefixed); + if ( pos != mapIntoSortedDylibs.end() ) { + DyldSharedCache::MappedMachO newEntry = *(pos->second); + newEntry.mh = mh; + cachedDylibs.push_back(newEntry); + found = true; + } + } + } + if ( !found ) + fprintf(stderr, "missing mapping for %s\n", installName); + } + }); + dyld3::DyldCacheParser dyldCacheParser(_buffer, true); + dyld3::ImageProxyGroup* dylibGroup = dyld3::ImageProxyGroup::makeDyldCacheDylibsGroup(_diagnostics, dyldCacheParser, cachedDylibs, + _options.pathPrefixes, _patchTable, + _options.optimizeStubs, !_options.dylibsRemovedDuringMastering); + if ( _diagnostics.hasError() ) + return false; + addCachedDylibsImageGroup(dylibGroup); + if ( _diagnostics.hasError() ) + return false; + + uint64_t t4 = mach_absolute_time(); + + // add ImageGroup for other OS dylibs and bundles + dyld3::ImageProxyGroup* otherGroup = dyld3::ImageProxyGroup::makeOtherOsGroup(_diagnostics, dyldCacheParser, dylibGroup, otherOsDylibs, + _options.inodesAreSameAsRuntime, _options.pathPrefixes); + if ( _diagnostics.hasError() ) + return false; + addCachedOtherDylibsImageGroup(otherGroup); + if ( _diagnostics.hasError() ) + return false; + + uint64_t t5 = mach_absolute_time(); + + // compute and add launch closures + std::map closures; + for (const DyldSharedCache::MappedMachO& mainProg : osExecutables) { + Diagnostics clsDiag; + const dyld3::launch_cache::binary_format::Closure* cls = dyld3::ImageProxyGroup::makeClosure(clsDiag, dyldCacheParser, dylibGroup, otherGroup, mainProg, + _options.inodesAreSameAsRuntime, _options.pathPrefixes); + if ( clsDiag.hasError() ) { + // if closure cannot be built, silently skip it, unless in verbose mode + if ( _options.verbose ) { + _diagnostics.warning("building closure for '%s': %s", mainProg.runtimePath.c_str(), clsDiag.errorMessage().c_str()); + for (const std::string& warn : clsDiag.warnings() ) + _diagnostics.warning("%s", warn.c_str()); + } + } + else { + closures[mainProg.runtimePath] = cls; + } + } + addClosures(closures); + if ( _diagnostics.hasError() ) + return false; + + uint64_t t6 = mach_absolute_time(); + + // fill in slide info at start of region[2] + // do this last because it modifies pointers in DATA segments + if ( _options.cacheSupportsASLR ) { + if ( _archLayout->is64 ) + writeSlideInfoV2>(); + else + writeSlideInfoV2>(); + } + + uint64_t t7 = mach_absolute_time(); + + // update last region size + dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset); + _currentFileSize = align(_currentFileSize, _archLayout->sharedRegionAlignP2); + mappings[2].size = _currentFileSize - mappings[2].fileOffset; + + // record cache bounds + _buffer->header.sharedRegionStart = _archLayout->sharedMemoryStart; + _buffer->header.sharedRegionSize = _archLayout->sharedMemorySize; + if ( _archLayout->sharedRegionsAreDiscontiguous ) { + // special case x86_64 which has three non-contiguous chunks each in their own 1GB regions + uint64_t maxSlide0 = 0x60000000 - mappings[0].size; // TEXT region has 1.5GB region + uint64_t maxSlide1 = 0x40000000 - mappings[1].size; + uint64_t maxSlide2 = 0x3FE00000 - mappings[2].size; + _buffer->header.maxSlide = std::min(std::min(maxSlide0, maxSlide1), maxSlide2); + } + else { + _buffer->header.maxSlide = (_archLayout->sharedMemoryStart + _archLayout->sharedMemorySize) - (mappings[2].address + mappings[2].size); + } + + // append "unmapped" local symbols region + if ( _options.excludeLocalSymbols ) { + size_t localsInfoSize = align(localsInfo->stringsOffset + localsInfo->stringsSize, _archLayout->sharedRegionAlignP2); + if ( _currentFileSize + localsInfoSize > _allocatedBufferSize ) { + _diagnostics.warning("local symbols omitted because cache buffer overflow"); + } + else { + memcpy((char*)_buffer+_currentFileSize, localsInfo, localsInfoSize); + _buffer->header.localSymbolsOffset = _currentFileSize; + _buffer->header.localSymbolsSize = localsInfoSize; + _currentFileSize += localsInfoSize; + } + free((void*)localsInfo); + } + + recomputeCacheUUID(); + + // Calculate the VMSize of the resulting cache + __block uint64_t endAddr = 0; + _buffer->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if (vmAddr+size > endAddr) + endAddr = vmAddr+size; + }); + _vmSize = endAddr - cacheStartAddress; + + // last sanity check on size + if ( _vmSize > _archLayout->sharedMemorySize ) { + _diagnostics.error("cache overflow after optimizations. %lluMB (max %lluMB)", _vmSize / 1024 / 1024, (_archLayout->sharedMemorySize) / 1024 / 1024); + return true; + } + + // codesignature is part of file, but is not mapped + codeSign(); + if ( _diagnostics.hasError() ) + return false; + + uint64_t t8 = mach_absolute_time(); + + if ( _options.verbose ) { + fprintf(stderr, "time to copy and bind cached dylibs: %ums\n", absolutetime_to_milliseconds(t2-t1)); + fprintf(stderr, "time to optimize LINKEDITs: %ums\n", absolutetime_to_milliseconds(t3-t2)); + fprintf(stderr, "time to build ImageGroup of %lu cached dylibs: %ums\n", sortedDylibs.size(), absolutetime_to_milliseconds(t4-t3)); + fprintf(stderr, "time to build ImageGroup of %lu other dylibs: %ums\n", otherOsDylibs.size(), absolutetime_to_milliseconds(t5-t4)); + fprintf(stderr, "time to build %lu closures: %ums\n", osExecutables.size(), absolutetime_to_milliseconds(t6-t5)); + fprintf(stderr, "time to compute slide info: %ums\n", absolutetime_to_milliseconds(t7-t6)); + fprintf(stderr, "time to compute UUID and codesign cache file: %ums\n", absolutetime_to_milliseconds(t8-t7)); + } + + // trim over allocated buffer + if ( _allocatedBufferSize > _currentFileSize ) { + uint8_t* startOfUnused = (uint8_t*)_buffer+_currentFileSize; + size_t unusedLen = _allocatedBufferSize-_currentFileSize; + vm_deallocate(mach_task_self(), (vm_address_t)startOfUnused, unusedLen); + _allocatedBufferSize = _currentFileSize; + } + + return false; +} + + +void CacheBuilder::writeCacheHeader(const dyld_cache_mapping_info regions[3], const std::vector& dylibs, const SegmentMapping& segmentMappings) +{ + // "dyld_v1" + spaces + archName(), with enough spaces to pad to 15 bytes + std::string magic = "dyld_v1"; + magic.append(15 - magic.length() - _options.archName.length(), ' '); + magic.append(_options.archName); + assert(magic.length() == 15); + + // fill in header + memcpy(_buffer->header.magic, magic.c_str(), 16); + _buffer->header.mappingOffset = sizeof(dyld_cache_header); + _buffer->header.mappingCount = 3; + _buffer->header.imagesOffset = (uint32_t)(_buffer->header.mappingOffset + 3*sizeof(dyld_cache_mapping_info) + sizeof(uint64_t)*_branchPoolStarts.size()); + _buffer->header.imagesCount = (uint32_t)dylibs.size() + _aliasCount; + _buffer->header.dyldBaseAddress = 0; + _buffer->header.codeSignatureOffset= 0; + _buffer->header.codeSignatureSize = 0; + _buffer->header.slideInfoOffset = _slideInfoFileOffset; + _buffer->header.slideInfoSize = _slideInfoBufferSizeAllocated; + _buffer->header.localSymbolsOffset = 0; + _buffer->header.localSymbolsSize = 0; + _buffer->header.cacheType = _options.optimizeStubs ? kDyldSharedCacheTypeProduction : kDyldSharedCacheTypeDevelopment; + _buffer->header.accelerateInfoAddr = 0; + _buffer->header.accelerateInfoSize = 0; + bzero(_buffer->header.uuid, 16); // overwritten later by recomputeCacheUUID() + _buffer->header.branchPoolsOffset = _buffer->header.mappingOffset + 3*sizeof(dyld_cache_mapping_info); + _buffer->header.branchPoolsCount = (uint32_t)_branchPoolStarts.size(); + _buffer->header.imagesTextOffset = _buffer->header.imagesOffset + sizeof(dyld_cache_image_info)*_buffer->header.imagesCount; + _buffer->header.imagesTextCount = dylibs.size(); + _buffer->header.platform = (uint8_t)_options.platform; + _buffer->header.formatVersion = dyld3::launch_cache::binary_format::kFormatVersion; + _buffer->header.dylibsExpectedOnDisk = !_options.dylibsRemovedDuringMastering; + _buffer->header.simulator = _options.forSimulator; + + // fill in mappings + dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset); + mappings[0] = regions[0]; + mappings[1] = regions[1]; + mappings[2] = regions[2]; + + // fill in branch pool addresses + uint64_t* p = (uint64_t*)((char*)_buffer + _buffer->header.branchPoolsOffset); + for (uint64_t pool : _branchPoolStarts) { + *p++ = pool; + } + + // fill in image table + dyld_cache_image_info* images = (dyld_cache_image_info*)((char*)_buffer + _buffer->header.imagesOffset); + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + const std::vector& segs = segmentMappings.at(dylib.mh); + dyld3::MachOParser parser(dylib.mh); + const char* installName = parser.installName(); + images->address = segs[0].dstCacheAddress; + if ( _options.dylibsRemovedDuringMastering ) { + images->modTime = 0; + images->inode = pathHash(installName); + } + else { + images->modTime = dylib.modTime; + images->inode = dylib.inode; + } + uint32_t installNameOffsetInTEXT = (uint32_t)(installName - (char*)dylib.mh); + images->pathFileOffset = (uint32_t)segs[0].dstCacheOffset + installNameOffsetInTEXT; + ++images; + } + // append aliases image records and strings +/* + for (auto &dylib : _dylibs) { + if (!dylib->installNameAliases.empty()) { + for (const std::string& alias : dylib->installNameAliases) { + images->set_address(_segmentMap[dylib][0].address); + if (_manifest.platform() == "osx") { + images->modTime = dylib->lastModTime; + images->inode = dylib->inode; + } + else { + images->modTime = 0; + images->inode = pathHash(alias.c_str()); + } + images->pathFileOffset = offset; + //fprintf(stderr, "adding alias %s for %s\n", alias.c_str(), dylib->installName.c_str()); + ::strcpy((char*)&_buffer[offset], alias.c_str()); + offset += alias.size() + 1; + ++images; + } + } + } +*/ + // calculate start of text image array and trailing string pool + dyld_cache_image_text_info* textImages = (dyld_cache_image_text_info*)((char*)_buffer + _buffer->header.imagesTextOffset); + uint32_t stringOffset = (uint32_t)(_buffer->header.imagesTextOffset + sizeof(dyld_cache_image_text_info) * dylibs.size()); + + // write text image array and image names pool at same time + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + const std::vector& segs = segmentMappings.at(dylib.mh); + dyld3::MachOParser parser(dylib.mh); + parser.getUuid(textImages->uuid); + textImages->loadAddress = segs[0].dstCacheAddress; + textImages->textSegmentSize = (uint32_t)segs[0].dstCacheSegmentSize; + textImages->pathOffset = stringOffset; + const char* installName = parser.installName(); + ::strcpy((char*)_buffer + stringOffset, installName); + stringOffset += (uint32_t)strlen(installName)+1; + ++textImages; + } + + // make sure header did not overflow into first mapped image + const dyld_cache_image_info* firstImage = (dyld_cache_image_info*)((char*)_buffer + _buffer->header.imagesOffset); + assert(stringOffset <= (firstImage->address - mappings[0].address)); +} + + +void CacheBuilder::copyRawSegments(const std::vector& dylibs, const SegmentMapping& mapping) +{ + uint8_t* cacheBytes = (uint8_t*)_buffer; + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + auto pos = mapping.find(dylib.mh); + assert(pos != mapping.end()); + for (const SegmentMappingInfo& info : pos->second) { + //fprintf(stderr, "copy %s segment %s (0x%08X bytes) from %p to %p (logical addr 0x%llX) for %s\n", _options.archName.c_str(), info.segName, info.copySegmentSize, info.srcSegment, &cacheBytes[info.dstCacheOffset], info.dstCacheAddress, dylib.runtimePath.c_str()); + ::memcpy(&cacheBytes[info.dstCacheOffset], info.srcSegment, info.copySegmentSize); + } + } +} + +void CacheBuilder::adjustAllImagesForNewSegmentLocations(const std::vector& dylibs, const SegmentMapping& mapping) +{ + uint8_t* cacheBytes = (uint8_t*)_buffer; + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + auto pos = mapping.find(dylib.mh); + assert(pos != mapping.end()); + mach_header* mhInCache = (mach_header*)&cacheBytes[pos->second[0].dstCacheOffset]; + adjustDylibSegments(_buffer, _archLayout->is64, mhInCache, pos->second, _pointersForASLR, _diagnostics); + if ( _diagnostics.hasError() ) + break; + } +} + +struct Counts { + unsigned long lazyCount = 0; + unsigned long nonLazyCount = 0; +}; + +void CacheBuilder::bindAllImagesInCacheFile(const dyld_cache_mapping_info regions[3]) +{ + const bool log = false; + __block std::unordered_map useCounts; + + // build map of install names to mach_headers + __block std::unordered_map installNameToMH; + __block std::vector dylibMHs; + _buffer->forEachImage(^(const mach_header* mh, const char* installName) { + installNameToMH[installName] = mh; + dylibMHs.push_back(mh); + }); + + __block Diagnostics parsingDiag; + bool (^dylibFinder)(uint32_t, const char*, void* , const mach_header**, void**) = ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) { + auto pos = installNameToMH.find(depLoadPath); + if ( pos != installNameToMH.end() ) { + *foundMH = pos->second; + *foundExtra = nullptr; + return true; + } + parsingDiag.error("dependent dylib %s not found", depLoadPath); + return false; + }; + if ( parsingDiag.hasError() ) { + _diagnostics.error("%s", parsingDiag.errorMessage().c_str()); + return; + } + + // bind every dylib in cache + for (const mach_header* mh : dylibMHs) { + dyld3::MachOParser parser(mh, true); + bool is64 = parser.is64(); + const char* depPaths[256]; + const char** depPathsArray = depPaths; + __block int depIndex = 1; + parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + depPathsArray[depIndex++] = loadPath; + }); + uint8_t* segCacheStarts[10]; + uint64_t segCacheAddrs[10]; + uint8_t** segCacheStartsArray = segCacheStarts; + uint64_t* segCacheAddrsArray = segCacheAddrs; + __block int segIndex = 0; + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + segCacheStartsArray[segIndex] = (segIndex == 0) ? (uint8_t*)mh : (uint8_t*)_buffer + fileOffset; + segCacheAddrsArray[segIndex] = vmAddr; + ++segIndex; + }); + __block Diagnostics bindingDiag; + parser.forEachBind(bindingDiag, ^(uint32_t dataSegIndex, uint64_t dataSegOffset, uint8_t type, int libOrdinal, uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop) { + if ( log ) { + if ( lazy ) + useCounts[symbolName].lazyCount += 1; + else + useCounts[symbolName].nonLazyCount += 1; + } + const mach_header* targetMH = nullptr; + if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) { + targetMH = mh; + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { + parsingDiag.error("bind ordinal BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE not supported in dylibs in dyld shared cache (found in %s)", parser.installName()); + stop = true; + return; + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) { + parsingDiag.error("bind ordinal BIND_SPECIAL_DYLIB_FLAT_LOOKUP not supported in dylibs in dyld shared cache (found in %s)", parser.installName()); + stop = true; + return; + } + else { + const char* fromPath = depPathsArray[libOrdinal]; + auto pos = installNameToMH.find(fromPath); + if (pos == installNameToMH.end()) { + if (!weakImport) { + _diagnostics.error("dependent dylib %s not found", fromPath); + } + return; + } + targetMH = pos->second; + } + dyld3::MachOParser targetParser(targetMH, true); + dyld3::MachOParser::FoundSymbol foundInfo; + uint64_t targetValue = 0; + uint8_t* fixupLoc = segCacheStartsArray[dataSegIndex] + dataSegOffset; + if ( targetParser.findExportedSymbol(parsingDiag, symbolName, nullptr, foundInfo, dylibFinder) ) { + const mach_header* foundInMH = foundInfo.foundInDylib; + dyld3::MachOParser foundInParser(foundInMH, true); + uint64_t foundInBaseAddress = foundInParser.preferredLoadAddress(); + switch ( foundInfo.kind ) { + case dyld3::MachOParser::FoundSymbol::Kind::resolverOffset: + // Bind to the target stub for resolver based functions. + // There may be a later optimization to alter the client + // stubs to directly to the target stub's lazy pointer. + case dyld3::MachOParser::FoundSymbol::Kind::headerOffset: + targetValue = foundInBaseAddress + foundInfo.value + addend; + _pointersForASLR.push_back((void*)fixupLoc); + if ( foundInMH != mh ) { + uint32_t mhVmOffset = (uint32_t)((uint8_t*)foundInMH - (uint8_t*)_buffer); + uint32_t definitionCacheVmOffset = (uint32_t)(mhVmOffset + foundInfo.value); + uint32_t referenceCacheDataVmOffset = (uint32_t)(segCacheAddrsArray[dataSegIndex] + dataSegOffset - regions[1].address); + assert(referenceCacheDataVmOffset < (1<<30)); + dyld3::launch_cache::binary_format::PatchOffset entry; + entry.last = false; + entry.hasAddend = (addend != 0); + entry.dataRegionOffset = referenceCacheDataVmOffset; + _patchTable[foundInMH][definitionCacheVmOffset].insert(*((uint32_t*)&entry)); + } + break; + case dyld3::MachOParser::FoundSymbol::Kind::absolute: + // pointers set to absolute values are not slid + targetValue = foundInfo.value + addend; + break; + } + } + else if ( weakImport ) { + // weak pointers set to zero are not slid + targetValue = 0; + } + else { + parsingDiag.error("cannot find symbol %s, needed in dylib %s", symbolName, parser.installName()); + stop = true; + } + switch ( type ) { + case BIND_TYPE_POINTER: + if ( is64 ) + *((uint64_t*)fixupLoc) = targetValue; + else + *((uint32_t*)fixupLoc) = (uint32_t)targetValue; + break; + case BIND_TYPE_TEXT_ABSOLUTE32: + case BIND_TYPE_TEXT_PCREL32: + parsingDiag.error("text relocs not supported for shared cache binding in %s", parser.installName()); + stop = true; + break; + default: + parsingDiag.error("bad bind type (%d) in %s", type, parser.installName()); + stop = true; + break; + + } + }); + if ( bindingDiag.hasError() ) { + parsingDiag.error("%s in dylib %s", bindingDiag.errorMessage().c_str(), parser.installName()); + } + if ( parsingDiag.hasError() ) + break; + // also need to add patch locations for weak-binds that point within same image, since they are not captured by binds above + parser.forEachWeakDef(bindingDiag, ^(bool strongDef, uint32_t dataSegIndex, uint64_t dataSegOffset, uint64_t addend, const char* symbolName, bool &stop) { + if ( strongDef ) + return; + uint8_t* fixupLoc = segCacheStartsArray[dataSegIndex] + dataSegOffset; + dyld3::MachOParser::FoundSymbol weakFoundInfo; + Diagnostics weakLookupDiag; + if ( parser.findExportedSymbol(weakLookupDiag, symbolName, nullptr, weakFoundInfo, nullptr) ) { + // this is an interior pointing (rebased) pointer + uint64_t targetValue; + if ( is64 ) + targetValue = *((uint64_t*)fixupLoc); + else + targetValue = *((uint32_t*)fixupLoc); + uint32_t definitionCacheVmOffset = (uint32_t)(targetValue - regions[0].address); + uint32_t referenceCacheDataVmOffset = (uint32_t)(segCacheAddrsArray[dataSegIndex] + dataSegOffset - regions[1].address); + assert(referenceCacheDataVmOffset < (1<<30)); + dyld3::launch_cache::binary_format::PatchOffset entry; + entry.last = false; + entry.hasAddend = (addend != 0); + entry.dataRegionOffset = referenceCacheDataVmOffset; + _patchTable[mh][definitionCacheVmOffset].insert(*((uint32_t*)&entry)); + } + }); + if ( bindingDiag.hasError() ) { + parsingDiag.error("%s in dylib %s", bindingDiag.errorMessage().c_str(), parser.installName()); + } + if ( parsingDiag.hasError() ) + break; + } + + if ( log ) { + unsigned lazyCount = 0; + unsigned nonLazyCount = 0; + std::unordered_set lazyTargets; + for (auto entry : useCounts) { + fprintf(stderr, "% 3ld % 3ld %s\n", entry.second.lazyCount, entry.second.nonLazyCount, entry.first.c_str()); + lazyCount += entry.second.lazyCount; + nonLazyCount += entry.second.nonLazyCount; + if ( entry.second.lazyCount != 0 ) + lazyTargets.insert(entry.first); + } + fprintf(stderr, "lazyCount = %d\n", lazyCount); + fprintf(stderr, "nonLazyCount = %d\n", nonLazyCount); + fprintf(stderr, "unique lazys = %ld\n", lazyTargets.size()); + } + + if ( parsingDiag.hasError() ) + _diagnostics.error("%s", parsingDiag.errorMessage().c_str()); +} + + +void CacheBuilder::recomputeCacheUUID(void) +{ + // Clear existing UUID, then MD5 whole cache buffer. + uint8_t* uuidLoc = _buffer->header.uuid; + bzero(uuidLoc, 16); + CC_MD5(_buffer, (unsigned)_currentFileSize, uuidLoc); + // uuids should conform to RFC 4122 UUID version 4 & UUID version 5 formats + uuidLoc[6] = ( uuidLoc[6] & 0x0F ) | ( 3 << 4 ); + uuidLoc[8] = ( uuidLoc[8] & 0x3F ) | 0x80; +} + + +CacheBuilder::SegmentMapping CacheBuilder::assignSegmentAddresses(const std::vector& dylibs, dyld_cache_mapping_info regions[3]) +{ + // calculate size of header info and where first dylib's mach_header should start + size_t startOffset = sizeof(dyld_cache_header) + 3*sizeof(dyld_cache_mapping_info); + size_t maxPoolCount = 0; + if ( _archLayout->branchReach != 0 ) + maxPoolCount = (_archLayout->sharedMemorySize / _archLayout->branchReach); + startOffset += maxPoolCount * sizeof(uint64_t); + startOffset += sizeof(dyld_cache_image_info) * dylibs.size(); + startOffset += sizeof(dyld_cache_image_text_info) * dylibs.size(); + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + dyld3::MachOParser parser(dylib.mh); + startOffset += (strlen(parser.installName()) + 1); + } + //fprintf(stderr, "%s total header size = 0x%08lX\n", _options.archName.c_str(), startOffset); + startOffset = align(startOffset, 12); + + _branchPoolStarts.clear(); + __block uint64_t addr = _archLayout->sharedMemoryStart; + __block SegmentMapping result; + + // assign TEXT segment addresses + regions[0].address = addr; + regions[0].fileOffset = 0; + regions[0].initProt = VM_PROT_READ | VM_PROT_EXECUTE; + regions[0].maxProt = VM_PROT_READ | VM_PROT_EXECUTE; + addr += startOffset; // header + + __block uint64_t lastPoolAddress = addr; + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + dyld3::MachOParser parser(dylib.mh, true); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) { + if ( protections != (VM_PROT_READ | VM_PROT_EXECUTE) ) + return; + // Insert branch island pools every 128MB for arm64 + if ( (_archLayout->branchPoolTextSize != 0) && ((addr + vmSize - lastPoolAddress) > _archLayout->branchReach) ) { + _branchPoolStarts.push_back(addr); + _diagnostics.verbose("adding branch pool at 0x%llX\n", addr); + lastPoolAddress = addr; + addr += _archLayout->branchPoolTextSize; + } + // Keep __TEXT segments 4K or more aligned + addr = align(addr, std::max(p2align, (uint8_t)12)); + SegmentMappingInfo info; + info.srcSegment = (uint8_t*)dylib.mh + fileOffset; + info.segName = segName; + info.dstCacheAddress = addr; + info.dstCacheOffset = (uint32_t)(addr - regions[0].address + regions[0].fileOffset); + info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12); + info.copySegmentSize = (uint32_t)align(sizeOfSections, 12); + info.srcSegmentIndex = segIndex; + result[dylib.mh].push_back(info); + addr += info.dstCacheSegmentSize; + }); + } + // align TEXT region end + uint64_t endTextAddress = align(addr, _archLayout->sharedRegionAlignP2); + regions[0].size = endTextAddress - regions[0].address; + + // assign __DATA* addresses + if ( _archLayout->sharedRegionsAreDiscontiguous ) + addr = _archLayout->sharedMemoryStart + 0x60000000; + else + addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2); + regions[1].address = addr; + regions[1].fileOffset = regions[0].fileOffset + regions[0].size; + regions[1].initProt = VM_PROT_READ | VM_PROT_WRITE; + regions[1].maxProt = VM_PROT_READ | VM_PROT_WRITE; + + // layout all __DATA_CONST segments + __block int dataConstSegmentCount = 0; + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + dyld3::MachOParser parser(dylib.mh, true); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) { + if ( protections != (VM_PROT_READ | VM_PROT_WRITE) ) + return; + if ( strcmp(segName, "__DATA_CONST") != 0 ) + return; + ++dataConstSegmentCount; + // Pack __DATA_CONST segments + addr = align(addr, p2align); + size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections); + SegmentMappingInfo info; + info.srcSegment = (uint8_t*)dylib.mh + fileOffset; + info.segName = segName; + info.dstCacheAddress = addr; + info.dstCacheOffset = (uint32_t)(addr - regions[1].address + regions[1].fileOffset); + info.dstCacheSegmentSize = (uint32_t)sizeOfSections; + info.copySegmentSize = (uint32_t)copySize; + info.srcSegmentIndex = segIndex; + result[dylib.mh].push_back(info); + addr += info.dstCacheSegmentSize; + }); + } + + // layout all __DATA segments (and other r/w non-dirty, non-const) segments + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + dyld3::MachOParser parser(dylib.mh, true); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) { + if ( protections != (VM_PROT_READ | VM_PROT_WRITE) ) + return; + if ( strcmp(segName, "__DATA_CONST") == 0 ) + return; + if ( strcmp(segName, "__DATA_DIRTY") == 0 ) + return; + if ( dataConstSegmentCount > 10 ) { + // Pack __DATA segments only if we also have __DATA_CONST segments + addr = align(addr, p2align); + } + else { + // Keep __DATA segments 4K or more aligned + addr = align(addr, std::max(p2align, (uint8_t)12)); + } + size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections); + SegmentMappingInfo info; + info.srcSegment = (uint8_t*)dylib.mh + fileOffset; + info.segName = segName; + info.dstCacheAddress = addr; + info.dstCacheOffset = (uint32_t)(addr - regions[1].address + regions[1].fileOffset); + info.dstCacheSegmentSize = (uint32_t)sizeOfSections; + info.copySegmentSize = (uint32_t)copySize; + info.srcSegmentIndex = segIndex; + result[dylib.mh].push_back(info); + addr += info.dstCacheSegmentSize; + }); + } + + // layout all __DATA_DIRTY segments, sorted + addr = align(addr, 12); + std::vector dirtyDataDylibs = makeSortedDylibs(dylibs, _options.dirtyDataSegmentOrdering); + for (const DyldSharedCache::MappedMachO& dylib : dirtyDataDylibs) { + dyld3::MachOParser parser(dylib.mh, true); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) { + if ( protections != (VM_PROT_READ | VM_PROT_WRITE) ) + return; + if ( strcmp(segName, "__DATA_DIRTY") != 0 ) + return; + // Pack __DATA_DIRTY segments + addr = align(addr, p2align); + size_t copySize = std::min((size_t)fileSize, (size_t)sizeOfSections); + SegmentMappingInfo info; + info.srcSegment = (uint8_t*)dylib.mh + fileOffset; + info.segName = segName; + info.dstCacheAddress = addr; + info.dstCacheOffset = (uint32_t)(addr - regions[1].address + regions[1].fileOffset); + info.dstCacheSegmentSize = (uint32_t)sizeOfSections; + info.copySegmentSize = (uint32_t)copySize; + info.srcSegmentIndex = segIndex; + result[dylib.mh].push_back(info); + addr += info.dstCacheSegmentSize; + }); + } + + // align DATA region end + uint64_t endDataAddress = align(addr, _archLayout->sharedRegionAlignP2); + regions[1].size = endDataAddress - regions[1].address; + + // start read-only region + if ( _archLayout->sharedRegionsAreDiscontiguous ) + addr = _archLayout->sharedMemoryStart + 0xA0000000; + else + addr = align((addr + _archLayout->sharedRegionPadding), _archLayout->sharedRegionAlignP2); + regions[2].address = addr; + regions[2].fileOffset = regions[1].fileOffset + regions[1].size; + regions[2].maxProt = VM_PROT_READ; + regions[2].initProt = VM_PROT_READ; + + // reserve space for kernel ASLR slide info at start of r/o region + if ( _options.cacheSupportsASLR ) { + _slideInfoBufferSizeAllocated = align((regions[1].size/4096) * 4, _archLayout->sharedRegionAlignP2); // only need 2 bytes per page + _slideInfoFileOffset = regions[2].fileOffset; + addr += _slideInfoBufferSizeAllocated; + } + + // layout all read-only (but not LINKEDIT) segments + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + dyld3::MachOParser parser(dylib.mh, true); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) { + if ( protections != VM_PROT_READ ) + return; + if ( strcmp(segName, "__LINKEDIT") == 0 ) + return; + // Keep segments segments 4K or more aligned + addr = align(addr, std::max(p2align, (uint8_t)12)); + SegmentMappingInfo info; + info.srcSegment = (uint8_t*)dylib.mh + fileOffset; + info.segName = segName; + info.dstCacheAddress = addr; + info.dstCacheOffset = (uint32_t)(addr - regions[2].address + regions[2].fileOffset); + info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12); + info.copySegmentSize = (uint32_t)sizeOfSections; + info.srcSegmentIndex = segIndex; + result[dylib.mh].push_back(info); + addr += info.dstCacheSegmentSize; + }); + } + // layout all LINKEDIT segments (after other read-only segments) + for (const DyldSharedCache::MappedMachO& dylib : dylibs) { + dyld3::MachOParser parser(dylib.mh, true); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, uint32_t segIndex, uint64_t sizeOfSections, uint8_t p2align, bool& stop) { + if ( protections != VM_PROT_READ ) + return; + if ( strcmp(segName, "__LINKEDIT") != 0 ) + return; + // Keep segments segments 4K or more aligned + addr = align(addr, std::max(p2align, (uint8_t)12)); + SegmentMappingInfo info; + info.srcSegment = (uint8_t*)dylib.mh + fileOffset; + info.segName = segName; + info.dstCacheAddress = addr; + info.dstCacheOffset = (uint32_t)(addr - regions[2].address + regions[2].fileOffset); + info.dstCacheSegmentSize = (uint32_t)align(sizeOfSections, 12); + info.copySegmentSize = (uint32_t)align(fileSize, 12); + info.srcSegmentIndex = segIndex; + result[dylib.mh].push_back(info); + addr += info.dstCacheSegmentSize; + }); + } + // add room for branch pool linkedits + _branchPoolsLinkEditStartAddr = addr; + addr += (_branchPoolStarts.size() * _archLayout->branchPoolLinkEditSize); + + // align r/o region end + uint64_t endReadOnlyAddress = align(addr, _archLayout->sharedRegionAlignP2); + regions[2].size = endReadOnlyAddress - regions[2].address; + _currentFileSize = regions[2].fileOffset + regions[2].size; + + // FIXME: Confirm these numbers for all platform/arch combos + // assume LINKEDIT optimzation reduces LINKEDITs to %40 of original size + if ( _options.excludeLocalSymbols ) { + _vmSize = regions[2].address + (regions[2].size * 2 / 5) - regions[0].address; + } + else { + _vmSize = regions[2].address + (regions[2].size * 9 / 10) - regions[0].address; + } + + // sort SegmentMappingInfo for each image to be in the same order as original segments + for (auto& entry : result) { + std::vector& infos = entry.second; + std::sort(infos.begin(), infos.end(), [&](const SegmentMappingInfo& a, const SegmentMappingInfo& b) { + return a.srcSegmentIndex < b.srcSegmentIndex; + }); + } + + return result; +} + +uint64_t CacheBuilder::pathHash(const char* path) +{ + uint64_t sum = 0; + for (const char* s=path; *s != '\0'; ++s) + sum += sum*4 + *s; + return sum; +} + + +void CacheBuilder::findDylibAndSegment(const void* contentPtr, std::string& foundDylibName, std::string& foundSegName) +{ + foundDylibName = "???"; + foundSegName = "???"; + uint32_t cacheOffset = (uint32_t)((uint8_t*)contentPtr - (uint8_t*)_buffer); + _buffer->forEachImage(^(const mach_header* mh, const char* installName) { + dyld3::MachOParser parser(mh, true); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( (cacheOffset > fileOffset) && (cacheOffset < (fileOffset+vmSize)) ) { + foundDylibName = installName; + foundSegName = segName; + } + }); + }); + } + + +template +bool CacheBuilder::makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t offset, const dyld_cache_slide_info2* info) +{ + typedef typename P::uint_t pint_t; + + const pint_t deltaMask = (pint_t)(info->delta_mask); + const pint_t valueMask = ~deltaMask; + const pint_t valueAdd = (pint_t)(info->value_add); + const unsigned deltaShift = __builtin_ctzll(deltaMask) - 2; + const uint32_t maxDelta = (uint32_t)(deltaMask >> deltaShift); + + pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset+0]; + pint_t lastValue = (pint_t)P::getP(*lastLoc); + if ( (lastValue - valueAdd) & deltaMask ) { + std::string dylibName; + std::string segName; + findDylibAndSegment((void*)pageContent, dylibName, segName); + _diagnostics.error("rebase pointer does not point within cache. lastOffset=0x%04X, seg=%s, dylib=%s\n", + lastLocationOffset, segName.c_str(), dylibName.c_str()); + return false; + } + if ( offset <= (lastLocationOffset+maxDelta) ) { + // previous location in range, make link from it + // encode this location into last value + pint_t delta = offset - lastLocationOffset; + pint_t newLastValue = ((lastValue - valueAdd) & valueMask) | (delta << deltaShift); + //warning(" add chain: delta = %d, lastOffset=0x%03X, offset=0x%03X, org value=0x%08lX, new value=0x%08lX", + // offset - lastLocationOffset, lastLocationOffset, offset, (long)lastValue, (long)newLastValue); + P::setP(*lastLoc, newLastValue); + return true; + } + //warning(" too big delta = %d, lastOffset=0x%03X, offset=0x%03X", offset - lastLocationOffset, lastLocationOffset, offset); + + // distance between rebase locations is too far + // see if we can make a chain from non-rebase locations + uint16_t nonRebaseLocationOffsets[1024]; + unsigned nrIndex = 0; + for (uint16_t i = lastLocationOffset; i < offset-maxDelta; ) { + nonRebaseLocationOffsets[nrIndex] = 0; + for (int j=maxDelta; j > 0; j -= 4) { + pint_t value = (pint_t)P::getP(*(pint_t*)&pageContent[i+j]); + if ( value == 0 ) { + // Steal values of 0 to be used in the rebase chain + nonRebaseLocationOffsets[nrIndex] = i+j; + break; + } + } + if ( nonRebaseLocationOffsets[nrIndex] == 0 ) { + lastValue = (pint_t)P::getP(*lastLoc); + pint_t newValue = ((lastValue - valueAdd) & valueMask); + //warning(" no way to make non-rebase delta chain, terminate off=0x%03X, old value=0x%08lX, new value=0x%08lX", lastLocationOffset, (long)value, (long)newValue); + P::setP(*lastLoc, newValue); + return false; + } + i = nonRebaseLocationOffsets[nrIndex]; + ++nrIndex; + } + + // we can make chain. go back and add each non-rebase location to chain + uint16_t prevOffset = lastLocationOffset; + pint_t* prevLoc = (pint_t*)&pageContent[prevOffset]; + for (int n=0; n < nrIndex; ++n) { + uint16_t nOffset = nonRebaseLocationOffsets[n]; + assert(nOffset != 0); + pint_t* nLoc = (pint_t*)&pageContent[nOffset]; + uint32_t delta2 = nOffset - prevOffset; + pint_t value = (pint_t)P::getP(*prevLoc); + pint_t newValue; + if ( value == 0 ) + newValue = (delta2 << deltaShift); + else + newValue = ((value - valueAdd) & valueMask) | (delta2 << deltaShift); + //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta2, nOffset, (long)value, (long)newValue); + P::setP(*prevLoc, newValue); + prevOffset = nOffset; + prevLoc = nLoc; + } + uint32_t delta3 = offset - prevOffset; + pint_t value = (pint_t)P::getP(*prevLoc); + pint_t newValue; + if ( value == 0 ) + newValue = (delta3 << deltaShift); + else + newValue = ((value - valueAdd) & valueMask) | (delta3 << deltaShift); + //warning(" non-rebase delta = %d, to off=0x%03X, old value=0x%08lX, new value=0x%08lX", delta3, offset, (long)value, (long)newValue); + P::setP(*prevLoc, newValue); + + return true; +} + + +template +void CacheBuilder::addPageStarts(uint8_t* pageContent, const bool bitmap[], const dyld_cache_slide_info2* info, + std::vector& pageStarts, std::vector& pageExtras) +{ + typedef typename P::uint_t pint_t; + + const pint_t deltaMask = (pint_t)(info->delta_mask); + const pint_t valueMask = ~deltaMask; + const uint32_t pageSize = info->page_size; + const pint_t valueAdd = (pint_t)(info->value_add); + + uint16_t startValue = DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE; + uint16_t lastLocationOffset = 0xFFFF; + for(int i=0; i < pageSize/4; ++i) { + unsigned offset = i*4; + if ( bitmap[i] ) { + if ( startValue == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { + // found first rebase location in page + startValue = i; + } + else if ( !makeRebaseChain

(pageContent, lastLocationOffset, offset, info) ) { + // can't record all rebasings in one chain + if ( (startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 ) { + // switch page_start to "extras" which is a list of chain starts + unsigned indexInExtras = (unsigned)pageExtras.size(); + if ( indexInExtras > 0x3FFF ) { + _diagnostics.error("rebase overflow in page extras"); + return; + } + pageExtras.push_back(startValue); + startValue = indexInExtras | DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA; + } + pageExtras.push_back(i); + } + lastLocationOffset = offset; + } + } + if ( lastLocationOffset != 0xFFFF ) { + // mark end of chain + pint_t* lastLoc = (pint_t*)&pageContent[lastLocationOffset]; + pint_t lastValue = (pint_t)P::getP(*lastLoc); + pint_t newValue = ((lastValue - valueAdd) & valueMask); + P::setP(*lastLoc, newValue); + } + if ( startValue & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + // add end bit to extras + pageExtras.back() |= DYLD_CACHE_SLIDE_PAGE_ATTR_END; + } + pageStarts.push_back(startValue); +} + +template +void CacheBuilder::writeSlideInfoV2() +{ + typedef typename P::uint_t pint_t; + typedef typename P::E E; + const uint32_t pageSize = 4096; + + // build one 1024/4096 bool bitmap per page (4KB/16KB) of DATA + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset); + uint8_t* const dataStart = (uint8_t*)_buffer + mappings[1].fileOffset; + uint8_t* const dataEnd = dataStart + mappings[1].size; + unsigned pageCount = (unsigned)(mappings[1].size+pageSize-1)/pageSize; + const long bitmapSize = pageCount*(pageSize/4)*sizeof(bool); + bool* bitmap = (bool*)calloc(bitmapSize, 1); + for (void* p : _pointersForASLR) { + if ( (p < dataStart) || ( p > dataEnd) ) { + _diagnostics.error("DATA pointer for sliding, out of range\n"); + free(bitmap); + return; + } + long byteOffset = (long)((uint8_t*)p - dataStart); + if ( (byteOffset % 4) != 0 ) { + _diagnostics.error("pointer not 4-byte aligned in DATA offset 0x%08lX\n", byteOffset); + free(bitmap); + return; + } + long boolIndex = byteOffset / 4; + // work around by ignoring pointers to be slid that are NULL on disk + if ( *((pint_t*)p) == 0 ) { + std::string dylibName; + std::string segName; + findDylibAndSegment(p, dylibName, segName); + _diagnostics.warning("NULL pointer asked to be slid in %s at DATA region offset 0x%04lX of %s", segName.c_str(), byteOffset, dylibName.c_str()); + continue; + } + bitmap[boolIndex] = true; + } + + // fill in fixed info + assert(_slideInfoFileOffset != 0); + dyld_cache_slide_info2* info = (dyld_cache_slide_info2*)((uint8_t*)_buffer + _slideInfoFileOffset); + info->version = 2; + info->page_size = pageSize; + info->delta_mask = _archLayout->pointerDeltaMask; + info->value_add = (sizeof(pint_t) == 8) ? 0 : _archLayout->sharedMemoryStart; // only value_add for 32-bit archs + + // set page starts and extras for each page + std::vector pageStarts; + std::vector pageExtras; + pageStarts.reserve(pageCount); + uint8_t* pageContent = dataStart;; + const bool* bitmapForPage = bitmap; + for (unsigned i=0; i < pageCount; ++i) { + //warning("page[%d]", i); + addPageStarts

(pageContent, bitmapForPage, info, pageStarts, pageExtras); + if ( _diagnostics.hasError() ) { + free(bitmap); + return; + } + pageContent += pageSize; + bitmapForPage += (sizeof(bool)*(pageSize/4)); + } + free((void*)bitmap); + + // fill in computed info + info->page_starts_offset = sizeof(dyld_cache_slide_info2); + info->page_starts_count = (unsigned)pageStarts.size(); + info->page_extras_offset = (unsigned)(sizeof(dyld_cache_slide_info2)+pageStarts.size()*sizeof(uint16_t)); + info->page_extras_count = (unsigned)pageExtras.size(); + uint16_t* pageStartsBuffer = (uint16_t*)((char*)info + info->page_starts_offset); + uint16_t* pageExtrasBuffer = (uint16_t*)((char*)info + info->page_extras_offset); + for (unsigned i=0; i < pageStarts.size(); ++i) + pageStartsBuffer[i] = pageStarts[i]; + for (unsigned i=0; i < pageExtras.size(); ++i) + pageExtrasBuffer[i] = pageExtras[i]; + // update header with final size + _buffer->header.slideInfoSize = align(info->page_extras_offset + pageExtras.size()*sizeof(uint16_t), _archLayout->sharedRegionAlignP2); + if ( _buffer->header.slideInfoSize > _slideInfoBufferSizeAllocated ) { + _diagnostics.error("kernel slide info overflow buffer"); + } + //warning("pageCount=%u, page_starts_count=%lu, page_extras_count=%lu", pageCount, pageStarts.size(), pageExtras.size()); +} + + +/* +void CacheBuilder::writeSlideInfoV1() +{ + // build one 128-byte bitmap per page (4096) of DATA + uint8_t* const dataStart = (uint8_t*)_buffer.get() + regions[1].fileOffset; + uint8_t* const dataEnd = dataStart + regions[1].size; + const long bitmapSize = (dataEnd - dataStart)/(4*8); + uint8_t* bitmap = (uint8_t*)calloc(bitmapSize, 1); + for (void* p : _pointersForASLR) { + if ( (p < dataStart) || ( p > dataEnd) ) + terminate("DATA pointer for sliding, out of range\n"); + long offset = (long)((uint8_t*)p - dataStart); + if ( (offset % 4) != 0 ) + terminate("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); + } + + // allocate worst case size block of all slide info + const unsigned entry_size = 4096/(8*4); // 8 bits per byte, possible pointer every 4 bytes. + const unsigned toc_count = (unsigned)bitmapSize/entry_size; + dyld_cache_slide_info* slideInfo = (dyld_cache_slide_info*)((uint8_t*)_buffer + _slideInfoFileOffset); + slideInfo->version = 1; + slideInfo->toc_offset = sizeof(dyld_cache_slide_info); + slideInfo->toc_count = toc_count; + slideInfo->entries_offset = (slideInfo->toc_offset+2*toc_count+127)&(-128); + slideInfo->entries_count = 0; + slideInfo->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 ) { + 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->entries_count = entry_count; + ::free((void*)bitmap); + + _buffer.header->slideInfoSize = align(slideInfo->entries_offset + entry_count*entry_size, _archLayout->sharedRegionAlignP2); +} + +*/ + +void CacheBuilder::fipsSign() { + __block bool found = false; + _buffer->forEachImage(^(const mach_header* mh, const char* installName) { + __block void *hash_location = nullptr; + // Return if this is not corecrypto + if (strcmp(installName, "/usr/lib/system/libcorecrypto.dylib") != 0) { + return; + } + found = true; + auto parser = dyld3::MachOParser(mh, true); + parser.forEachLocalSymbol(_diagnostics, ^(const char *symbolName, uint64_t n_value, uint8_t n_type, uint8_t n_sect, uint16_t n_desc, bool &stop) { + if (strcmp(symbolName, "_fipspost_precalc_hmac") != 0) + return; + hash_location = (void *)(n_value - _archLayout->sharedMemoryStart + (uintptr_t)_buffer); + stop = true; + }); + + // Bail out if we did not find the symbol + if (hash_location == nullptr) { + _diagnostics.warning("Could not find _fipspost_precalc_hmac, skipping FIPS sealing"); + return; + } + + parser.forEachSection(^(const char *segName, const char *sectionName, uint32_t flags, const void *content, size_t size, bool illegalSectionSize, bool &stop) { + // FIXME: If we ever implement userspace __TEXT_EXEC this will need to be updated + if ( (strcmp(segName, "__TEXT" ) != 0) || (strcmp(sectionName, "__text") != 0) ) { + return; + } + + if (illegalSectionSize) { + _diagnostics.error("FIPS section %s/%s extends beyond the end of the segment", segName, sectionName); + return; + } + + //We have _fipspost_precalc_hmac and __TEXT,__text, seal it + unsigned char hmac_key = 0; + CCHmac(kCCHmacAlgSHA256, &hmac_key, 1, content, size, hash_location); + stop = true; + }); + }); + + if (!found) { + _diagnostics.warning("Could not find /usr/lib/system/libcorecrypto.dylib, skipping FIPS sealing"); + } +} + +void CacheBuilder::codeSign() +{ + uint8_t dscHashType; + uint8_t dscHashSize; + uint32_t dscDigestFormat; + bool agile = false; + + // select which codesigning hash + switch (_options.codeSigningDigestMode) { + case DyldSharedCache::Agile: + agile = true; + // Fall through to SHA1, because the main code directory remains SHA1 for compatibility. + case DyldSharedCache::SHA1only: + dscHashType = CS_HASHTYPE_SHA1; + dscHashSize = CS_HASH_SIZE_SHA1; + dscDigestFormat = kCCDigestSHA1; + break; + case DyldSharedCache::SHA256only: + dscHashType = CS_HASHTYPE_SHA256; + dscHashSize = CS_HASH_SIZE_SHA256; + dscDigestFormat = kCCDigestSHA256; + break; + default: + _diagnostics.error("codeSigningDigestMode has unknown, unexpected value %d, bailing out.", + _options.codeSigningDigestMode); + return; + } + + std::string cacheIdentifier = "com.apple.dyld.cache." + _options.archName; + if ( _options.dylibsRemovedDuringMastering ) { + if ( _options.optimizeStubs ) + cacheIdentifier = "com.apple.dyld.cache." + _options.archName + ".release"; + else + cacheIdentifier = "com.apple.dyld.cache." + _options.archName + ".development"; + } + // get pointers into shared cache buffer + size_t inBbufferSize = _currentFileSize; + const uint8_t* inBuffer = (uint8_t*)_buffer; + uint8_t* csBuffer = (uint8_t*)_buffer+inBbufferSize; + + // layout code signature contents + uint32_t blobCount = agile ? 4 : 3; + size_t idSize = cacheIdentifier.size()+1; // +1 for terminating 0 + uint32_t slotCount = (uint32_t)((inBbufferSize + CS_PAGE_SIZE - 1) / CS_PAGE_SIZE); + uint32_t xSlotCount = CSSLOT_REQUIREMENTS; + size_t idOffset = offsetof(CS_CodeDirectory, end_withExecSeg); + size_t hashOffset = idOffset+idSize + dscHashSize*xSlotCount; + size_t hash256Offset = idOffset+idSize + CS_HASH_SIZE_SHA256*xSlotCount; + size_t cdSize = hashOffset + (slotCount * dscHashSize); + size_t cd256Size = agile ? hash256Offset + (slotCount * CS_HASH_SIZE_SHA256) : 0; + size_t reqsSize = 12; + size_t cmsSize = sizeof(CS_Blob); + size_t cdOffset = sizeof(CS_SuperBlob) + blobCount*sizeof(CS_BlobIndex); + size_t cd256Offset = cdOffset + cdSize; + size_t reqsOffset = cd256Offset + cd256Size; // equals cdOffset + cdSize if not agile + size_t cmsOffset = reqsOffset + reqsSize; + size_t sbSize = cmsOffset + cmsSize; + size_t sigSize = align(sbSize, 14); // keep whole cache 16KB aligned + + if ( _currentFileSize+sigSize > _allocatedBufferSize ) { + _diagnostics.error("cache buffer too small to hold code signature (buffer size=%lldMB, signature size=%ldMB, free space=%lldMB)", + _allocatedBufferSize/1024/1024, sigSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024); + return; + } + + // create overall code signature which is a superblob + CS_SuperBlob* sb = reinterpret_cast(csBuffer); + sb->magic = htonl(CSMAGIC_EMBEDDED_SIGNATURE); + sb->length = htonl(sbSize); + sb->count = htonl(blobCount); + sb->index[0].type = htonl(CSSLOT_CODEDIRECTORY); + sb->index[0].offset = htonl(cdOffset); + sb->index[1].type = htonl(CSSLOT_REQUIREMENTS); + sb->index[1].offset = htonl(reqsOffset); + sb->index[2].type = htonl(CSSLOT_CMS_SIGNATURE); + sb->index[2].offset = htonl(cmsOffset); + if ( agile ) { + sb->index[3].type = htonl(CSSLOT_ALTERNATE_CODEDIRECTORIES + 0); + sb->index[3].offset = htonl(cd256Offset); + } + + // fill in empty requirements + CS_RequirementsBlob* reqs = (CS_RequirementsBlob*)(((char*)sb)+reqsOffset); + reqs->magic = htonl(CSMAGIC_REQUIREMENTS); + reqs->length = htonl(sizeof(CS_RequirementsBlob)); + reqs->data = 0; + + // initialize fixed fields of Code Directory + CS_CodeDirectory* cd = (CS_CodeDirectory*)(((char*)sb)+cdOffset); + cd->magic = htonl(CSMAGIC_CODEDIRECTORY); + cd->length = htonl(cdSize); + cd->version = htonl(0x20400); // supports exec segment + cd->flags = htonl(kSecCodeSignatureAdhoc); + cd->hashOffset = htonl(hashOffset); + cd->identOffset = htonl(idOffset); + cd->nSpecialSlots = htonl(xSlotCount); + cd->nCodeSlots = htonl(slotCount); + cd->codeLimit = htonl(inBbufferSize); + cd->hashSize = dscHashSize; + cd->hashType = dscHashType; + cd->platform = 0; // not platform binary + cd->pageSize = __builtin_ctz(CS_PAGE_SIZE); // log2(CS_PAGE_SIZE); + cd->spare2 = 0; // unused (must be zero) + cd->scatterOffset = 0; // not supported anymore + cd->teamOffset = 0; // no team ID + cd->spare3 = 0; // unused (must be zero) + cd->codeLimit64 = 0; // falls back to codeLimit + + // executable segment info + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset); + cd->execSegBase = htonll(mappings[0].fileOffset); // base of TEXT segment + cd->execSegLimit = htonll(mappings[0].size); // size of TEXT segment + cd->execSegFlags = 0; // not a main binary + + // initialize dynamic fields of Code Directory + strcpy((char*)cd + idOffset, cacheIdentifier.c_str()); + + // add special slot hashes + uint8_t* hashSlot = (uint8_t*)cd + hashOffset; + uint8_t* reqsHashSlot = &hashSlot[-CSSLOT_REQUIREMENTS*dscHashSize]; + CCDigest(dscDigestFormat, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHashSlot); + + CS_CodeDirectory* cd256; + uint8_t* hash256Slot; + uint8_t* reqsHash256Slot; + if ( agile ) { + // Note that the assumption here is that the size up to the hashes is the same as for + // sha1 code directory, and that they come last, after everything else. + + cd256 = (CS_CodeDirectory*)(((char*)sb)+cd256Offset); + cd256->magic = htonl(CSMAGIC_CODEDIRECTORY); + cd256->length = htonl(cd256Size); + cd256->version = htonl(0x20400); // supports exec segment + cd256->flags = htonl(kSecCodeSignatureAdhoc); + cd256->hashOffset = htonl(hash256Offset); + cd256->identOffset = htonl(idOffset); + cd256->nSpecialSlots = htonl(xSlotCount); + cd256->nCodeSlots = htonl(slotCount); + cd256->codeLimit = htonl(inBbufferSize); + cd256->hashSize = CS_HASH_SIZE_SHA256; + cd256->hashType = CS_HASHTYPE_SHA256; + cd256->platform = 0; // not platform binary + cd256->pageSize = __builtin_ctz(CS_PAGE_SIZE); // log2(CS_PAGE_SIZE); + cd256->spare2 = 0; // unused (must be zero) + cd256->scatterOffset = 0; // not supported anymore + cd256->teamOffset = 0; // no team ID + cd256->spare3 = 0; // unused (must be zero) + cd256->codeLimit64 = 0; // falls back to codeLimit + + // executable segment info + cd256->execSegBase = cd->execSegBase; + cd256->execSegLimit = cd->execSegLimit; + cd256->execSegFlags = cd->execSegFlags; + + // initialize dynamic fields of Code Directory + strcpy((char*)cd256 + idOffset, cacheIdentifier.c_str()); + + // add special slot hashes + hash256Slot = (uint8_t*)cd256 + hash256Offset; + reqsHash256Slot = &hash256Slot[-CSSLOT_REQUIREMENTS*CS_HASH_SIZE_SHA256]; + CCDigest(kCCDigestSHA256, (uint8_t*)reqs, sizeof(CS_RequirementsBlob), reqsHash256Slot); + } + else { + cd256 = NULL; + hash256Slot = NULL; + reqsHash256Slot = NULL; + } + + // fill in empty CMS blob for ad-hoc signing + CS_Blob* cms = (CS_Blob*)(((char*)sb)+cmsOffset); + cms->magic = htonl(CSMAGIC_BLOBWRAPPER); + cms->length = htonl(sizeof(CS_Blob)); + + // alter header of cache to record size and location of code signature + // do this *before* hashing each page + _buffer->header.codeSignatureOffset = inBbufferSize; + _buffer->header.codeSignatureSize = sigSize; + + // compute hashes + const uint8_t* code = inBuffer; + for (uint32_t i=0; i < slotCount; ++i) { + CCDigest(dscDigestFormat, code, CS_PAGE_SIZE, hashSlot); + hashSlot += dscHashSize; + + if ( agile ) { + CCDigest(kCCDigestSHA256, code, CS_PAGE_SIZE, hash256Slot); + hash256Slot += CS_HASH_SIZE_SHA256; + } + code += CS_PAGE_SIZE; + } + + // hash of entire code directory (cdHash) uses same hash as each page + uint8_t fullCdHash[dscHashSize]; + CCDigest(dscDigestFormat, (const uint8_t*)cd, cdSize, fullCdHash); + // Note: cdHash is defined as first 20 bytes of hash + memcpy(_cdHashFirst, fullCdHash, 20); + if ( agile ) { + uint8_t fullCdHash256[CS_HASH_SIZE_SHA256]; + CCDigest(kCCDigestSHA256, (const uint8_t*)cd256, cd256Size, fullCdHash256); + // Note: cdHash is defined as first 20 bytes of hash, even for sha256 + memcpy(_cdHashSecond, fullCdHash256, 20); + } + else { + memset(_cdHashSecond, 0, 20); + } + + // increase file size to include newly append code signature + _currentFileSize += sigSize; +} + +const bool CacheBuilder::agileSignature() +{ + return _options.codeSigningDigestMode == DyldSharedCache::Agile; +} + +static const std::string cdHash(uint8_t hash[20]) +{ + char buff[48]; + for (int i = 0; i < 20; ++i) + sprintf(&buff[2*i], "%2.2x", hash[i]); + return buff; +} + +const std::string CacheBuilder::cdHashFirst() +{ + return cdHash(_cdHashFirst); +} + +const std::string CacheBuilder::cdHashSecond() +{ + return cdHash(_cdHashSecond); +} + +void CacheBuilder::addCachedDylibsImageGroup(dyld3::ImageProxyGroup* dylibGroup) +{ + const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = dylibGroup->makeImageGroupBinary(_diagnostics, _s_neverStubEliminate); + if (!groupBinary) + return; + + dyld3::launch_cache::ImageGroup group(groupBinary); + size_t groupSize = group.size(); + + if ( _currentFileSize+groupSize > _allocatedBufferSize ) { + _diagnostics.error("cache buffer too small to hold group[0] info (buffer size=%lldMB, group size=%ldMB, free space=%lldMB)", + _allocatedBufferSize/1024/1024, groupSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024); + return; + } + + // append ImageGroup data to read-only region of cache + uint8_t* loc = (uint8_t*)_buffer + _currentFileSize; + memcpy(loc, groupBinary, groupSize); + dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset); + _buffer->header.dylibsImageGroupAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset); + _buffer->header.dylibsImageGroupSize = (uint32_t)groupSize; + _currentFileSize += groupSize; + free((void*)groupBinary); +} + + +void CacheBuilder::addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup* otherGroup) +{ + const dyld3::launch_cache::binary_format::ImageGroup* groupBinary = otherGroup->makeImageGroupBinary(_diagnostics); + if (!groupBinary) + return; + + dyld3::launch_cache::ImageGroup group(groupBinary); + size_t groupSize = group.size(); + + if ( _currentFileSize+groupSize > _allocatedBufferSize ) { + _diagnostics.error("cache buffer too small to hold group[1] info (buffer size=%lldMB, group size=%ldMB, free space=%lldMB)", + _allocatedBufferSize/1024/1024, groupSize/1024/1024, (_allocatedBufferSize-_currentFileSize)/1024/1024); + return; + } + + // append ImageGroup data to read-only region of cache + uint8_t* loc = (uint8_t*)_buffer + _currentFileSize; + memcpy(loc, groupBinary, groupSize); + dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset); + _buffer->header.otherImageGroupAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset); + _buffer->header.otherImageGroupSize = (uint32_t)groupSize; + _currentFileSize += groupSize; + free((void*)groupBinary); +} + +void CacheBuilder::addClosures(const std::map& closures) +{ + // preflight space needed + size_t closuresSpace = 0; + for (const auto& entry : closures) { + dyld3::launch_cache::Closure closure(entry.second); + closuresSpace += closure.size(); + } + size_t freeSpace = _allocatedBufferSize - _currentFileSize; + if ( closuresSpace > freeSpace ) { + _diagnostics.error("cache buffer too small to hold all closures (buffer size=%lldMB, closures size=%ldMB, free space=%ldMB)", + _allocatedBufferSize/1024/1024, closuresSpace/1024/1024, freeSpace/1024/1024); + return; + } + + dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)_buffer + _buffer->header.mappingOffset); + _buffer->header.progClosuresAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset); + uint8_t* closuresBase = (uint8_t*)_buffer + _currentFileSize; + std::vector closureEntrys; + uint32_t currentClosureOffset = 0; + for (const auto& entry : closures) { + const dyld3::launch_cache::binary_format::Closure* closBuf = entry.second; + closureEntrys.push_back(DylibIndexTrie::Entry(entry.first, DylibIndex(currentClosureOffset))); + dyld3::launch_cache::Closure closure(closBuf); + size_t size = closure.size(); + assert((size % 4) == 0); + memcpy(closuresBase+currentClosureOffset, closBuf, size); + currentClosureOffset += size; + freeSpace -= size; + free((void*)closBuf); + } + _buffer->header.progClosuresSize = currentClosureOffset; + _currentFileSize += currentClosureOffset; + freeSpace = _allocatedBufferSize - _currentFileSize; + + // build trie of indexes into closures list + DylibIndexTrie closureTrie(closureEntrys); + std::vector trieBytes; + closureTrie.emit(trieBytes); + while ( (trieBytes.size() % 8) != 0 ) + trieBytes.push_back(0); + if ( trieBytes.size() > freeSpace ) { + _diagnostics.error("cache buffer too small to hold all closures trie (buffer size=%lldMB, trie size=%ldMB, free space=%ldMB)", + _allocatedBufferSize/1024/1024, trieBytes.size()/1024/1024, freeSpace/1024/1024); + return; + } + memcpy((uint8_t*)_buffer + _currentFileSize, &trieBytes[0], trieBytes.size()); + _buffer->header.progClosuresTrieAddr = mappings[2].address + (_currentFileSize - mappings[2].fileOffset); + _buffer->header.progClosuresTrieSize = trieBytes.size(); + _currentFileSize += trieBytes.size(); +} + + diff --git a/dyld/dyld3/shared-cache/CacheBuilder.h b/dyld/dyld3/shared-cache/CacheBuilder.h new file mode 100644 index 0000000..4a6227c --- /dev/null +++ b/dyld/dyld3/shared-cache/CacheBuilder.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2017 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 CacheBuilder_h +#define CacheBuilder_h + +#include +#include +#include +#include + +#include "DyldSharedCache.h" +#include "Diagnostics.h" +#include "ImageProxy.h" + + +namespace dyld3 { + namespace launch_cache { + namespace binary_format { + struct ImageGroup; + struct Closure; + } + } +} + + +struct CacheBuilder { + + CacheBuilder(const DyldSharedCache::CreateOptions& options); + + bool build(const std::vector& dylibsToCache, + const std::vector& otherOsDylibs, + const std::vector& osExecutables); + void deleteBuffer(); + const DyldSharedCache* buffer() { return _buffer; } + size_t bufferSize() { return (size_t)_allocatedBufferSize; } + std::string errorMessage(); + const std::set warnings(); + const bool agileSignature(); + const std::string cdHashFirst(); + const std::string cdHashSecond(); + + struct SegmentMappingInfo { + const void* srcSegment; + const char* segName; + uint64_t dstCacheAddress; + uint32_t dstCacheOffset; + uint32_t dstCacheSegmentSize; + uint32_t copySegmentSize; + uint32_t srcSegmentIndex; + }; + +private: + + typedef std::unordered_map> SegmentMapping; + + struct ArchLayout + { + uint64_t sharedMemoryStart; + uint64_t sharedMemorySize; + uint64_t sharedRegionPadding; + uint64_t pointerDeltaMask; + const char* archName; + uint32_t branchPoolTextSize; + uint32_t branchPoolLinkEditSize; + uint32_t branchReach; + uint8_t sharedRegionAlignP2; + bool sharedRegionsAreDiscontiguous; + bool is64; + }; + + static const ArchLayout _s_archLayout[]; + static const char* const _s_neverStubEliminate[]; + + std::vector makeSortedDylibs(const std::vector& dylibs, const std::unordered_map sortOrder); + + SegmentMapping assignSegmentAddresses(const std::vector& dylibs, struct dyld_cache_mapping_info regions[3]); + + bool cacheOverflow(const dyld_cache_mapping_info regions[3]); + void adjustImageForNewSegmentLocations(const std::vector& segNewStartAddresses, + const std::vector& segCacheFileOffsets, + const std::vector& segCacheSizes, std::vector& pointersForASLR); + + void fipsSign(); + void codeSign(); + uint64_t pathHash(const char* path); + void writeCacheHeader(const struct dyld_cache_mapping_info regions[3], const std::vector& dylibs, const SegmentMapping&); + void copyRawSegments(const std::vector& dylibs, const SegmentMapping& mapping); + void adjustAllImagesForNewSegmentLocations(const std::vector& dylibs, const SegmentMapping& mapping); + void bindAllImagesInCacheFile(const dyld_cache_mapping_info regions[3]); + void writeSlideInfoV1(); + void recomputeCacheUUID(void); + void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName); + + void addCachedDylibsImageGroup(dyld3::ImageProxyGroup*); + void addCachedOtherDylibsImageGroup(dyld3::ImageProxyGroup*); + void addClosures(const std::map& closures); + + template void writeSlideInfoV2(); + template bool makeRebaseChain(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info); + template void addPageStarts(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info, + std::vector& pageStarts, std::vector& pageExtras); + + + const DyldSharedCache::CreateOptions& _options; + DyldSharedCache* _buffer; + Diagnostics _diagnostics; + const ArchLayout* _archLayout; + uint32_t _aliasCount; + uint64_t _slideInfoFileOffset; + uint64_t _slideInfoBufferSizeAllocated; + uint64_t _allocatedBufferSize; + uint64_t _currentFileSize; + uint64_t _vmSize; + std::unordered_map _dataDirtySegsOrder; + std::vector _pointersForASLR; + dyld3::ImageProxyGroup::PatchTable _patchTable; + std::vector _branchPoolStarts; + uint64_t _branchPoolsLinkEditStartAddr; + uint8_t _cdHashFirst[20]; + uint8_t _cdHashSecond[20]; +}; + + +// implemented in AdjustDylibSegemnts.cpp +void adjustDylibSegments(DyldSharedCache* cache, bool is64, mach_header* mhInCache, const std::vector& mappingInfo, std::vector& pointersForASLR, Diagnostics& diag); + +// implemented in OptimizerLinkedit.cpp +uint64_t optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo); + +// implemented in OptimizerBranches.cpp +void bypassStubs(DyldSharedCache* cache, const std::vector& branchPoolStartAddrs, const char* const alwaysUsesStubsTo[], Diagnostics& diag); + +// implemented in OptimizerObjC.cpp +void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector& pointersForASLR, Diagnostics& diag); + + + +inline uint64_t align(uint64_t addr, uint8_t p2) +{ + uint64_t mask = (1 << p2); + return (addr + mask - 1) & (-mask); +} + + + +#endif /* CacheBuilder_h */ diff --git a/dyld/dyld3/shared-cache/DyldSharedCache.cpp b/dyld/dyld3/shared-cache/DyldSharedCache.cpp new file mode 100644 index 0000000..4fbdd23 --- /dev/null +++ b/dyld/dyld3/shared-cache/DyldSharedCache.cpp @@ -0,0 +1,328 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !DYLD_IN_PROCESS +#include +#include +#include +#include +#include +#endif + +#define NO_ULEB +#include "MachOParser.h" +#include "CacheBuilder.h" +#include "DyldSharedCache.h" +#include "LaunchCache.h" +#include "Trie.hpp" +#include "StringUtils.h" + + + +#if !DYLD_IN_PROCESS +DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& options, + const std::vector& dylibsToCache, + const std::vector& otherOsDylibs, + const std::vector& osExecutables) +{ + CreateResults results; + CacheBuilder cache(options); + + results.overflowed = cache.build(dylibsToCache, otherOsDylibs, osExecutables); + + results.agileSignature = cache.agileSignature(); + results.cdHashFirst = cache.cdHashFirst(); + results.cdHashSecond = cache.cdHashSecond(); + results.warnings = cache.warnings(); + if ( cache.errorMessage().empty() ) { + results.cacheContent = cache.buffer(); + results.cacheLength = cache.bufferSize(); + } + else { + cache.deleteBuffer(); + results.cacheContent = nullptr; + results.cacheLength = 0; + results.errorMessage = cache.errorMessage(); + } + return results; +} + +bool DyldSharedCache::verifySelfContained(std::vector& dylibsToCache, MappedMachO (^loader)(const std::string& runtimePath), std::vector>>& rejected) +{ + + // build map of dylibs + __block std::map> badDylibs; + __block std::set knownDylibs; + for (const DyldSharedCache::MappedMachO& dylib : dylibsToCache) { + std::set reasons; + dyld3::MachOParser parser(dylib.mh); + if (parser.canBePlacedInDyldCache(dylib.runtimePath, reasons)) { + knownDylibs.insert(dylib.runtimePath); + knownDylibs.insert(parser.installName()); + } else { + badDylibs[dylib.runtimePath] = reasons; + } + } + + // check all dependencies to assure every dylib in cache only depends on other dylibs in cache + __block bool doAgain = true; + while ( doAgain ) { + __block std::vector foundMappings; + doAgain = false; + // scan dylib list making sure all dependents are in dylib list + for (const DyldSharedCache::MappedMachO& dylib : dylibsToCache) { + if ( badDylibs.count(dylib.runtimePath) != 0 ) + continue; + dyld3::MachOParser parser(dylib.mh); + parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + if ( knownDylibs.count(loadPath) == 0 ) { + doAgain = true; + MappedMachO foundMapping; + if ( badDylibs.count(loadPath) == 0 ) + foundMapping = loader(loadPath); + if ( foundMapping.length == 0 ) { + std::string reason = std::string("Could not find dependency '") + loadPath +"'"; + auto i = badDylibs.find(dylib.runtimePath); + if (i == badDylibs.end()) { + std::set reasons; + reasons.insert(reason); + badDylibs[dylib.runtimePath] = reasons; + } else { + i->second.insert(reason); + } + knownDylibs.erase(dylib.runtimePath); + dyld3::MachOParser parserBad(dylib.mh); + knownDylibs.erase(parserBad.installName()); + } + else { + dyld3::MachOParser foundParser(foundMapping.mh); + std::set reasons; + if (foundParser.canBePlacedInDyldCache(foundParser.installName(), reasons)) { + foundMappings.push_back(foundMapping); + knownDylibs.insert(foundMapping.runtimePath); + knownDylibs.insert(foundParser.installName()); + } else { + auto i = badDylibs.find(dylib.runtimePath); + if (i == badDylibs.end()) { + badDylibs[dylib.runtimePath] = reasons; + } else { + i->second.insert(reasons.begin(), reasons.end()); + } + } + } + } + }); + } + dylibsToCache.insert(dylibsToCache.end(), foundMappings.begin(), foundMappings.end()); + // remove bad dylibs + const auto badDylibsCopy = badDylibs; + dylibsToCache.erase(std::remove_if(dylibsToCache.begin(), dylibsToCache.end(), [&](const DyldSharedCache::MappedMachO& dylib) { + auto i = badDylibsCopy.find(dylib.runtimePath); + if ( i != badDylibsCopy.end()) { + rejected.push_back(std::make_pair(dylib, i->second)); + return true; + } + else { + return false; + } + }), dylibsToCache.end()); + } + + return badDylibs.empty(); +} +#endif + +void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const +{ + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset); + const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount]; + for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) { + handler((char*)this + m->fileOffset, m->address, m->size, m->initProt); + } +} + +void DyldSharedCache::forEachImage(void (^handler)(const mach_header* mh, const char* installName)) const +{ + const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset); + if ( mappings[0].fileOffset != 0 ) + return; + uint64_t firstImageOffset = 0; + uint64_t firstRegionAddress = mappings[0].address; + for (uint32_t i=0; i < header.imagesCount; ++i) { + const char* dylibPath = (char*)this + dylibs[i].pathFileOffset; + uint64_t offset = dylibs[i].address - firstRegionAddress; + if ( firstImageOffset == 0 ) + firstImageOffset = offset; + // skip over aliases + if ( dylibs[i].pathFileOffset < firstImageOffset) + continue; + const mach_header* mh = (mach_header*)((char*)this + offset); + handler(mh, dylibPath); + } +} + +void DyldSharedCache::forEachImageEntry(void (^handler)(const char* path, uint64_t mTime, uint64_t inode)) const +{ + const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset); + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset); + if ( mappings[0].fileOffset != 0 ) + return; + uint64_t firstImageOffset = 0; + uint64_t firstRegionAddress = mappings[0].address; + for (uint32_t i=0; i < header.imagesCount; ++i) { + const char* dylibPath = (char*)this + dylibs[i].pathFileOffset; + uint64_t offset = dylibs[i].address - firstRegionAddress; + if ( firstImageOffset == 0 ) + firstImageOffset = offset; + // skip over aliases + if ( dylibs[i].pathFileOffset < firstImageOffset) + continue; + handler(dylibPath, dylibs[i].modTime, dylibs[i].inode); + } +} + +void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const +{ + // check for old cache without imagesText array + if ( header.mappingOffset < 123 ) + return; + + // walk imageText table and call callback for each entry + const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)this + header.imagesTextOffset); + const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header.imagesTextCount]; + for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) { + handler(p->loadAddress, p->textSegmentSize, p->uuid, (char*)this + p->pathOffset); + } +} + + +std::string DyldSharedCache::archName() const +{ + const char* archSubString = ((char*)this) + 8; + while (*archSubString == ' ') + ++archSubString; + return archSubString; +} + + +uint32_t DyldSharedCache::platform() const +{ + return header.platform; +} + +#if !DYLD_IN_PROCESS +std::string DyldSharedCache::mapFile() const +{ + __block std::string result; + __block std::vector regionStartAddresses; + __block std::vector regionSizes; + __block std::vector regionFileOffsets; + + result.reserve(256*1024); + forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + regionStartAddresses.push_back(vmAddr); + regionSizes.push_back(size); + regionFileOffsets.push_back((uint8_t*)content - (uint8_t*)this); + char lineBuffer[256]; + const char* prot = "RW"; + if ( permissions == (VM_PROT_EXECUTE|VM_PROT_READ) ) + prot = "EX"; + else if ( permissions == VM_PROT_READ ) + prot = "RO"; + if ( size > 1024*1024 ) + sprintf(lineBuffer, "mapping %s %4lluMB 0x%0llX -> 0x%0llX\n", prot, size/(1024*1024), vmAddr, vmAddr+size); + else + sprintf(lineBuffer, "mapping %s %4lluKB 0x%0llX -> 0x%0llX\n", prot, size/1024, vmAddr, vmAddr+size); + result += lineBuffer; + }); + + // TODO: add linkedit breakdown + result += "\n\n"; + + forEachImage(^(const mach_header* mh, const char* installName) { + result += std::string(installName) + "\n"; + dyld3::MachOParser parser(mh); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + char lineBuffer[256]; + sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", segName, vmAddr, vmAddr+vmSize); + result += lineBuffer; + }); + result += "\n"; + }); + + return result; +} +#endif + + +uint64_t DyldSharedCache::unslidLoadAddress() const +{ + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset); + return mappings[0].address; +} + +void DyldSharedCache::getUUID(uuid_t uuid) const +{ + memcpy(uuid, header.uuid, sizeof(uuid_t)); +} + +uint64_t DyldSharedCache::mappedSize() const +{ + __block uint64_t startAddr = 0; + __block uint64_t endAddr = 0; + forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if ( startAddr == 0 ) + startAddr = vmAddr; + uint64_t end = vmAddr+size; + if ( end > endAddr ) + endAddr = end; + }); + return (endAddr - startAddr); +} + + + + + + + + + + diff --git a/dyld/dyld3/shared-cache/DyldSharedCache.h b/dyld/dyld3/shared-cache/DyldSharedCache.h new file mode 100644 index 0000000..2d84065 --- /dev/null +++ b/dyld/dyld3/shared-cache/DyldSharedCache.h @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2017 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 DyldSharedCache_h +#define DyldSharedCache_h + +#include +#include +#include +#include + +#include "dyld_cache_format.h" +#include "Diagnostics.h" +#include "MachOParser.h" + + +namespace dyld3 { + namespace launch_cache { + namespace binary_format { + struct Closure; + struct ImageGroup; + struct Image; + } + } +} + + +class VIS_HIDDEN DyldSharedCache +{ +public: + + enum CodeSigningDigestMode + { + SHA256only = 0, + SHA1only = 1, + Agile = 2 + }; + + struct CreateOptions + { + std::string archName; + dyld3::Platform platform; + bool excludeLocalSymbols; + bool optimizeStubs; + bool optimizeObjC; + CodeSigningDigestMode codeSigningDigestMode; + bool agileSignatureChooseSHA256CdHash; + bool dylibsRemovedDuringMastering; + bool inodesAreSameAsRuntime; + bool cacheSupportsASLR; + bool forSimulator; + bool verbose; + bool evictLeafDylibsOnOverflow; + std::unordered_map dylibOrdering; + std::unordered_map dirtyDataSegmentOrdering; + std::vector pathPrefixes; + std::string loggingPrefix; + }; + + struct MappedMachO + { + MappedMachO() + : mh(nullptr), length(0), isSetUID(false), protectedBySIP(false), sliceFileOffset(0), modTime(0), inode(0) { } + MappedMachO(const std::string& path, const mach_header* p, size_t l, bool isu, bool sip, uint64_t o, uint64_t m, uint64_t i) + : runtimePath(path), mh(p), length(l), isSetUID(isu), protectedBySIP(sip), sliceFileOffset(o), modTime(m), inode(i) { } + + std::string runtimePath; + const mach_header* mh; + size_t length; + uint64_t isSetUID : 1, + protectedBySIP : 1, + sliceFileOffset : 62; + uint64_t modTime; // only recorded if inodesAreSameAsRuntime + uint64_t inode; // only recorded if inodesAreSameAsRuntime + }; + + struct CreateResults + { + const DyldSharedCache* cacheContent = nullptr; // caller needs to vm_deallocate() when done + size_t cacheLength = 0; + std::string errorMessage; + std::set warnings; + bool agileSignature = false; + std::string cdHashFirst; + std::string cdHashSecond; + bool overflowed = false; + }; + + + // This function verifies the set of dylibs that will go into the cache are self contained. That the depend on no dylibs + // outset the set. It will call back the loader function to try to find any mising dylibs. + static bool verifySelfContained(std::vector& dylibsToCache, MappedMachO (^loader)(const std::string& runtimePath), std::vector>>& excluded); + + + // + // This function is single threaded and creates a shared cache. The cache file is created in-memory. + // + // Inputs: + // options: various per-platform flags + // dylibsToCache: a list of dylibs to include in the cache + // otherOsDylibs: a list of other OS dylibs and bundle which should have load info added to the cache + // osExecutables: a list of main executables which should have closures created in the cache + // + // Returns: + // On success: + // cacheContent: start of the allocated cache buffer which must be vm_deallocated after the caller writes out the buffer. + // cacheLength: size of the allocated cache buffer + // cdHash: hash of the code directory of the code blob of the created cache + // warnings: all warning messsages generated during the creation of the cache + // + // On failure: + // cacheContent: nullptr + // errorMessage: the string describing why the cache could not be created + // warnings: all warning messsages generated before the failure + // + static CreateResults create(const CreateOptions& options, + const std::vector& dylibsToCache, + const std::vector& otherOsDylibs, + const std::vector& osExecutables); + + + // + // Returns a text "map" file as a big string + // + std::string mapFile() const; + + + // + // Returns the architecture name of the shared cache, e.g. "arm64" + // + std::string archName() const; + + + // + // Returns the platform the cache is for + // + uint32_t platform() const; + + + // + // Iterates over each dylib in the cache + // + void forEachImage(void (^handler)(const mach_header* mh, const char* installName)) const; + + + // + // Iterates over each dylib in the cache + // + void forEachImageEntry(void (^handler)(const char* path, uint64_t mTime, uint64_t inode)) const; + + + // + // Iterates over each dylib in the cache + // + void forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName)) const; + + + // + // Iterates over each of the three regions in the cache + // + void forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const; + + + // + // returns address the cache would load at if unslid + // + uint64_t unslidLoadAddress() const; + + + // + // returns UUID of cache + // + void getUUID(uuid_t uuid) const; + + + // + // returns the vm size required to map cache + // + uint64_t mappedSize() const; + + + dyld_cache_header header; +}; + + + + + + + + +#endif /* DyldSharedCache_h */ diff --git a/dyld/dyld3/shared-cache/FileAbstraction.hpp b/dyld/dyld3/shared-cache/FileAbstraction.hpp new file mode 100644 index 0000000..1d73d74 --- /dev/null +++ b/dyld/dyld3/shared-cache/FileAbstraction.hpp @@ -0,0 +1,163 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, (uint32_t)value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+3) & ~(T)3; } + template + static T round_down(T value) { return value & ~(T)3; } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+7) & ~(T)7; } + template + static T round_down(T value) { return value & ~(T)7; } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/dyld/dyld3/shared-cache/FileUtils.cpp b/dyld/dyld3/shared-cache/FileUtils.cpp new file mode 100644 index 0000000..19f02a4 --- /dev/null +++ b/dyld/dyld3/shared-cache/FileUtils.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "FileUtils.h" +#include "Diagnostics.h" + +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 +extern "C" int rootless_check_trusted_fd(int fd) __attribute__((weak_import)); +#endif + + +void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path, bool (^dirFilter)(const std::string& path), void (^fileCallback)(const std::string& path, const struct stat&), bool processFiles) +{ + std::string fullDirPath = pathPrefix + path; + DIR* dir = ::opendir(fullDirPath.c_str()); + if ( dir == nullptr ) { + //fprintf(stderr, "can't read 'dir '%s', errno=%d\n", inputPath.c_str(), errno); + return; + } + while (dirent* entry = readdir(dir)) { + struct stat statBuf; + std::string dirAndFile = path + "/" + entry->d_name; + std::string fullDirAndFile = pathPrefix + dirAndFile; + switch ( entry->d_type ) { + case DT_REG: + if ( processFiles ) { + if ( ::lstat(fullDirAndFile.c_str(), &statBuf) == -1 ) + break; + if ( ! S_ISREG(statBuf.st_mode) ) + break; + fileCallback(dirAndFile, statBuf); + } + break; + case DT_DIR: + if ( strcmp(entry->d_name, ".") == 0 ) + break; + if ( strcmp(entry->d_name, "..") == 0 ) + break; + if ( dirFilter(dirAndFile) ) + break; + iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback); + break; + case DT_LNK: + // don't follow symlinks, dylib will be found through absolute path + break; + } + } + ::closedir(dir); +} + + +bool safeSave(const void* buffer, size_t bufferLen, const std::string& path) +{ + std::string pathTemplate = path + "-XXXXXX"; + size_t templateLen = strlen(pathTemplate.c_str())+2; + char pathTemplateSpace[templateLen]; + strlcpy(pathTemplateSpace, pathTemplate.c_str(), templateLen); + int fd = mkstemp(pathTemplateSpace); + if ( fd != -1 ) { + ssize_t writtenSize = pwrite(fd, buffer, bufferLen, 0); + if ( writtenSize == bufferLen ) { + ::fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); // mkstemp() makes file "rw-------", switch it to "rw-r--r--" + if ( ::rename(pathTemplateSpace, path.c_str()) == 0) { + ::close(fd); + return true; // success + } + } + ::close(fd); + ::unlink(pathTemplateSpace); + } + return false; // failure +} + +const void* mapFileReadOnly(const std::string& path, size_t& mappedSize) +{ + struct stat statBuf; + if ( ::stat(path.c_str(), &statBuf) != 0 ) + return nullptr; + + int fd = ::open(path.c_str(), O_RDONLY); + if ( fd < 0 ) + return nullptr; + + const void *p = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + ::close(fd); + if ( p != MAP_FAILED ) { + mappedSize = (size_t)statBuf.st_size; + return p; + } + + return nullptr; +} + +static bool sipIsEnabled() +{ + static bool rootlessEnabled; + static dispatch_once_t onceToken; + // Check to make sure file system protections are on at all + dispatch_once(&onceToken, ^{ + rootlessEnabled = (csr_check(CSR_ALLOW_UNRESTRICTED_FS) != 0); + }); + return rootlessEnabled; +} + +bool isProtectedBySIP(const std::string& path) +{ + if ( !sipIsEnabled() ) + return false; + + return (rootless_check_trusted(path.c_str()) == 0); +} + +bool isProtectedBySIP(int fd) +{ + if ( !sipIsEnabled() ) + return false; + +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 + return (rootless_check_trusted_fd(fd) == 0); +#else + // fallback to using rootless_check_trusted + char realPath[MAXPATHLEN]; + if ( fcntl(fd, F_GETPATH, realPath) == 0 ) + return (rootless_check_trusted(realPath) == 0); + return false; +#endif +} + +bool fileExists(const std::string& path) +{ + struct stat statBuf; + return ( ::stat(path.c_str(), &statBuf) == 0 ); +} + +// There is an order file specifying the order in which dylibs are laid out in +// general, as well as an order file specifying the order in which __DATA_DIRTY +// segments are laid out in particular. +// +// The syntax is one dylib (install name) per line. Blank lines are ignored. +// Comments start with the # character. +std::unordered_map loadOrderFile(const std::string& orderFile) { + std::unordered_map order; + + std::ifstream myfile(orderFile); + if ( myfile.is_open() ) { + uint32_t count = 0; + std::string line; + while ( std::getline(myfile, line) ) { + size_t pos = line.find('#'); + if ( pos != std::string::npos ) + line.resize(pos); + while ( !line.empty() && isspace(line.back()) ) { + line.pop_back(); + } + if ( !line.empty() ) + order[line] = count++; + } + myfile.close(); + } + + return order; +} + + +std::string toolDir() +{ + char buffer[PATH_MAX]; + uint32_t bufsize = PATH_MAX; + int result = _NSGetExecutablePath(buffer, &bufsize); + if ( result == 0 ) { + std::string path = buffer; + size_t pos = path.rfind('/'); + if ( pos != std::string::npos ) + return path.substr(0,pos+1); + } + //warning("tool directory not found"); + return "/tmp/"; +} + +std::string basePath(const std::string& path) +{ + std::string::size_type slash_pos = path.rfind("/"); + if (slash_pos != std::string::npos) { + slash_pos++; + return path.substr(slash_pos); + } else { + return path; + } +} + +std::string dirPath(const std::string& path) +{ + std::string::size_type slash_pos = path.rfind("/"); + if (slash_pos != std::string::npos) { + slash_pos++; + return path.substr(0, slash_pos); + } else { + char cwd[MAXPATHLEN]; + (void)getcwd(cwd, MAXPATHLEN); + return cwd; + } +} + +std::string realPath(const std::string& path) +{ + char resolvedPath[PATH_MAX]; + if (realpath(dirPath(path).c_str(), &resolvedPath[0]) != nullptr) { + return std::string(resolvedPath) + "/" + basePath(path); + } else { + return ""; + } +} + +std::string realFilePath(const std::string& path) +{ + char resolvedPath[PATH_MAX]; + if ( realpath(path.c_str(), resolvedPath) != nullptr ) + return std::string(resolvedPath); + else + return ""; +} + + +std::string normalize_absolute_file_path(std::string path) { + std::vector components; + std::vector processed_components; + std::stringstream ss(path); + std::string retval; + std::string item; + + while (std::getline(ss, item, '/')) { + components.push_back(item); + } + + if (components[0] == ".") { + retval = "."; + } + + for (auto& component : components) { + if (component.empty() || component == ".") + continue; + else if (component == ".." && processed_components.size()) + processed_components.pop_back(); + else + processed_components.push_back(component); + } + + for (auto & component : processed_components) { + retval = retval + "/" + component; + } + + return retval; +} + + +#if BUILDING_CACHE_BUILDER + +FileCache fileCache; + +FileCache::FileCache(void) +{ + cache_queue = dispatch_queue_create("com.apple.dyld.cache.cache", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0)); +} + +void FileCache::preflightCache(Diagnostics& diags, const std::unordered_set& paths) +{ + for (auto& path : paths) { + preflightCache(diags, path); + } +} + +void FileCache::preflightCache(Diagnostics& diags, const std::string& path) +{ + dispatch_async(cache_queue, ^{ + std::string normalizedPath = normalize_absolute_file_path(path); + if (entries.count(normalizedPath) == 0) { + entries[normalizedPath] = fill(diags, normalizedPath); + } + }); +} + +std::pair FileCache::cacheLoad(Diagnostics& diags, const std::string path) +{ + __block bool found = false; + __block std::pair retval; + std::string normalizedPath = normalize_absolute_file_path(path); + dispatch_sync(cache_queue, ^{ + auto entry = entries.find(normalizedPath); + if (entry != entries.end()) { + retval = entry->second; + found = true; + } + }); + + if (!found) { + auto info = fill(diags, normalizedPath); + dispatch_sync(cache_queue, ^{ + auto entry = entries.find(normalizedPath); + if (entry != entries.end()) { + retval = entry->second; + } else { + retval = entries[normalizedPath] = info; + retval = info; + } + }); + } + + return retval; +} + +//FIXME error handling +std::pair FileCache::fill(Diagnostics& diags, const std::string& path) +{ + void* buffer_ptr = nullptr; + struct stat stat_buf; + struct statfs statfs_buf; + bool localcopy = true; + + int fd = ::open(path.c_str(), O_RDONLY, 0); + if (fd == -1) { + diags.verbose("can't open file '%s', errno=%d\n", path.c_str(), errno); + return std::make_pair((uint8_t*)(-1), stat_buf); + } + + if (fstat(fd, &stat_buf) == -1) { + diags.verbose("can't stat open file '%s', errno=%d\n", path.c_str(), errno); + ::close(fd); + return std::make_pair((uint8_t*)(-1), stat_buf); + } + + if (stat_buf.st_size < 4096) { + diags.verbose("file too small '%s'\n", path.c_str()); + ::close(fd); + return std::make_pair((uint8_t*)(-1), stat_buf); + } + + if(fstatfs(fd, &statfs_buf) == 0) { + std::string fsName = statfs_buf.f_fstypename; + if (fsName == "hfs" || fsName == "apfs") { + localcopy = false; + } + } + + if (!localcopy) { + buffer_ptr = mmap(NULL, (size_t)stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buffer_ptr == MAP_FAILED) { + diags.verbose("mmap() for file at %s failed, errno=%d\n", path.c_str(), errno); + ::close(fd); + return std::make_pair((uint8_t*)(-1), stat_buf); + } + } else { + buffer_ptr = malloc((size_t)stat_buf.st_size); + ssize_t readBytes = pread(fd, buffer_ptr, (size_t)stat_buf.st_size, 0); + if (readBytes == -1) { + diags.verbose("Network read for file at %s failed, errno=%d\n", path.c_str(), errno); + ::close(fd); + return std::make_pair((uint8_t*)(-1), stat_buf); + } else if (readBytes != stat_buf.st_size) { + diags.verbose("Network read udnerrun for file at %s, expected %lld bytes, got %zd bytes\n", path.c_str(), stat_buf.st_size, readBytes); + ::close(fd); + return std::make_pair((uint8_t*)(-1), stat_buf); + } + } + + ::close(fd); + + return std::make_pair((uint8_t*)buffer_ptr, stat_buf); +} + +#endif // BUILDING_CACHE_BUILDER + diff --git a/dyld/dyld3/shared-cache/FileUtils.h b/dyld/dyld3/shared-cache/FileUtils.h new file mode 100644 index 0000000..dbf6ae4 --- /dev/null +++ b/dyld/dyld3/shared-cache/FileUtils.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2017 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 FileUtils_h +#define FileUtils_h + +#include + +#include +#include +#include +#include +#include + +class Diagnostics; + +#if BUILDING_CACHE_BUILDER +struct FileCache { + FileCache(void); + std::pair cacheLoad(Diagnostics& diags, const std::string path); + void preflightCache(Diagnostics& diags, const std::string& path); + void preflightCache(Diagnostics& diags, const std::unordered_set& paths); + +private: + std::pair fill(Diagnostics& diags, const std::string& path); + + std::unordered_map> entries; + dispatch_queue_t cache_queue; +}; + +extern FileCache fileCache; +#endif + +// +// recursively walk all files in a directory tree +// symlinks are ignored +// dirFilter should return true on directories which should not be recursed into +// callback is called on each regular file found with stat() info about the file +// +void iterateDirectoryTree(const std::string& pathPrefix, const std::string& path, bool (^dirFilter)(const std::string& dirPath), + void (^callback)(const std::string& path, const struct stat& statBuf), bool processFiles=true); + + +// +// writes the buffer to a temp file, then renames the file to the final path +// returns true on success +// +bool safeSave(const void* buffer, size_t bufferLen, const std::string& path); + + +const void* mapFileReadOnly(const std::string& path, size_t& mappedSize); + +bool isProtectedBySIP(const std::string& path); +bool isProtectedBySIP(int fd); + +bool fileExists(const std::string& path); + +std::unordered_map loadOrderFile(const std::string& orderFile); + +std::string normalize_absolute_file_path(std::string path); +std::string basePath(const std::string& path); +std::string dirPath(const std::string& path); +std::string realPath(const std::string& path); +std::string realFilePath(const std::string& path); + +std::string toolDir(); + + + + +#endif // FileUtils_h diff --git a/dyld/dyld3/shared-cache/ImageProxy.cpp b/dyld/dyld3/shared-cache/ImageProxy.cpp new file mode 100644 index 0000000..fcd6db5 --- /dev/null +++ b/dyld/dyld3/shared-cache/ImageProxy.cpp @@ -0,0 +1,2366 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ImageProxy.h" +#include "FileUtils.h" +#include "StringUtils.h" +#include "MachOParser.h" +#include "LaunchCacheFormat.h" +#include "LaunchCacheWriter.h" +#include "PathOverrides.h" +#include "libdyldEntryVector.h" + +namespace dyld3 { + +typedef launch_cache::TargetSymbolValue TargetSymbolValue; + + + +/////////////////////////// ImageProxy /////////////////////////// + +ImageProxy::ImageProxy(const mach_header* mh, const BinaryImageData* imageData, uint32_t indexInGroup, bool dyldCacheIsRaw) + : _mh(mh), _sliceFileOffset(0), _modTime(0), _inode(0), _imageBinaryData(imageData), _runtimePath(launch_cache::Image(imageData).path()), + _groupNum(0), _indexInGroup(indexInGroup), _isSetUID(false), _dyldCacheIsRaw(dyldCacheIsRaw), _platformBinary(false), _overrideOf(ImageRef::weakImportMissing()), + _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false), + _invalid(launch_cache::Image(imageData).isInvalid()), _staticallyReferenced(false), _cwdMustBeThisDir(false) +{ +} + +ImageProxy::ImageProxy(const DyldSharedCache::MappedMachO& mapping, uint32_t groupNum, uint32_t indexInGroup, bool dyldCacheIsRaw) + : _mh(mapping.mh), _sliceFileOffset(mapping.sliceFileOffset), _modTime(mapping.modTime), _inode(mapping.inode), _imageBinaryData(nullptr), _runtimePath(mapping.runtimePath), + _groupNum(groupNum), _indexInGroup(indexInGroup), _isSetUID(mapping.isSetUID), _dyldCacheIsRaw(dyldCacheIsRaw), _platformBinary(mapping.protectedBySIP), + _overrideOf(ImageRef::weakImportMissing()), _directDependentsSet(false), _deepDependentsSet(false), _initBeforesArraySet(false), _initBeforesComputed(false), + _invalid(false), _staticallyReferenced(false), _cwdMustBeThisDir(false) +{ +} + + +void ImageProxy::processRPaths(ImageProxyGroup& owningGroup) +{ + // parse LC_RPATH + __block std::unordered_set rawRpaths; + MachOParser parser(_mh, _dyldCacheIsRaw); + parser.forEachRPath(^(const char* rpath, bool& stop) { + if ( rawRpaths.count(rpath) ) { + _diag.warning("duplicate LC_RPATH (%s) in %s", rpath, _runtimePath.c_str()); + return; + } + rawRpaths.insert(rpath); + std::string thisRPath = rpath; + if ( startsWith(thisRPath, "@executable_path/") ) { + std::string mainPath = owningGroup.mainProgRuntimePath(); + if ( mainPath.empty() && parser.isDynamicExecutable() ) { + mainPath = _runtimePath; + } + if ( !mainPath.empty() ) { + std::string newPath = mainPath.substr(0, mainPath.rfind('/')+1) + thisRPath.substr(17); + std::string normalizedPath = owningGroup.normalizedPath(newPath); + if ( fileExists(normalizedPath) ) + _rpaths.push_back(normalizedPath); + else + _diag.warning("LC_RPATH to nowhere (%s) in %s", rpath, _runtimePath.c_str()); + char resolvedMainPath[PATH_MAX]; + if ( (realpath(mainPath.c_str(), resolvedMainPath) != nullptr) && (mainPath.c_str() != resolvedMainPath) ) { + std::string realMainPath = resolvedMainPath; + size_t lastSlashPos = realMainPath.rfind('/'); + std::string newRealPath = realMainPath.substr(0, lastSlashPos+1) + thisRPath.substr(17); + if ( realMainPath != mainPath ) { + for (const std::string& pre : owningGroup._buildTimePrefixes) { + std::string aPath = owningGroup.normalizedPath(pre + newRealPath); + if ( fileExists(aPath) ) { + _rpaths.push_back(owningGroup.normalizedPath(newRealPath)); + } + } + } + } + } + else { + _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str()); + } + } + else if ( thisRPath == "@executable_path" ) { + std::string mainPath = owningGroup.mainProgRuntimePath(); + if ( mainPath.empty() && parser.isDynamicExecutable() ) { + mainPath = _runtimePath; + } + if ( !mainPath.empty() ) { + std::string newPath = mainPath.substr(0, mainPath.rfind('/')+1); + std::string normalizedPath = owningGroup.normalizedPath(newPath); + _rpaths.push_back(normalizedPath); + } + else { + _diag.warning("LC_RPATH uses @executable_path in %s", _runtimePath.c_str()); + } + } + else if ( startsWith(thisRPath, "@loader_path/") ) { + size_t lastSlashPos = _runtimePath.rfind('/'); + std::string newPath = _runtimePath.substr(0, lastSlashPos+1) + thisRPath.substr(13); + bool found = false; + for (const std::string& pre : owningGroup._buildTimePrefixes) { + std::string aPath = owningGroup.normalizedPath(pre + newPath); + if ( fileExists(aPath) ) { + _rpaths.push_back(owningGroup.normalizedPath(newPath)); + found = true; + break; + } + } + char resolvedPath[PATH_MAX]; + if ( (realpath(_runtimePath.c_str(), resolvedPath) != nullptr) && (_runtimePath.c_str() != resolvedPath) ) { + std::string realRunPath = resolvedPath; + lastSlashPos = realRunPath.rfind('/'); + std::string newRealPath = realRunPath.substr(0, lastSlashPos+1) + thisRPath.substr(13); + if ( newRealPath != newPath ) { + for (const std::string& pre : owningGroup._buildTimePrefixes) { + std::string aPath = owningGroup.normalizedPath(pre + newRealPath); + if ( fileExists(aPath) ) { + _rpaths.push_back(owningGroup.normalizedPath(newRealPath)); + found = true; + break; + } + } + } + } + if ( !found ) { + // even though this path does not exist, we need to add it to must-be-missing paths + // in case it shows up at launch time + _rpaths.push_back(owningGroup.normalizedPath(newPath)); + _diag.warning("LC_RPATH to nowhere (%s) in %s", rpath, _runtimePath.c_str()); + } + } + else if ( thisRPath == "@loader_path" ) { + size_t lastSlashPos = _runtimePath.rfind('/'); + std::string newPath = _runtimePath.substr(0, lastSlashPos+1); + std::string normalizedPath = owningGroup.normalizedPath(newPath); + _rpaths.push_back(normalizedPath); + } + else if ( rpath[0] == '@' ) { + _diag.warning("LC_RPATH with unknown @ variable (%s) in %s", rpath, _runtimePath.c_str()); + } + else { + if ( rpath[0] == '/' ) + _diag.warning("LC_RPATH is absolute path (%s) in %s", rpath, _runtimePath.c_str()); + _rpaths.push_back(rpath); + } + }); + //if ( !_rpaths.empty() ) { + // fprintf(stderr, "for %s\n", _runtimePath.c_str()); + // for (const std::string& p : _rpaths) + // fprintf(stderr, " %s\n", p.c_str()); + //} +} + +void ImageProxy::addDependentsDeep(ImageProxyGroup& owningGroup, RPathChain* prev, bool staticallyReferenced) +{ + // mark binaries that are statically referenced and thus will never be unloaded + if ( staticallyReferenced ) + _staticallyReferenced = true; + + if ( _deepDependentsSet ) + return; + + // find all immediate dependents + addDependentsShallow(owningGroup, prev); + if ( _diag.hasError() ) { + _invalid = true; + return; + } + + // recurse though each dependent + RPathChain rchain = { this, prev, _rpaths }; + for (ImageProxy* proxy : _dependents) { + if ( proxy == nullptr ) + continue; // skip over weak missing dependents + if ( !proxy->_directDependentsSet ) + proxy->addDependentsDeep(owningGroup, &rchain, staticallyReferenced); + if ( proxy->invalid() ) + _invalid = true; + } + + _deepDependentsSet = true; +} + +void ImageProxy::addDependentsShallow(ImageProxyGroup& owningGroup, RPathChain* prev) +{ + if ( _directDependentsSet ) + return; + + MachOParser thisParser(mh(), _dyldCacheIsRaw); + dyld3::Platform thisPlatform = thisParser.platform(); + + processRPaths(owningGroup); + __block RPathChain rchain = { this, prev, _rpaths }; + + thisParser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + if ( (loadPath[0] != '/') && (loadPath[0] != '@') ) { + _diag.warning("load path is file system relative (%s) in %s", loadPath, runtimePath().c_str()); + } + Diagnostics depDiag; + ImageProxy* dep = owningGroup.findImage(depDiag, loadPath, isWeak, &rchain); + if ( (dep == nullptr) || dep->invalid() ) { + if (isWeak) { + // weak link against a broken dylib, pretend dylib is not there + dep = nullptr; + } else { + if ( depDiag.warnings().empty() ) { + if ( thisParser.header()->filetype == MH_EXECUTE ) + _diag.error("required dylib '%s' not found", loadPath); + else + _diag.error("required dylib '%s' not found, needed by '%s'", loadPath, runtimePath().c_str()); + } + else { + std::string allTries; + for (const std::string& warn : depDiag.warnings()) { + if ( allTries.empty() ) + allTries = warn; + else + allTries = allTries + ", " + warn; + } + _diag.error("required dylib '%s' not found, needed by '%s'. Did try: %s", loadPath, runtimePath().c_str(), allTries.c_str()); + } + } + } + else { + MachOParser depParser(dep->mh(), _dyldCacheIsRaw); + if ( _diag.noError() ) { + // verify found image has compatible version and matching platform + dyld3::Platform depPlatform = depParser.platform(); + if ( depPlatform != thisPlatform ) { + // simulator allows a few macOS libSystem dylibs + if ( !inLibSystem() || !dep->inLibSystem() ) { + _diag.error("found '%s' but it was built for different platform '%s' than required '%s'. Needed by '%s'", dep->runtimePath().c_str(), + MachOParser::platformName(depPlatform).c_str(), MachOParser::platformName(thisPlatform).c_str(), runtimePath().c_str()); + } + } + } + if ( _diag.noError() ) { + // verify compat version + const char* installName; + uint32_t foundCompatVers; + uint32_t foundCurrentVers; + if ( depParser.header()->filetype != MH_DYLIB ) { + _diag.error("found '%s' which is not a dylib. Needed by '%s'", dep->runtimePath().c_str(), runtimePath().c_str()); + } + else { + depParser.getDylibInstallName(&installName, &foundCompatVers, &foundCurrentVers); + if ( foundCompatVers < compatVersion ) { + _diag.error("found '%s' which has compat version (%s) which is less than required (%s). Needed by '%s'", dep->runtimePath().c_str(), + MachOParser::versionString(foundCompatVers).c_str(), MachOParser::versionString(compatVersion).c_str(), runtimePath().c_str()); + } + } + } + } + if ( _diag.hasError() ) { + stop = true; + _invalid = true; + } + _dependents.push_back(dep); + if ( isWeak ) + _dependentsKind.push_back(launch_cache::Image::LinkKind::weak); + else if ( isReExport ) + _dependentsKind.push_back(launch_cache::Image::LinkKind::reExport); + else if ( isUpward ) + _dependentsKind.push_back(launch_cache::Image::LinkKind::upward); + else + _dependentsKind.push_back(launch_cache::Image::LinkKind::regular); + }); + _directDependentsSet = true; +} + +bool ImageProxy::inLibSystem() const +{ + return startsWith(runtimePath(), "/usr/lib/system/") || startsWith(runtimePath(), "/usr/lib/libSystem."); +} + +void ImageProxy::forEachDependent(void (^handler)(ImageProxy* dep, LinkKind)) const +{ + for (int i=0; i < _dependents.size(); ++i) { + handler(_dependents[i], _dependentsKind[i]); + } +} + + +bool ImageProxy::findExportedSymbol(Diagnostics& diag, const char* symbolName, MachOParser::FoundSymbol& foundInfo) const +{ + MachOParser parser(_mh, _dyldCacheIsRaw); + return parser.findExportedSymbol(diag, symbolName, (void*)this, foundInfo, ^(uint32_t depIndex, const char* depLoadPath, void* extra, const mach_header** foundMH, void** foundExtra) { + ImageProxy* proxy = (ImageProxy*)extra; + if ( depIndex < proxy->_dependents.size() ) { + ImageProxy* depProxy = proxy->_dependents[depIndex]; + *foundMH = depProxy->_mh; + *foundExtra = (void*)depProxy; + return true; + } + return false; + }); +} + +bool ImageProxy::InitOrderInfo::beforeHas(ImageRef ref) +{ + ImageRef clearRef = ref; + clearRef.clearKind(); + return ( std::find(initBefore.begin(), initBefore.end(), clearRef) != initBefore.end() ); +} + +bool ImageProxy::InitOrderInfo::upwardHas(ImageProxy* proxy) +{ + return ( std::find(danglingUpward.begin(), danglingUpward.end(), proxy) != danglingUpward.end() ); +} + +void ImageProxy::InitOrderInfo::removeRedundantUpwards() +{ + danglingUpward.erase(std::remove_if(danglingUpward.begin(), danglingUpward.end(), + [&](ImageProxy* proxy) { + ImageRef ref(0, proxy->_groupNum, proxy->_indexInGroup); + return beforeHas(ref); + }), danglingUpward.end()); +} + + +// +// Every image has a list of "init-before" which means if that image was dlopen()ed +// here is the exact list of images to initialize in the exact order. This makes +// the runtime easy. It just walks the init-before list in order and runs each +// initializer if it has not already been run. +// +// The init-before list for each image is calculated based on the init-before list +// of each of its dependents. It simply starts with the list of its first dependent, +// then appends the list of the next, removing entries already in the list, etc. +// Lastly if the current image has an initializer, it is appended to its init-before list. +// +// To handle cycles, when recursing to get a dependent's init-before list, any image +// whose list is still being calculated (cycle), just returns its list so far. +// +// Explicit upward links are handled in two parts. First, in the algorithm described above, +// all upward links are ignored, which works fine as long as anything upward linked is +// downward linked at some point. If not, it is called a "dangling upward link". Since +// nothing depends on those, they are added to the end of the final init-before list. +// + +void ImageProxy::recursiveBuildInitBeforeInfo(ImageProxyGroup& owningGroup) +{ + if ( _initBeforesComputed ) + return; + _initBeforesComputed = true; // break cycles + + if ( _imageBinaryData != nullptr ) { + assert(_groupNum == 0); + // if this is proxy for something in dyld cache, get its list from cache + // and parse list into befores and upwards + launch_cache::Image image(_imageBinaryData); + image.forEachInitBefore(^(launch_cache::binary_format::ImageRef ref) { + if ( (LinkKind)ref.kind() == LinkKind::upward ) { + ImageProxyGroup* groupP = &owningGroup; + while (groupP->_groupNum != 0) + groupP = groupP->_nextSearchGroup; + launch_cache::ImageGroup dyldCacheGroup(groupP->_basedOn); + launch_cache::Image dyldCacheImage = dyldCacheGroup.image(ref.indexInGroup()); + Diagnostics diag; + ImageProxy* p = groupP->findAbsoluteImage(diag, dyldCacheImage.path(), false, false); + if ( diag.noError() ) + _initBeforesInfo.danglingUpward.push_back(p); + } + else { + _initBeforesInfo.initBefore.push_back(ref); + } + }); + } + else { + // calculate init-before list for this image by merging init-before's of all its dependent dylibs + unsigned depIndex = 0; + for (ImageProxy* depProxy : _dependents) { + if ( depProxy == nullptr ) { + assert(_dependentsKind[depIndex] == LinkKind::weak); + } + else { + if ( _dependentsKind[depIndex] == LinkKind::upward ) { + // if this upward link is already in the list, we ignore it. Otherwise add to front of list + if ( _initBeforesInfo.upwardHas(depProxy) ) { + // already in upward list, do nothing + } + else { + ImageRef ref(0, depProxy->_groupNum, depProxy->_indexInGroup); + if ( _initBeforesInfo.beforeHas(ref) ) { + // already in before list, do nothing + } + else { + // add to upward list + _initBeforesInfo.danglingUpward.push_back(depProxy); + } + } + } + else { + // compute init-befores of downward dependents + depProxy->recursiveBuildInitBeforeInfo(owningGroup); + // merge befores from this downward link into current befores list + for (ImageRef depInit : depProxy->_initBeforesInfo.initBefore) { + if ( !_initBeforesInfo.beforeHas(depInit) ) + _initBeforesInfo.initBefore.push_back(depInit); + } + // merge upwards from this downward link into current befores list + for (ImageProxy* upProxy : depProxy->_initBeforesInfo.danglingUpward) { + ImageRef ref(0, upProxy->_groupNum, upProxy->_indexInGroup); + if ( _initBeforesInfo.beforeHas(ref) ) { + // already in current initBefore list, so ignore this upward + } + else if ( _initBeforesInfo.upwardHas(upProxy) ) { + // already in current danglingUpward list, so ignore this upward + } + else { + // append to current danglingUpward list + _initBeforesInfo.danglingUpward.push_back(upProxy); + } + } + } + } + ++depIndex; + } + // eliminate any upward links added to befores list by some other dependent + _initBeforesInfo.removeRedundantUpwards(); + + // if this images has initializer(s) (or +load), add it to list + MachOParser parser(_mh, _dyldCacheIsRaw); + Diagnostics diag; + if ( parser.hasInitializer(diag) || parser.hasPlusLoadMethod(diag) ) { + launch_cache::binary_format::ImageRef ref(0, _groupNum, _indexInGroup); + _initBeforesInfo.initBefore.push_back(ref); + } + + //fprintf(stderr, "info for (%d, %d) %s\n", _group, _index, _runtimePath.c_str()); + //for (ImageRef ref : _initBeforesInfo.initBefore) + // fprintf(stderr, " ref = {%d, %d, %d}\n", ref.kind(), ref.group(), ref.index()); + //for (ImageProxy* p : _initBeforesInfo.danglingUpward) + // fprintf(stderr, " up = %s\n", p->runtimePath().c_str()); + } +} + +void ImageProxy::convertInitBeforeInfoToArray(ImageProxyGroup& owningGroup) +{ + if ( _initBeforesInfo.danglingUpward.empty() ) { + _initBeforesArray = _initBeforesInfo.initBefore; + } + else { + for (ImageRef ref : _initBeforesInfo.initBefore) + _initBeforesArray.push_back(ref); + bool inLibSys = inLibSystem(); + for (ImageProxy* proxy : _initBeforesInfo.danglingUpward) { + // ignore upward dependendencies between stuff within libSystem.dylib + if ( inLibSys && proxy->inLibSystem() ) + continue; + proxy->getInitBeforeList(owningGroup); + for (ImageRef depInit : proxy->_initBeforesInfo.initBefore) { + if ( std::find(_initBeforesArray.begin(), _initBeforesArray.end(), depInit) == _initBeforesArray.end() ) + _initBeforesArray.push_back(depInit); + } + ImageRef ref(0, proxy->_groupNum, proxy->_indexInGroup); + if ( std::find(_initBeforesArray.begin(), _initBeforesArray.end(), ref) == _initBeforesArray.end() ) + _initBeforesArray.push_back(ref); + } + } + //fprintf(stderr, "final init-before info for %s\n", _runtimePath.c_str()); + //for (ImageRef ref : _initBeforesArray) { + // fprintf(stderr, " ref = {%d, %d, %d}\n", ref.linkKind, ref.group, ref.index); + //} +} + +const std::vector& ImageProxy::getInitBeforeList(ImageProxyGroup& owningGroup) +{ + if ( !_initBeforesArraySet ) { + _initBeforesArraySet = true; // break cycles + recursiveBuildInitBeforeInfo(owningGroup); + convertInitBeforeInfoToArray(owningGroup); + } + return _initBeforesArray; +} + +ImageProxy::FixupInfo ImageProxy::buildFixups(Diagnostics& diag, uint64_t cacheUnslideBaseAddress, launch_cache::ImageGroupWriter& groupWriter) const +{ + __block ImageProxy::FixupInfo info; + MachOParser image(_mh, _dyldCacheIsRaw); + + // add fixup for each rebase + __block bool rebaseError = false; + image.forEachRebase(diag, ^(uint32_t segIndex, uint64_t segOffset, uint8_t type, bool& stop) { + dyld3::launch_cache::ImageGroupWriter::FixupType fixupType = launch_cache::ImageGroupWriter::FixupType::rebase; + switch ( type ) { + case REBASE_TYPE_POINTER: + fixupType = launch_cache::ImageGroupWriter::FixupType::rebase; + break; + case REBASE_TYPE_TEXT_ABSOLUTE32: + fixupType = launch_cache::ImageGroupWriter::FixupType::rebaseText; + info.hasTextRelocs = true; + break; + case REBASE_TYPE_TEXT_PCREL32: + diag.error("pcrel text rebasing not supported"); + stop = true; + rebaseError = true; + break; + default: + diag.error("unknown rebase type"); + stop = true; + rebaseError = true; + break; + } + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeInvalid()}); + //fprintf(stderr, "rebase: segIndex=%d, segOffset=0x%0llX, type=%d\n", segIndex, segOffset, type); + }); + if ( diag.hasError() ) + return FixupInfo(); + + // add fixup for each bind + image.forEachBind(diag, ^(uint32_t segIndex, uint64_t segOffset, uint8_t type, int libOrdinal, + uint64_t addend, const char* symbolName, bool weakImport, bool lazy, bool& stop) { + launch_cache::ImageGroupWriter::FixupType fixupType; + switch ( type ) { + case BIND_TYPE_POINTER: + if ( lazy ) + fixupType = launch_cache::ImageGroupWriter::FixupType::pointerLazyBind; + else + fixupType = launch_cache::ImageGroupWriter::FixupType::pointerBind; + break; + case BIND_TYPE_TEXT_ABSOLUTE32: + fixupType = launch_cache::ImageGroupWriter::FixupType::bindText; + info.hasTextRelocs = true; + break; + case BIND_TYPE_TEXT_PCREL32: + fixupType = launch_cache::ImageGroupWriter::FixupType::bindTextRel; + info.hasTextRelocs = true; + break; + case BIND_TYPE_IMPORT_JMP_REL32: + fixupType = launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel; + break; + default: + diag.error("malformed executable, unknown bind type (%d)", type); + stop = true; + return; + } + const ImageProxy* depProxy = nullptr; + bool isWeakDylib = false; + if ( libOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { + // -bundle_loader symbols cannot be bound ahead of time, we must look them up at load time + uint32_t imagePathPoolOffset = groupWriter.addString("@main"); + uint32_t imageSymbolPoolOffset = groupWriter.addString(symbolName); + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset, imageSymbolPoolOffset, weakImport)}); + return; + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP ) { + // -dynamic_lookup symbols cannot be bound ahead of time, we must look them up at load time + uint32_t imagePathPoolOffset = groupWriter.addString("@flat"); + uint32_t imageSymbolPoolOffset = groupWriter.addString(symbolName); + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeDynamicGroupValue(imagePathPoolOffset, imageSymbolPoolOffset, weakImport)}); + return; + } + else if ( libOrdinal == BIND_SPECIAL_DYLIB_SELF ) { + depProxy = this; + } + else if ( (libOrdinal >= 1) && (libOrdinal <= _dependents.size()) ) { + isWeakDylib = (_dependentsKind[libOrdinal-1] == LinkKind::weak); + depProxy = _dependents[libOrdinal-1]; + } + else { + diag.error("ordinal %d not supported", libOrdinal); + stop = true; + return; + } + if ( depProxy != nullptr ) { + MachOParser::FoundSymbol foundInfo; + if ( depProxy->findExportedSymbol(diag, symbolName, foundInfo) ) { + MachOParser implDylib(foundInfo.foundInDylib, _dyldCacheIsRaw); + switch ( foundInfo.kind ) { + case MachOParser::FoundSymbol::Kind::headerOffset: + case MachOParser::FoundSymbol::Kind::resolverOffset: + if ( implDylib.inDyldCache() ) { + uint32_t cacheOffset = (uint32_t)(implDylib.preferredLoadAddress() + foundInfo.value - cacheUnslideBaseAddress + addend); + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeSharedCacheOffset(cacheOffset)}); + } + else { + ImageProxy* foundProxy = (ImageProxy*)(foundInfo.foundExtra); + bool isIndirectGroupNum = foundProxy->_groupNum >= 128; + uint32_t groupNum = isIndirectGroupNum ? groupWriter.addIndirectGroupNum(foundProxy->_groupNum) : foundProxy->_groupNum; + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeGroupValue(groupNum, foundProxy->_indexInGroup, foundInfo.value+addend, isIndirectGroupNum)}); + } + break; + case MachOParser::FoundSymbol::Kind::absolute: + if (((((intptr_t)(foundInfo.value+addend)) << 2) >> 2) != (foundInfo.value+addend)) { + diag.error("absolute value %lld not supported", foundInfo.value+addend); + stop = true; + return; + } + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(foundInfo.value+addend)}); + break; + } + } + else { + if ( !weakImport ) { + diag.error("symbol '%s' not found, expected in '%s'", symbolName, depProxy->runtimePath().c_str()); + stop = true; + } + // mark fixup needs to set fixup location to zero + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)}); + } + } + else { + if ( isWeakDylib ) { + // dylib not found and is weak, set pointers into it to zero + info.fixups.push_back({segIndex, segOffset, fixupType, TargetSymbolValue::makeAbsolute(0)}); + } + else { + diag.error("dylib ordinal %d not found and not weak", libOrdinal); + stop = true; + } + } + }); + if ( diag.hasError() ) + return FixupInfo(); + + uint32_t weakDefPathPoolOffset = groupWriter.addString("@weak_def"); + image.forEachWeakDef(diag, ^(bool strongDef, uint32_t segIndex, uint64_t segOffset, uint64_t addend, const char* symbolName, bool& stop) { + if ( strongDef ) + return; + // find fixup for that location and change it to be a @weakdef dynamic target + bool altered = false; + for (FixUp& fixup : info.fixups) { + if ( (fixup.segOffset == segOffset) && (fixup.segIndex == segIndex) ) { + uint32_t symbolPoolOffset = groupWriter.addString(symbolName); + fixup.type = launch_cache::ImageGroupWriter::FixupType::pointerBind; + fixup.target = TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset, symbolPoolOffset, false); + altered = true; + } + } + if ( !altered ) { + if ( image.isSlideable() ) { + fprintf(stderr, "weak def for %s can't find underlying rebase/bind in %s\n", symbolName, runtimePath().c_str()); + } + else { + // no-pie executable does not have rebase for weak-def fixup, so add fixup + uint32_t symbolPoolOffset = groupWriter.addString(symbolName); + info.fixups.push_back({segIndex, segOffset, launch_cache::ImageGroupWriter::FixupType::pointerBind, TargetSymbolValue::makeDynamicGroupValue(weakDefPathPoolOffset, symbolPoolOffset, false)} ); + } + } + + }); + + return info; +} + + +void ImageProxy::setOverrideOf(uint32_t groupNum, uint32_t indexInGroup) +{ + _overrideOf = ImageRef(0, groupNum, indexInGroup); +} + + +static bool alreadyInList(const std::vector& imageList, ImageProxy* image) +{ + for (ImageProxy* proxy : imageList) { + if ( proxy == image ) + return true; + } + return false; +} + +void ImageProxy::addToFlatLookup(std::vector& imageList) +{ + // add all images shallow + bool addedSomething = false; + for (ImageProxy* dep : _dependents) { + if ( dep == nullptr ) + continue; + if ( !alreadyInList(imageList, dep) ) { + imageList.push_back(dep); + addedSomething = true; + } + } + // recurse + if ( addedSomething ) { + for (ImageProxy* dep : _dependents) { + if ( dep == nullptr ) + continue; + dep->addToFlatLookup(imageList); + } + } +} + + +/////////////////////////// ImageProxyGroup /////////////////////////// + + +class StringPool +{ +public: + uint32_t add(const std::string& str); + size_t size() const { return _buffer.size(); } + const char* buffer() const { return &_buffer[0]; } + void align(); +private: + std::vector _buffer; + std::unordered_map _existingEntries; +}; + +uint32_t StringPool::add(const std::string& str) +{ + auto pos = _existingEntries.find(str); + if ( pos != _existingEntries.end() ) + return pos->second; + size_t len = str.size() + 1; + size_t offset = _buffer.size(); + _buffer.insert(_buffer.end(), &str[0], &str[len]); + _existingEntries[str] = (uint32_t)offset; + assert(offset < 0xFFFF); + return (uint32_t)offset; +} + +void StringPool::align() +{ + while ( (_buffer.size() % 4) != 0 ) + _buffer.push_back('\0'); +} + +ImageProxyGroup::ImageProxyGroup(uint32_t groupNum, const DyldCacheParser& dyldCache, const launch_cache::binary_format::ImageGroup* basedOn, + ImageProxyGroup* next, const std::string& mainProgRuntimePath, + const std::vector& knownGroups, + const std::vector& buildTimePrefixes, + const std::vector& envVars, + bool stubsEliminated, bool dylibsExpectedOnDisk, bool inodesAreSameAsRuntime) + : _pathOverrides(envVars), _patchTable(nullptr), _basedOn(basedOn), _dyldCache(dyldCache), _nextSearchGroup(next), _groupNum(groupNum), + _stubEliminated(stubsEliminated), _dylibsExpectedOnDisk(dylibsExpectedOnDisk), _inodesAreSameAsRuntime(inodesAreSameAsRuntime), + _knownGroups(knownGroups), _buildTimePrefixes(buildTimePrefixes), _mainProgRuntimePath(mainProgRuntimePath), _platform(Platform::unknown) +{ + _archName = dyldCache.cacheHeader()->archName(); + _platform = (Platform)(dyldCache.cacheHeader()->platform()); +} + + +ImageProxyGroup::~ImageProxyGroup() +{ + for (DyldSharedCache::MappedMachO& mapping : _ownedMappings ) { + vm_deallocate(mach_task_self(), (vm_address_t)mapping.mh, mapping.length); + } + for (ImageProxy* proxy : _images) { + delete proxy; + } +} + + +std::string ImageProxyGroup::normalizedPath(const std::string& path) +{ + for (const std::string& prefix : _buildTimePrefixes) { + std::string fullPath = prefix + path; + if ( fileExists(fullPath) ) { + if ( (fullPath.find("/../") != std::string::npos) || (fullPath.find("//") != std::string::npos) || (fullPath.find("/./") != std::string::npos) ) { + char resolvedPath[PATH_MAX]; + if ( realpath(fullPath.c_str(), resolvedPath) != nullptr ) { + std::string resolvedUnPrefixed = &resolvedPath[prefix.size()]; + return resolvedUnPrefixed; + } + } + break; + } + } + return path; +} + + +ImageProxy* ImageProxyGroup::findImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, ImageProxy::RPathChain* rChain) +{ + __block ImageProxy* result = nullptr; + _pathOverrides.forEachPathVariant(runtimeLoadPath.c_str(), _platform, ^(const char* possiblePath, bool& stop) { + if ( startsWith(possiblePath, "@rpath/") ) { + std::string trailing = &possiblePath[6]; + for (const ImageProxy::RPathChain* cur=rChain; cur != nullptr; cur = cur->prev) { + for (const std::string& rpath : cur->rpaths) { + std::string aPath = rpath + trailing; + result = findAbsoluteImage(diag, aPath, true, false); + if ( result != nullptr ) { + _pathToProxy[runtimeLoadPath] = result; + stop = true; + return; + } + } + } + // if cannot be found via current stack of rpaths, check if already found + auto pos = _pathToProxy.find(possiblePath); + if ( pos != _pathToProxy.end() ) { + result = pos->second; + stop = true; + return; + } + } + else if ( startsWith(possiblePath, "@loader_path/") ) { + std::string loaderFile = rChain->inProxy->runtimePath(); + size_t lastSlash = loaderFile.rfind('/'); + if ( lastSlash != std::string::npos ) { + std::string loaderDir = loaderFile.substr(0, lastSlash); + std::string newPath = loaderDir + &possiblePath[12]; + result = findAbsoluteImage(diag, newPath, canBeMissing, false); + if ( result != nullptr ) { + _pathToProxy[runtimeLoadPath] = result; + stop = true; + return; + } + } + } + else if ( startsWith(possiblePath, "@executable_path/") ) { + for (const ImageProxy::RPathChain* cur=rChain; cur != nullptr; cur = cur->prev) { + if ( cur->inProxy->mh()->filetype == MH_EXECUTE ) { + std::string mainProg = cur->inProxy->runtimePath(); + size_t lastSlash = mainProg.rfind('/'); + if ( lastSlash != std::string::npos ) { + std::string mainDir = mainProg.substr(0, lastSlash); + std::string newPath = mainDir + &possiblePath[16]; + result = findAbsoluteImage(diag, newPath, canBeMissing, false); + if ( result != nullptr ) { + _pathToProxy[runtimeLoadPath] = result; + stop = true; + return; + } + } + } + } + } + else { + // load command is full path to dylib + result = findAbsoluteImage(diag, possiblePath, canBeMissing, false); + if ( result != nullptr ) { + stop = true; + return; + } + } + }); + + // when building closure, check if an added dylib is an override for something in the cache + if ( (result != nullptr) && (_groupNum > 1) && !result->isProxyForCachedDylib() ) { + for (ImageProxyGroup* grp = this; grp != nullptr; grp = grp->_nextSearchGroup) { + if ( grp->_basedOn == nullptr ) + continue; + uint32_t indexInGroup; + launch_cache::ImageGroup imageGroup(grp->_basedOn); + if ( imageGroup.findImageByPath(runtimeLoadPath.c_str(), indexInGroup) ) { + result->setOverrideOf(imageGroup.groupNum(), indexInGroup); + break; + } + } + } + + return result; +} + + +bool ImageProxyGroup::builtImageStillValid(const launch_cache::Image& image) +{ + // only do checks when running on system + if ( _buildTimePrefixes.size() != 1 ) + return true; + if ( _buildTimePrefixes.front().size() != 0 ) + return true; + if ( _platform != MachOParser::currentPlatform() ) + return true; + + struct stat statBuf; + bool expectedOnDisk = image.group().dylibsExpectedOnDisk(); + bool overridableDylib = image.overridableDylib(); + bool cachedDylib = !image.isDiskImage(); + bool fileFound = ( ::stat(image.path(), &statBuf) == 0 ); + + if ( cachedDylib ) { + if ( expectedOnDisk ) { + if ( fileFound ) { + // macOS case: verify dylib file info matches what it was when cache was built + return ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) ); + } + else { + // macOS case: dylib missing + return false; + } + } + else { + if ( fileFound ) { + if ( overridableDylib ) { + // iOS case: internal install with dylib root + return false; + } + else { + // iOS case: customer install, ignore dylib on disk + return true; + } + } + else { + // iOS case: cached dylib not on disk as expected + return true; + } + } + } + else { + if ( fileFound ) { + if ( image.validateUsingModTimeAndInode() ) { + // macOS case: verify dylib file info matches what it was when cache was built + return ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) ); + } + else { + // FIXME: need to verify file cdhash + return true; + } + } + else { + // dylib not on disk as expected + return false; + } + } +} + +ImageProxy* ImageProxyGroup::findAbsoluteImage(Diagnostics& diag, const std::string& runtimeLoadPath, bool canBeMissing, bool makeErrorMessage, bool pathIsAlreadyReal) +{ + auto pos = _pathToProxy.find(runtimeLoadPath); + if ( pos != _pathToProxy.end() ) + return pos->second; + + // see if this ImageProxyGroup is a proxy for an ImageGroup from the dyld shared cache + if ( _basedOn != nullptr ) { + uint32_t foundIndex; + launch_cache::ImageGroup imageGroup(_basedOn); + if ( imageGroup.findImageByPath(runtimeLoadPath.c_str(), foundIndex) ) { + launch_cache::Image image = imageGroup.image(foundIndex); + if ( builtImageStillValid(image) ) { + ImageProxy* proxy = nullptr; + if ( _groupNum == 0 ) { + const mach_header* mh = (mach_header*)((uint8_t*)(_dyldCache.cacheHeader()) + image.cacheOffset()); + proxy = new ImageProxy(mh, image.binaryData(), foundIndex, _dyldCache.cacheIsMappedRaw()); + } + else { + DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(diag, runtimeLoadPath); + if ( mapping != nullptr ) { + proxy = new ImageProxy(*mapping, _groupNum, foundIndex, false); + } + } + if ( proxy != nullptr ) { + _pathToProxy[runtimeLoadPath] = proxy; + _images.push_back(proxy); + if ( runtimeLoadPath != image.path() ) { + // lookup path is an alias, add real path too + _pathToProxy[image.path()] = proxy; + } + return proxy; + } + } + } + } + + if ( _nextSearchGroup != nullptr ) { + ImageProxy* result = _nextSearchGroup->findAbsoluteImage(diag, runtimeLoadPath, true, false); + if ( result != nullptr ) + return result; + } + + // see if this is a symlink to a dylib + if ( !pathIsAlreadyReal ) { + for (const std::string& prefix : _buildTimePrefixes) { + std::string fullPath = prefix + runtimeLoadPath; + if ( endsWith(prefix, "/") ) + fullPath = prefix.substr(0, prefix.size()-1) + runtimeLoadPath; + if ( fileExists(fullPath) ) { + std::string resolvedPath = realFilePath(fullPath); + if ( !resolvedPath.empty() && (resolvedPath!= fullPath) ) { + std::string resolvedRuntimePath = resolvedPath.substr(prefix.size()); + ImageProxy* proxy = findAbsoluteImage(diag, resolvedRuntimePath, true, false, true); + if ( proxy != nullptr ) + return proxy; + } + } + } + } + + if ( (_groupNum >= 2) && (_basedOn == nullptr) ) { + if ( (runtimeLoadPath[0] != '/') && (runtimeLoadPath[0] != '@') ) { + for (ImageProxy* aProxy : _images) { + if ( endsWith(aProxy->runtimePath(), runtimeLoadPath) ) { + aProxy->setCwdMustBeThisDir(); + return aProxy; + } + } + } + + DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(diag, runtimeLoadPath); + if ( mapping != nullptr ) { + ImageProxy* proxy = new ImageProxy(*mapping, _groupNum, (uint32_t)_images.size(), false); + _pathToProxy[runtimeLoadPath] = proxy; + _images.push_back(proxy); + return proxy; + } + } + + if ( !canBeMissing && makeErrorMessage ) { + if ( diag.warnings().empty() ) { + if ( diag.hasError() ) { + std::string orgMsg = diag.errorMessage(); + diag.error("'%s' %s", runtimeLoadPath.c_str(), orgMsg.c_str()); + } + else { + diag.error("could not find '%s'", runtimeLoadPath.c_str()); + } + } + else { + std::string allTries; + for (const std::string& warn : diag.warnings()) { + if ( allTries.empty() ) + allTries = warn; + else + allTries = allTries + ", " + warn; + } + diag.clearWarnings(); + diag.error("could not use '%s'. Did try: %s", runtimeLoadPath.c_str(), allTries.c_str()); + } + } + + // record locations not found so it can be verified they are still missing at runtime + _mustBeMissingFiles.insert(runtimeLoadPath); + + return nullptr; +} + + +DyldSharedCache::MappedMachO* ImageProxyGroup::addMappingIfValidMachO(Diagnostics& diag, const std::string& runtimePath, bool ignoreMainExecutables) +{ + bool fileFound = false; + for (const std::string& prefix : _buildTimePrefixes) { + std::string fullPath = prefix + runtimePath; + struct stat statBuf; + if ( stat(fullPath.c_str(), &statBuf) != 0 ) + continue; + fileFound = true; + // map whole file and determine if it is mach-o or a fat file + int fd = ::open(fullPath.c_str(), O_RDONLY); + if ( fd < 0 ) { + diag.warning("file not open()able '%s' errno=%d", fullPath.c_str(), errno); + continue; + } + size_t len = (size_t)statBuf.st_size; + size_t offset = 0; + const void* p = ::mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if ( p != MAP_FAILED ) { + size_t sliceLen; + size_t sliceOffset; + bool missingSlice; + Diagnostics fatDiag; + if ( FatUtil::isFatFileWithSlice(fatDiag, p, len, _archName, sliceOffset, sliceLen, missingSlice) ) { + // unmap whole file + ::munmap((void*)p, len); + // remap just slice + p = ::mmap(NULL, sliceLen, PROT_READ, MAP_PRIVATE, fd, sliceOffset); + if ( p != MAP_FAILED ) { + offset = sliceOffset; + len = sliceLen; + } + } + else if ( fatDiag.hasError() ) { + diag.warning("%s", fatDiag.errorMessage().c_str()); + } + if ( (p != MAP_FAILED) && !missingSlice && MachOParser::isValidMachO(diag, _archName, _platform, p, len, fullPath, ignoreMainExecutables) ) { + bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID)); + bool sip = false; // FIXME + _ownedMappings.emplace_back(runtimePath, (mach_header*)p, len, issetuid, sip, offset, statBuf.st_mtime, statBuf.st_ino); + ::close(fd); + return &_ownedMappings.back(); + } + else if (p != MAP_FAILED) { + ::munmap((void*)p, len); + } + } + ::close(fd); + } + if ( !fileFound ) + diag.warning("file not found '%s'", runtimePath.c_str()); + + return nullptr; +} + +static bool dontExamineDir(const std::string& dirPath) +{ + return endsWith(dirPath, ".app") || endsWith(dirPath, ".xctoolchain") || endsWith(dirPath, ".sdk") || endsWith(dirPath, ".platform"); +} + +void ImageProxyGroup::addExtraMachOsInBundle(const std::string& appDir) +{ + iterateDirectoryTree("", appDir, ^(const std::string& dirPath) { return dontExamineDir(dirPath); }, ^(const std::string& path, const struct stat& statBuf) { + // ignore files that don't have 'x' bit set (all runnable mach-o files do) + const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH); + if ( !hasXBit ) + return; + + // ignore files too small + if ( statBuf.st_size < 0x1000 ) + return; + + // if the file is mach-o, add to list + if ( _pathToProxy.find(path) == _pathToProxy.end() ) { + Diagnostics machoDiag; + DyldSharedCache::MappedMachO* mapping = addMappingIfValidMachO(machoDiag, path, true); + if ( mapping != nullptr ) { + ImageProxy* proxy = new ImageProxy(*mapping, _groupNum, (uint32_t)_images.size(), false); + if ( proxy != nullptr ) { + _pathToProxy[path] = proxy; + _images.push_back(proxy); + } + } + } + }); +} + +// used when building dyld shared cache +ImageProxyGroup* ImageProxyGroup::makeDyldCacheDylibsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, + const std::vector& cachedDylibs, + const std::vector& buildTimePrefixes, + const PatchTable& patchTable, bool stubEliminated, bool dylibsExpectedOnDisk) +{ + std::vector emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used + std::vector noExistingGroups; + ImageProxyGroup* groupProxy = new ImageProxyGroup(0, dyldCache, nullptr, nullptr, "", noExistingGroups, buildTimePrefixes, emptyEnvVars, stubEliminated, dylibsExpectedOnDisk); + groupProxy->_patchTable = &patchTable; + + // add every dylib in shared cache to _images + uint32_t indexInGroup = 0; + for (const DyldSharedCache::MappedMachO& mapping : cachedDylibs) { + ImageProxy* proxy = new ImageProxy(mapping, 0, indexInGroup++, true); + groupProxy->_images.push_back(proxy); + groupProxy->_pathToProxy[mapping.runtimePath] = proxy; + } + + // verify libdyld is compatible + ImageRef libdyldEntryImageRef = ImageRef::makeEmptyImageRef(); + uint32_t libdyldEntryOffset; + groupProxy->findLibdyldEntry(diag, libdyldEntryImageRef, libdyldEntryOffset); + if ( diag.hasError() ) { + delete groupProxy; + return nullptr; + } + + // wire up dependents + bool hadError = false; + for (size_t i=0; i < groupProxy->_images.size(); ++i) { + // note: addDependentsShallow() can append to _images, so can't use regular iterator + ImageProxy* proxy = groupProxy->_images[i]; + proxy->addDependentsShallow(*groupProxy); + if ( proxy->diagnostics().hasError() ) { + hadError = true; + diag.copy(proxy->diagnostics()); + break; + } + } + + if ( hadError ) { + delete groupProxy; + return nullptr; + } + + return groupProxy; +} + + +// used when building dyld shared cache +ImageProxyGroup* ImageProxyGroup::makeOtherOsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup, + const std::vector& otherDylibsAndBundles, + bool inodesAreSameAsRuntime, const std::vector& buildTimePrefixes) +{ + std::vector emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used + const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup(); + std::vector existingGroups = { cachedDylibsGroupData }; + ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr, "", existingGroups, buildTimePrefixes, emptyEnvVars); + ImageProxyGroup* groupProxy = new ImageProxyGroup(1, dyldCache, nullptr, cachedDylibsGroup, "", existingGroups, buildTimePrefixes, emptyEnvVars, + false, true, inodesAreSameAsRuntime); + + // add every dylib/bundle in "other: list to _images + uint32_t indexInGroup = 0; + for (const DyldSharedCache::MappedMachO& mapping : otherDylibsAndBundles) { + ImageProxy* proxy = new ImageProxy(mapping, 1, indexInGroup++, false); + groupProxy->_images.push_back(proxy); + groupProxy->_pathToProxy[mapping.runtimePath] = proxy; + } + + // wire up dependents + for (size_t i=0; i < groupProxy->_images.size(); ++i) { + // note: addDependentsShallow() can append to _images, so can't use regular iterator + ImageProxy* proxy = groupProxy->_images[i]; + // note: other-dylibs can only depend on dylibs in this group or group 0, so no need for deep dependents + proxy->addDependentsShallow(*groupProxy); + if ( proxy->diagnostics().hasError() ) { + diag.warning("adding dependents to %s: %s", proxy->runtimePath().c_str(), proxy->diagnostics().errorMessage().c_str()); + proxy->markInvalid(); + } + } + // propagate invalidness + __block bool somethingInvalid; + do { + somethingInvalid = false; + for (ImageProxy* proxy : groupProxy->_images) { + proxy->forEachDependent(^(ImageProxy* dep, LinkKind) { + if ( (dep != nullptr) && dep->invalid() && !proxy->invalid()) { + proxy->markInvalid(); + somethingInvalid = true; + } + }); + } + } while (somethingInvalid); + + return groupProxy; +} + +// used by closured for dlopen of unknown dylibs +const BinaryImageGroupData* ImageProxyGroup::makeDlopenGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, uint32_t groupNum, + const std::vector& existingGroups, + const std::string& imagePath, const std::vector& envVars) +{ + const std::vector& noBuildTimePrefixes = {""}; + ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, existingGroups[0], nullptr, "", existingGroups, noBuildTimePrefixes, envVars); + ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, nullptr, &dyldCacheDylibProxyGroup, "", existingGroups, noBuildTimePrefixes, envVars); + ImageProxyGroup dlopenGroupProxy(groupNum, dyldCache, nullptr, &dyldCacheOtherProxyGroup, imagePath, existingGroups, noBuildTimePrefixes, envVars, false, true, true); + + DyldSharedCache::MappedMachO* topMapping = dlopenGroupProxy.addMappingIfValidMachO(diag, imagePath, true); + if ( topMapping == nullptr ) { + if ( diag.noError() ) { + const std::set& warnings = diag.warnings(); + if ( warnings.empty() ) + diag.error("no loadable mach-o in %s", imagePath.c_str()); + else + diag.error("%s", (*warnings.begin()).c_str()); + } + return nullptr; + } + + ImageProxy* topImageProxy = new ImageProxy(*topMapping, groupNum, 0, false); + if ( topImageProxy == nullptr ) { + diag.error("can't find slice matching dyld cache in %s", imagePath.c_str()); + return nullptr; + } + dlopenGroupProxy._images.push_back(topImageProxy); + dlopenGroupProxy._pathToProxy[imagePath] = topImageProxy; + + // add all dylibs needed by dylib and are not in dyld cache + topImageProxy->addDependentsDeep(dlopenGroupProxy, nullptr, false); + if ( topImageProxy->diagnostics().hasError() ) { + diag.copy(topImageProxy->diagnostics()); + return nullptr; + } + + const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag); + + return result; +} + + +// used when building dyld shared cache +BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup, + ImageProxyGroup* otherOsDylibs, const DyldSharedCache::MappedMachO& mainProgMapping, + bool inodesAreSameAsRuntime, const std::vector& buildTimePrefixes) +{ + // _basedOn can not be set until ImageGroup is built + if ( cachedDylibsGroup->_basedOn == nullptr ) { + cachedDylibsGroup->_basedOn = dyldCache.cachedDylibsGroup(); + } + const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup(); + const BinaryImageGroupData* otherDylibsGroupData = dyldCache.otherDylibsGroup(); + std::vector existingGroups = { cachedDylibsGroupData, otherDylibsGroupData }; + std::vector emptyEnvVars; // Note: this method only used when constructing dyld cache where envs are not used + ImageProxyGroup mainClosureGroupProxy(2, dyldCache, nullptr, otherOsDylibs, mainProgMapping.runtimePath, existingGroups, buildTimePrefixes, + emptyEnvVars, false, true, inodesAreSameAsRuntime); + + ImageProxy* mainProxy = new ImageProxy(mainProgMapping, 2, 0, false); + if ( mainProxy == nullptr ) { + diag.error("can't find slice matching dyld cache in %s", mainProgMapping.runtimePath.c_str()); + return nullptr; + } + mainClosureGroupProxy._images.push_back(mainProxy); + mainClosureGroupProxy._pathToProxy[mainProgMapping.runtimePath] = mainProxy; + + return mainClosureGroupProxy.makeClosureBinary(diag, mainProxy, false); +} + + +bool ImageProxyGroup::addInsertedDylibs(Diagnostics& diag) +{ + __block bool success = true; + _pathOverrides.forEachInsertedDylib(^(const char* dylibPath) { + ImageProxy* insertProxy = findAbsoluteImage(diag, dylibPath, false, true); + if ( insertProxy == nullptr ) + success = false; + }); + return success; +} + +static DyldCacheParser findDyldCache(Diagnostics& diag, const ClosureBuffer::CacheIdent& cacheIdent, task_t requestor, bool* dealloc) +{ + *dealloc = false; +#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) + size_t currentCacheSize; + const DyldSharedCache* currentCache = (const DyldSharedCache*)_dyld_get_shared_cache_range(¤tCacheSize); + if ( currentCache != nullptr ) { + uuid_t currentCacheUUID; + currentCache->getUUID(currentCacheUUID); + if ( memcmp(currentCacheUUID, cacheIdent.cacheUUID, 16) == 0 ) + return DyldCacheParser((const DyldSharedCache*)currentCache, false); + } +#endif + if ( requestor == mach_task_self() ) { + // handle dyld_closure_util case where -cache_file option maps raw cache file into this process + const DyldSharedCache* altCache = (DyldSharedCache*)cacheIdent.cacheAddress; + uuid_t altCacheUUID; + altCache->getUUID(altCacheUUID); + if ( memcmp(altCacheUUID, cacheIdent.cacheUUID, 16) == 0 ) + return DyldCacheParser(altCache, true); // only one cache can be mapped into process, so this must be raw + else + diag.error("dyld cache uuid has changed"); + } +#if BUILDING_CLOSURED + else { + // handle case where requestor to closured is running with a different dyld cache that closured + uint8_t cacheBuffer[4096]; + mach_vm_size_t actualReadSize = sizeof(cacheBuffer); + kern_return_t r; + r = mach_vm_read_overwrite(requestor, cacheIdent.cacheAddress, sizeof(cacheBuffer), (vm_address_t)&cacheBuffer, &actualReadSize); + if ( r != KERN_SUCCESS ) { + diag.error("unable to read cache header from requesting process (addr=0x%llX), kern err=%d", cacheIdent.cacheAddress, r); + return DyldCacheParser(nullptr, false); + } + const dyld_cache_header* header = (dyld_cache_header*)cacheBuffer; + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)(cacheBuffer + header->mappingOffset); + vm_address_t bufferAddress = 0; + r = vm_allocate(mach_task_self(), &bufferAddress, (long)cacheIdent.cacheMappedSize, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + diag.error("unable to allocate space to copy custom dyld cache (size=0x%llX), kern err=%d", cacheIdent.cacheMappedSize, r); + return DyldCacheParser(nullptr, false); + } + uint64_t slide = cacheIdent.cacheAddress - mappings[0].address; + for (int i=0; i < 3; ++i) { + mach_vm_address_t mappedAddress = bufferAddress + (mappings[i].address - mappings[0].address); + mach_vm_size_t mappedSize = mappings[i].size; + vm_prot_t curProt = VM_PROT_READ; + vm_prot_t maxProt = VM_PROT_READ; + r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, + requestor, mappings[i].address+slide, true, &curProt, &maxProt, VM_INHERIT_NONE); + if ( r != KERN_SUCCESS ) { + diag.error("unable to mach_vm_remap region %d custom dyld cache (request addr=0x%llX, size=0x%llX), kern err=%d, localBuffer=0x%lX, localMapTarget=0x%llX", + i, mappings[i].address+slide, mappedSize, r, (long)bufferAddress, mappedAddress); + return DyldCacheParser(nullptr, false); + } + if ( curProt != VM_PROT_READ ) + vm_protect(mach_task_self(), (long)mappedAddress, (long)mappedSize, false, VM_PROT_READ); + } + *dealloc = true; + return DyldCacheParser((DyldSharedCache*)bufferAddress, false); // assumes cache in other process is mapped as three regions + } +#endif + return DyldCacheParser(nullptr, false); +} + +BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector& buildTimePrefixes) +{ + // unpack buffer + bool deallocCacheCopy; + DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy); + if ( diag.hasError() ) + return nullptr; + const char* mainProg = buffer.targetPath(); + std::vector envVars; + int envCount = buffer.envVarCount(); + const char* envVarCStrings[envCount]; + buffer.copyImageGroups(envVarCStrings); + for (int i=0; i < envCount; ++i) { + envVars.push_back(envVarCStrings[i]); + } + + // make ImageProxyGroups: 0, 1, 2 + const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup(); + const BinaryImageGroupData* otherDylibsGroupData = dyldCache.otherDylibsGroup(); + std::vector realBuildTimePrefixes; + for (const std::string& prefix : buildTimePrefixes) { + char resolvedPath[PATH_MAX]; + if ( realpath(prefix.c_str(), resolvedPath) != nullptr ) + realBuildTimePrefixes.push_back(resolvedPath); + else + realBuildTimePrefixes.push_back(prefix); + } + std::vector existingGroups = { cachedDylibsGroupData, otherDylibsGroupData }; + ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr, "", existingGroups, realBuildTimePrefixes, envVars); + ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, otherDylibsGroupData, &dyldCacheDylibProxyGroup, "", existingGroups, realBuildTimePrefixes, envVars); + ImageProxyGroup mainClosureGroupProxy( 2, dyldCache, nullptr, &dyldCacheOtherProxyGroup, mainProg, existingGroups, realBuildTimePrefixes, envVars, false, true, true); + + // add any DYLD_INSERTED_LIBRARIES then main program into closure + BinaryClosureData* result = nullptr; + if ( mainClosureGroupProxy.addInsertedDylibs(diag) ) { + ImageProxy* proxy = mainClosureGroupProxy.findAbsoluteImage(diag, mainProg, false, true); + if ( proxy != nullptr ) { + // build closure + result = mainClosureGroupProxy.makeClosureBinary(diag, proxy, false); + } + } + + // if client has a different cache, unmap our copy + if ( deallocCacheCopy ) + vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize); + + return result; +} + +ClosureBuffer closured_CreateImageGroup(const ClosureBuffer& input) +{ + Diagnostics diag; + const BinaryImageGroupData* newGroup = ImageProxyGroup::makeDlopenGroup(diag, input, mach_task_self(), {""}); + + if ( diag.noError() ) { + // on success return the ImageGroup binary in the ClosureBuffer + dyld3::ClosureBuffer result(newGroup); + free((void*)newGroup); + return result; + } + else { + // on failure return the error message in the ClosureBuffer + dyld3::ClosureBuffer err(diag.errorMessage().c_str()); + return err; + } +} + +const BinaryImageGroupData* ImageProxyGroup::makeDlopenGroup(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector& buildTimePrefixes) +{ + // unpack buffer + bool deallocCacheCopy; + DyldCacheParser dyldCache = findDyldCache(diag, buffer.cacheIndent(), requestor, &deallocCacheCopy); + if ( diag.hasError() ) + return nullptr; + + const char* targetDylib = buffer.targetPath(); + std::vector envVars; + int envCount = buffer.envVarCount(); + const char* envVarCStrings[envCount]; + buffer.copyImageGroups(envVarCStrings); + for (int i=0; i < envCount; ++i) { + envVars.push_back(envVarCStrings[i]); + } + uint32_t groupCount = buffer.imageGroupCount() + 2; + const launch_cache::BinaryImageGroupData* groupDataPtrs[groupCount]; + groupDataPtrs[0] = dyldCache.cachedDylibsGroup(); + groupDataPtrs[1] = dyldCache.otherDylibsGroup(); + buffer.copyImageGroups(&groupDataPtrs[2]); + + // build an ImageProxyGroup for each existing group, and one for new group being constructed + std::vector existingGroups; + std::vector> proxies; + ImageProxyGroup* prevProxy = nullptr; + for (uint32_t i=0; i < groupCount; ++i) { + const launch_cache::BinaryImageGroupData* groupData = groupDataPtrs[i]; + existingGroups.push_back(groupData); + launch_cache::ImageGroup group(groupData); + uint32_t groupNum = group.groupNum(); + assert(groupNum == proxies.size()); + proxies.emplace_back(new ImageProxyGroup(groupNum, dyldCache, groupData, prevProxy, "", existingGroups, buildTimePrefixes, envVars)); + prevProxy = proxies.back().get(); + } + ImageProxyGroup dlopenGroupProxy(groupCount, dyldCache, nullptr, prevProxy, targetDylib, existingGroups, buildTimePrefixes, envVars); + + // find and mmap() top level dylib + DyldSharedCache::MappedMachO* topMapping = dlopenGroupProxy.addMappingIfValidMachO(diag, targetDylib, true); + if ( topMapping == nullptr ) { + std::string allWarnings; + for (const std::string& warn : diag.warnings()) { + if ( allWarnings.empty() ) + allWarnings = warn; + else + allWarnings = allWarnings + ", " + warn; + } + diag.clearWarnings(); + diag.error("%s", allWarnings.c_str()); + if ( deallocCacheCopy ) + vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize); + return nullptr; + } + + // make ImageProxy for top level dylib + ImageProxy* topImageProxy = new ImageProxy(*topMapping, groupCount, 0, false); + if ( topImageProxy == nullptr ) { + diag.error("can't find slice matching dyld cache in %s", targetDylib); + if ( deallocCacheCopy ) + vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize); + return nullptr; + } + dlopenGroupProxy._images.push_back(topImageProxy); + dlopenGroupProxy._pathToProxy[targetDylib] = topImageProxy; + + // add all dylibs needed by dylib and are not in dyld cache + topImageProxy->addDependentsDeep(dlopenGroupProxy, nullptr, false); + if ( topImageProxy->diagnostics().hasError() ) { + diag.copy(topImageProxy->diagnostics()); + if ( deallocCacheCopy ) + vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize); + return nullptr; + } + + // construct ImageGroup from ImageProxies + const BinaryImageGroupData* result = dlopenGroupProxy.makeImageGroupBinary(diag); + + // clean up + if ( deallocCacheCopy ) + vm_deallocate(mach_task_self(), (vm_address_t)dyldCache.cacheHeader(), (long)buffer.cacheIndent().cacheMappedSize); + + return result; +} + + + + +// Used by closured and dyld_closure_util +BinaryClosureData* ImageProxyGroup::makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, + const std::string& mainProg, bool includeDylibsInDir, + const std::vector& buildTimePrefixes, + const std::vector& envVars) +{ + const BinaryImageGroupData* cachedDylibsGroupData = dyldCache.cachedDylibsGroup(); + const BinaryImageGroupData* otherDylibsGroupData = dyldCache.otherDylibsGroup(); + std::vector realBuildTimePrefixes; + for (const std::string& prefix : buildTimePrefixes) { + char resolvedPath[PATH_MAX]; + if ( realpath(prefix.c_str(), resolvedPath) != nullptr ) + realBuildTimePrefixes.push_back(resolvedPath); + else + realBuildTimePrefixes.push_back(prefix); + } + std::vector existingGroups = { cachedDylibsGroupData, otherDylibsGroupData }; + ImageProxyGroup dyldCacheDylibProxyGroup(0, dyldCache, cachedDylibsGroupData, nullptr, "", existingGroups, realBuildTimePrefixes, envVars); + ImageProxyGroup dyldCacheOtherProxyGroup(1, dyldCache, otherDylibsGroupData, &dyldCacheDylibProxyGroup, "", existingGroups, realBuildTimePrefixes, envVars); + ImageProxyGroup mainClosureGroupProxy( 2, dyldCache, nullptr, &dyldCacheOtherProxyGroup, mainProg, existingGroups, realBuildTimePrefixes, envVars, false, true, true); + + // add any DYLD_INSERTED_LIBRARIES into closure + if ( !mainClosureGroupProxy.addInsertedDylibs(diag) ) + return nullptr; + + ImageProxy* proxy = mainClosureGroupProxy.findAbsoluteImage(diag, mainProg, false, true); + if ( proxy == nullptr ) + return nullptr; + + return mainClosureGroupProxy.makeClosureBinary(diag, proxy, includeDylibsInDir); +} + +const char* sSkipPrograms_macOS[] = { + "/Applications/iBooks.app/Contents/MacOS/iBooks", +}; + +const char* sSkipPrograms_embeddedOSes[] = { + "/sbin/launchd", + "/usr/local/sbin/launchd.debug", + "/usr/local/sbin/launchd.development" +}; + +BinaryClosureData* ImageProxyGroup::makeClosureBinary(Diagnostics& diag, ImageProxy* mainProgProxy, bool includeDylibsInDir) +{ + assert(mainProgProxy != nullptr); + assert(_images.size() >= 1); + + // check black list + if ( _platform == Platform::macOS ) { + for (const char* skipProg : sSkipPrograms_macOS) { + if ( mainProgProxy->runtimePath() == skipProg ) { + diag.error("black listed program"); + return nullptr; + } + } + } else { + for (const char* skipProg : sSkipPrograms_embeddedOSes) { + if ( mainProgProxy->runtimePath() == skipProg ) { + diag.error("black listed program"); + return nullptr; + } + } + } + + _mainExecutableIndex = (uint32_t)_images.size() - 1; + // add all dylibs needed by main excutable and are not in dyld cache + mainProgProxy->addDependentsDeep(*this, nullptr, true); + if ( mainProgProxy->diagnostics().hasError() ) { + diag.copy(mainProgProxy->diagnostics()); + return nullptr; + } + + // if main program is in .app bundle, look for other mach-o files to add to closure for use by dlopen + bool isAppMainExecutable = false; + std::string appDir; + std::string leafName = basePath(mainProgProxy->runtimePath()); + size_t posAppX = mainProgProxy->runtimePath().rfind(std::string("/") + leafName + ".appex/"); + size_t posApp = mainProgProxy->runtimePath().rfind(std::string("/") + leafName + ".app/"); + if ( posAppX != std::string::npos ) { + appDir = mainProgProxy->runtimePath().substr(0, posAppX+leafName.size()+7); + isAppMainExecutable = true; + } + else if ( posApp != std::string::npos ) { + appDir = mainProgProxy->runtimePath().substr(0, posApp+leafName.size()+5); + isAppMainExecutable = true; + } + if ( isAppMainExecutable ) { + addExtraMachOsInBundle(appDir); + for (size_t i=0; i < _images.size(); ++i) { + // note: addDependentsDeep() can append to _images, so can't use regular iterator + ImageProxy* aProxy = _images[i]; + ImageProxy::RPathChain base = { aProxy, nullptr, mainProgProxy->rpaths() }; + aProxy->addDependentsDeep(*this, &base, false); + if ( aProxy->diagnostics().hasError() ) { + aProxy->markInvalid(); + diag.warning("%s could not be added to closure because %s", aProxy->runtimePath().c_str(), aProxy->diagnostics().errorMessage().c_str()); + } + } + } + else if ( includeDylibsInDir ) { + size_t pos = mainProgProxy->runtimePath().rfind('/'); + if ( pos != std::string::npos ) { + std::string mainDir = mainProgProxy->runtimePath().substr(0, pos); + addExtraMachOsInBundle(mainDir); + for (size_t i=0; i < _images.size(); ++i) { + // note: addDependentsDeep() can append to _images, so can't use regular iterator + ImageProxy* aProxy = _images[i]; + aProxy->addDependentsDeep(*this, nullptr, false); + } + } + } + + // add addition dependents of any inserted libraries + if ( _mainExecutableIndex != 0 ) { + for (uint32_t i=0; i < _mainExecutableIndex; ++i) { + _images[i]->addDependentsDeep(*this, nullptr, true); + if ( _images[i]->diagnostics().hasError() ) + return nullptr; + } + } + + // gather warnings from all statically dependent images + for (ImageProxy* proxy : _images) { + if ( !proxy->staticallyReferenced() && proxy->diagnostics().hasError() ) + continue; + diag.copy(proxy->diagnostics()); + if ( diag.hasError() ) { + return nullptr; + } + } + + // get program entry + MachOParser mainExecutableParser(mainProgProxy->mh(), _dyldCache.cacheIsMappedRaw()); + bool usesCRT; + uint32_t entryOffset; + mainExecutableParser.getEntry(entryOffset, usesCRT); + + // build ImageGroupWriter + launch_cache::ImageGroupWriter groupWriter(_groupNum, mainExecutableParser.uses16KPages(), mainExecutableParser.is64(), _dylibsExpectedOnDisk, _inodesAreSameAsRuntime); + populateGroupWriter(diag, groupWriter); + if ( diag.hasError() ) + return nullptr; + + // pre-compute libSystem and libdyld into closure + ImageRef libdyldEntryImageRef = ImageRef::makeEmptyImageRef(); + uint32_t libdyldEntryOffset; + findLibdyldEntry(diag, libdyldEntryImageRef, libdyldEntryOffset); + if ( diag.hasError() ) + return nullptr; + ImageRef libSystemImageRef = ImageRef::makeEmptyImageRef(); + + findLibSystem(diag, mainExecutableParser.isSimulatorBinary(), libSystemImageRef); + if ( diag.hasError() ) + return nullptr; + + // build info about missing files and env vars + __block StringPool stringPool; + __block std::vector envVarOffsets; + std::vector missingFileComponentOffsets; + stringPool.add(" "); + for (const std::string& path : _mustBeMissingFiles) { + size_t start = 1; + size_t slashPos = path.find('/', start); + while (slashPos != std::string::npos) { + std::string component = path.substr(start, slashPos - start); + uint16_t offset = stringPool.add(component); + missingFileComponentOffsets.push_back(offset); + start = slashPos + 1; + slashPos = path.find('/', start); + } + std::string lastComponent = path.substr(start); + uint16_t offset = stringPool.add(lastComponent); + missingFileComponentOffsets.push_back(offset); + missingFileComponentOffsets.push_back(0); // mark end of a path + } + missingFileComponentOffsets.push_back(0); // mark end of all paths + if ( missingFileComponentOffsets.size() & 1 ) + missingFileComponentOffsets.push_back(0); // 4-byte align array + __block uint32_t envVarCount = 0; + _pathOverrides.forEachEnvVar(^(const char* envVar) { + envVarOffsets.push_back(stringPool.add(envVar)); + ++envVarCount; + }); + + // 4-byte align string pool size + stringPool.align(); + + // malloc a buffer and fill in ImageGroup part + uint32_t groupSize = groupWriter.size(); + uint32_t missingFilesArraySize = (uint32_t)((missingFileComponentOffsets.size()*sizeof(uint16_t) + 3) & (-4)); + uint32_t envVarsSize = (uint32_t)(envVarOffsets.size()*sizeof(uint32_t)); + uint32_t stringPoolSize = (uint32_t)stringPool.size(); + size_t allocSize = sizeof(launch_cache::binary_format::Closure) + + groupSize + + missingFilesArraySize + + envVarsSize + + stringPoolSize; + BinaryClosureData* clo = (BinaryClosureData*)malloc(allocSize); + groupWriter.finalizeTo(diag, _knownGroups, &clo->group); + launch_cache::ImageGroup cloGroup(&clo->group); + launch_cache::Image mainImage(cloGroup.imageBinary(_mainExecutableIndex)); + + uint32_t maxImageLoadCount = groupWriter.maxLoadCount(diag, _knownGroups, &clo->group); + + if ( mainImage.isInvalid() ) { + free((void*)clo); + diag.error("depends on invalid dylib"); + return nullptr; + } + + // fill in closure attributes + clo->magic = launch_cache::binary_format::Closure::magicV1; + clo->usesCRT = usesCRT; + clo->isRestricted = mainProgProxy->isSetUID() || mainExecutableParser.isRestricted(); + clo->usesLibraryValidation = mainExecutableParser.usesLibraryValidation(); + clo->padding = 0; + clo->missingFileComponentsOffset = offsetof(launch_cache::binary_format::Closure, group) + groupSize; + clo->dyldEnvVarsOffset = clo->missingFileComponentsOffset + missingFilesArraySize; + clo->dyldEnvVarsCount = envVarCount; + clo->stringPoolOffset = clo->dyldEnvVarsOffset + envVarsSize; + clo->stringPoolSize = stringPoolSize; + clo->libSystemRef = libSystemImageRef; + clo->libDyldRef = libdyldEntryImageRef; + clo->libdyldVectorOffset = libdyldEntryOffset; + clo->mainExecutableIndexInGroup = _mainExecutableIndex; + clo->mainExecutableEntryOffset = entryOffset; + clo->initialImageCount = maxImageLoadCount; + _dyldCache.cacheHeader()->getUUID(clo->dyldCacheUUID); + + if ( !mainExecutableParser.getCDHash(clo->mainExecutableCdHash) ) { + // if no code signature, fill in 16-bytes with UUID then 4 bytes of zero + bzero(clo->mainExecutableCdHash, 20); + mainExecutableParser.getUuid(clo->mainExecutableCdHash); + } + if ( missingFilesArraySize != 0 ) + memcpy((uint8_t*)clo + clo->missingFileComponentsOffset, &missingFileComponentOffsets[0], missingFileComponentOffsets.size()*sizeof(uint16_t)); + if ( envVarsSize != 0 ) + memcpy((uint8_t*)clo + clo->dyldEnvVarsOffset, &envVarOffsets[0], envVarsSize); + if ( stringPool.size() != 0 ) + memcpy((uint8_t*)clo + clo->stringPoolOffset, stringPool.buffer(), stringPool.size()); + + return clo; +} + +const BinaryImageGroupData* ImageProxyGroup::makeImageGroupBinary(Diagnostics& diag, const char* const neverEliminateStubs[]) +{ + const bool continueIfErrors = (_groupNum == 1); + bool uses16KPages = true; + bool is64 = true; + if ( !_images.empty() ) { + MachOParser firstParser(_images.front()->mh(), _dyldCache.cacheIsMappedRaw()); + uses16KPages = firstParser.uses16KPages(); + is64 = firstParser.is64(); + } + launch_cache::ImageGroupWriter groupWriter(_groupNum, uses16KPages, is64, _dylibsExpectedOnDisk, _inodesAreSameAsRuntime); + populateGroupWriter(diag, groupWriter, neverEliminateStubs); + if ( diag.hasError() ) + return nullptr; + + // malloc a buffer and fill in ImageGroup part + BinaryImageGroupData* groupData = (BinaryImageGroupData*)malloc(groupWriter.size()); + groupWriter.finalizeTo(diag, _knownGroups, groupData); + + if ( !continueIfErrors && groupWriter.isInvalid(0) ) { + free((void*)groupData); + diag.error("depends on invalid dylib"); + return nullptr; + } + + return groupData; +} + + +void ImageProxyGroup::findLibdyldEntry(Diagnostics& diag, ImageRef& ref, uint32_t& vmOffsetInLibDyld) +{ + Diagnostics libDyldDiag; + ImageProxy* libDyldProxy = findImage(libDyldDiag, "/usr/lib/system/libdyld.dylib", false, nullptr); + if ( libDyldProxy == nullptr ) { + diag.error("can't find libdyld.dylib"); + return; + } + ref = ImageRef(0, libDyldProxy->groupNum(), libDyldProxy->indexInGroup()); + + // find offset of "dyld3::entryVectorForDyld" in libdyld.dylib + Diagnostics entryDiag; + MachOParser::FoundSymbol dyldEntryInfo; + MachOParser libDyldParser(libDyldProxy->mh(), _dyldCache.cacheIsMappedRaw()); + if ( !libDyldParser.findExportedSymbol(entryDiag, "__ZN5dyld318entryVectorForDyldE", nullptr, dyldEntryInfo, nullptr) ) { + diag.error("can't find dyld entry point into libdyld.dylib"); + return; + } + vmOffsetInLibDyld = (uint32_t)dyldEntryInfo.value; + const LibDyldEntryVector* entry = (LibDyldEntryVector*)(libDyldParser.content(vmOffsetInLibDyld)); + if ( entry == nullptr ) { + diag.error("dyld entry point at offset 0x%0X not found in libdyld.dylib", vmOffsetInLibDyld); + return; + } + if ( entry->vectorVersion != LibDyldEntryVector::kCurrentVectorVersion ) + diag.error("libdyld.dylib vector version is incompatible with this dyld cache builder"); + else if ( entry->binaryFormatVersion != launch_cache::binary_format::kFormatVersion ) + diag.error("libdyld.dylib closures binary format version is incompatible with this dyld cache builder"); +} + +void ImageProxyGroup::findLibSystem(Diagnostics& diag, bool forSimulator, ImageRef& ref) +{ + Diagnostics libSysDiag; + ImageProxy* libSystemProxy = findImage(libSysDiag, forSimulator ? "/usr/lib/libSystem.dylib" : "/usr/lib/libSystem.B.dylib" , false, nullptr); + if ( libSystemProxy == nullptr ) { + diag.error("can't find libSystem.dylib"); + return; + } + ref = ImageRef(0, libSystemProxy->groupNum(), libSystemProxy->indexInGroup()); +} + + +std::vector ImageProxyGroup::flatLookupOrder() +{ + std::vector results; + // start with main executable and any inserted dylibs + for (uint32_t i=0; i <= _mainExecutableIndex; ++i) + results.push_back(_images[i]); + + // recursive add dependents of main executable + _images[_mainExecutableIndex]->addToFlatLookup(results); + + // recursive add dependents of any inserted dylibs + for (uint32_t i=0; i < _mainExecutableIndex; ++i) + _images[i]->addToFlatLookup(results); + + return results; +} + +void ImageProxyGroup::populateGroupWriter(Diagnostics& diag, launch_cache::ImageGroupWriter& groupWriter, const char* const neverEliminateStubs[]) +{ + const bool buildingDylibsInCache = (_groupNum == 0); + const bool continueIfErrors = (_groupNum == 1); + + std::unordered_set neverStubEliminate; + if ( neverEliminateStubs != nullptr ) { + for (const char* const* nes=neverEliminateStubs; *nes != nullptr; ++nes) + neverStubEliminate.insert(*nes); + } + + // pass 1: add all images + const uint64_t cacheUnslideBaseAddress = _dyldCache.cacheHeader()->unslidLoadAddress(); + const uint32_t imageCount = (uint32_t)_images.size(); + groupWriter.setImageCount(imageCount); + for (uint32_t i=0; i < imageCount; ++i) { + MachOParser imageParser(_images[i]->mh(), _dyldCache.cacheIsMappedRaw()); + assert((imageParser.inDyldCache() == buildingDylibsInCache) && "all images must be same type"); + // add info for each image + groupWriter.setImagePath(i, _images[i]->runtimePath().c_str()); + groupWriter.setImageIsBundle(i, (imageParser.fileType() == MH_BUNDLE)); + bool hasObjC = imageParser.hasObjC(); + groupWriter.setImageHasObjC(i, hasObjC); + bool isEncrypted = imageParser.isEncrypted(); + groupWriter.setImageIsEncrypted(i, isEncrypted); + bool mayHavePlusLoad = false; + if ( hasObjC ) { + mayHavePlusLoad = isEncrypted || imageParser.hasPlusLoadMethod(diag); + groupWriter.setImageMayHavePlusLoads(i, mayHavePlusLoad); + } + groupWriter.setImageHasWeakDefs(i, imageParser.hasWeakDefs()); + groupWriter.setImageMustBeThisDir(i, _images[i]->cwdMustBeThisDir()); + groupWriter.setImageIsPlatformBinary(i, _images[i]->isPlatformBinary()); + groupWriter.setImageOverridableDylib(i, !_stubEliminated || (neverStubEliminate.count(_images[i]->runtimePath()) != 0)); + uuid_t uuid; + if ( imageParser.getUuid(uuid) ) + groupWriter.setImageUUID(i, uuid); + if ( _inodesAreSameAsRuntime ) { + groupWriter.setImageFileMtimeAndInode(i, _images[i]->fileModTime(), _images[i]->fileInode()); + } + else { + uint8_t cdHash[20]; + if ( !imageParser.getCDHash(cdHash) ) + bzero(cdHash, 20); + // if image is not code signed, cdHash filled with all zeros + groupWriter.setImageCdHash(i, cdHash); + } + if ( !buildingDylibsInCache ) { + groupWriter.setImageSliceOffset(i, _images[i]->sliceFileOffset()); + uint32_t fairPlayTextOffset; + uint32_t fairPlaySize; + if ( imageParser.isFairPlayEncrypted(fairPlayTextOffset, fairPlaySize) ) + groupWriter.setImageFairPlayRange(i, fairPlayTextOffset, fairPlaySize); + uint32_t codeSigOffset; + uint32_t codeSigSize; + if ( imageParser.hasCodeSignature(codeSigOffset, codeSigSize) ) + groupWriter.setImageCodeSignatureLocation(i, codeSigOffset, codeSigSize); + } + groupWriter.setImageDependentsCount(i, imageParser.dependentDylibCount()); + // add segments to image + groupWriter.setImageSegments(i, imageParser, cacheUnslideBaseAddress); + // add initializers to image + __block std::vector initOffsets; + imageParser.forEachInitializer(diag, ^(uint32_t offset) { + initOffsets.push_back(offset); + }); + groupWriter.setImageInitializerOffsets(i, initOffsets); + if ( diag.hasError() && !continueIfErrors ) { + return; + } + // add DOFs to image + __block std::vector dofOffsets; + imageParser.forEachDOFSection(diag, ^(uint32_t offset) { + dofOffsets.push_back(offset); + }); + groupWriter.setImageDOFOffsets(i, dofOffsets); + if ( diag.hasError() && !continueIfErrors ) { + return; + } + bool neverUnload = false; + if ( buildingDylibsInCache ) + neverUnload = true; + if ( _images[i]->staticallyReferenced() ) + neverUnload = true; + if ( imageParser.hasObjC() && (imageParser.fileType() == MH_DYLIB) ) + neverUnload = true; + if ( imageParser.hasThreadLocalVariables() ) + neverUnload = true; + if ( !dofOffsets.empty() ) + neverUnload = true; + groupWriter.setImageNeverUnload(i, neverUnload); + if ( _images[i]->invalid() ) + groupWriter.setImageInvalid(i); + // record if this is an override of an OS dylib + ImageRef stdRef = _images[i]->overrideOf(); + if ( stdRef != ImageRef::weakImportMissing() ) { + ImageRef thisImageRef(0, _groupNum, i); + groupWriter.addImageIsOverride(stdRef, thisImageRef); + } + + // add alias if runtimepath does not match installName + if ( imageParser.fileType() == MH_DYLIB ) { + const char* installName = imageParser.installName(); + if ( installName[0] == '/' ) { + if ( _images[i]->runtimePath() != installName ) { + // add install name as an alias + groupWriter.addImageAliasPath(i, installName); + } + } + // IOKit.framework on embedded uses not flat bundle, but clients dlopen() it as if it were flat + if ( buildingDylibsInCache && (_platform != Platform::macOS) && (_images[i]->runtimePath() == "/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit") ) { + groupWriter.addImageAliasPath(i, "/System/Library/Frameworks/IOKit.framework/IOKit"); + } + } + } + + // pass 2: add all dependencies (now that we have indexes defined) + for (uint32_t i=0; (i < imageCount) && diag.noError(); ++i) { + // add dependents to image + __block uint32_t depIndex = 0; + _images[i]->forEachDependent(^(ImageProxy* dep, LinkKind kind) { + if ( dep == nullptr ) { + if ( kind == LinkKind::weak ) + groupWriter.setImageDependent(i, depIndex, launch_cache::binary_format::ImageRef::weakImportMissing()); + else + groupWriter.setImageInvalid(i); + } + else { + launch_cache::binary_format::ImageRef ref((uint8_t)kind, dep->groupNum(), dep->indexInGroup()); + groupWriter.setImageDependent(i, depIndex, ref); + } + ++depIndex; + }); + } + + // pass 3: invalidate any images dependent on invalid images) + if ( continueIfErrors ) { + const launch_cache::binary_format::ImageRef missingRef = launch_cache::binary_format::ImageRef::weakImportMissing(); + __block bool somethingInvalidated = false; + do { + somethingInvalidated = false; + for (uint32_t i=0; i < imageCount; ++i) { + if ( groupWriter.isInvalid(i) ) + continue; + uint32_t depCount = groupWriter.imageDependentsCount(i); + for (uint32_t depIndex=0; depIndex < depCount; ++depIndex) { + launch_cache::binary_format::ImageRef ref = groupWriter.imageDependent(i, depIndex); + if ( ref == missingRef ) + continue; + if ( ref.groupNum() == _groupNum ) { + if ( groupWriter.isInvalid(ref.indexInGroup()) ) { + // this image depends on something invalid, so mark it invalid + //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str()); + groupWriter.setImageInvalid(i); + somethingInvalidated = true; + break; + } + } + } + } + } while (somethingInvalidated); + } + + // pass 4: add fixups for each image, if needed + bool someBadFixups = false; + if ( !buildingDylibsInCache ) { + // compute fix ups for all images + __block std::vector fixupInfos; + fixupInfos.resize(imageCount); + for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) { + if ( groupWriter.isInvalid(imageIndex) ) + continue; + Diagnostics fixupDiag; + fixupInfos[imageIndex] = _images[imageIndex]->buildFixups(fixupDiag, cacheUnslideBaseAddress, groupWriter); + if ( fixupDiag.hasError() ) { + // disable image in group + someBadFixups = true; + groupWriter.setImageInvalid(imageIndex); + if ( continueIfErrors ) { + diag.warning("fixup problem in %s: %s", _images[imageIndex]->runtimePath().c_str(), fixupDiag.errorMessage().c_str()); + continue; + } + else { + diag.error("fixup problem in %s: %s", _images[imageIndex]->runtimePath().c_str(), fixupDiag.errorMessage().c_str()); + return; + } + } + } + // if building closure, build patches to shared cache + if ( _groupNum == 2) { + std::unordered_set staticImagesWithWeakDefs; + ImageProxyGroup* cacheGroup = _nextSearchGroup->_nextSearchGroup; + assert(cacheGroup->_basedOn != nullptr); + launch_cache::ImageGroup dyldCacheGroup(cacheGroup->_basedOn); + for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) { + if ( groupWriter.isInvalid(imageIndex) ) + continue; + ImageProxy* thisProxy = _images[imageIndex]; + // Only process interposing info on dylibs statically linked into closure + if ( !thisProxy->staticallyReferenced() ) + continue; + MachOParser imageParser(thisProxy->mh(), _dyldCache.cacheIsMappedRaw()); + // if any images in closure interpose on something in dyld cache, record the cache patches needed + imageParser.forEachInterposingTuple(diag, ^(uint32_t segIndex, uint64_t replacementSegOffset, uint64_t replaceeSegOffset, uint64_t replacementContent, bool& tupleStop) { + if ( _groupNum != 2 ) { + groupWriter.setImageInvalid(imageIndex); + return; + } + TargetSymbolValue interposeReplacee = TargetSymbolValue::makeInvalid(); + TargetSymbolValue interposeReplacement = TargetSymbolValue::makeInvalid(); + for (const FixUp& fixup : fixupInfos[imageIndex].fixups) { + if ( fixup.segIndex != segIndex ) + continue; + if ( fixup.segOffset == replacementSegOffset ) { + if ( fixup.type == launch_cache::ImageGroupWriter::FixupType::rebase ) { + uint64_t offsetInImage = replacementContent - imageParser.preferredLoadAddress(); + interposeReplacement = TargetSymbolValue::makeGroupValue(2, imageIndex, offsetInImage, false); + } + else { + diag.warning("bad interposing implementation in %s", _images[imageIndex]->runtimePath().c_str()); + return; + } + } + else if ( fixup.segOffset == replaceeSegOffset ) { + if ( fixup.type == launch_cache::ImageGroupWriter::FixupType::pointerBind ) { + interposeReplacee = fixup.target; + } + else { + diag.warning("bad interposing target in %s", _images[imageIndex]->runtimePath().c_str()); + return; + } + } + } + // scan through fixups of other images in closure looking to see what functions this entry references + for (uint32_t otherIndex=0; otherIndex < imageCount; ++otherIndex) { + if ( otherIndex == imageIndex ) + continue; + for (FixUp& fixup : fixupInfos[otherIndex].fixups) { + switch ( fixup.type ) { + case launch_cache::ImageGroupWriter::FixupType::pointerBind: + case launch_cache::ImageGroupWriter::FixupType::pointerLazyBind: + // alter fixup to use interposed function instead of requested + if ( fixup.target == interposeReplacee ) + fixup.target = interposeReplacement; + break; + case launch_cache::ImageGroupWriter::FixupType::rebase: + case launch_cache::ImageGroupWriter::FixupType::rebaseText: + case launch_cache::ImageGroupWriter::FixupType::ignore: + case launch_cache::ImageGroupWriter::FixupType::bindText: + case launch_cache::ImageGroupWriter::FixupType::bindTextRel: + case launch_cache::ImageGroupWriter::FixupType::bindImportJmpRel: + break; + } + } + } + if ( interposeReplacee.isInvalid() || interposeReplacement.isInvalid() ) { + diag.error("malformed interposing section in %s", _images[imageIndex]->runtimePath().c_str()); + tupleStop = true; + return; + } + // record any overrides in shared cache that will need to be applied at launch time + uint64_t offsetInCache; + if ( interposeReplacee.isSharedCacheTarget(offsetInCache) ) { + uint32_t patchTableIndex; + if ( dyldCacheGroup.hasPatchTableIndex((uint32_t)offsetInCache, patchTableIndex) ) { + uint32_t replacementGroupNum; + uint32_t replacementIndexInGroup; + uint64_t replacementOffsetInImage; + assert(interposeReplacement.isGroupImageTarget(replacementGroupNum, replacementIndexInGroup, replacementOffsetInImage)); + assert(replacementGroupNum == 2); + assert(replacementIndexInGroup < (1 << 8)); + assert(replacementOffsetInImage < 0xFFFFFFFFULL); + DyldCacheOverride cacheOverride; + cacheOverride.patchTableIndex = patchTableIndex; + cacheOverride.imageIndex = replacementIndexInGroup; + cacheOverride.imageOffset = replacementOffsetInImage; + _cacheOverrides.push_back(cacheOverride); + } + } + }); + if ( diag.hasError() && !continueIfErrors ) { + return; + } + // if any dylibs in the closure override a dyld cache dylib, then record the cache patches needed + ImageRef overrideOf = thisProxy->overrideOf(); + if ( (overrideOf != ImageRef::makeEmptyImageRef()) && (overrideOf.groupNum() == 0) ) { + //fprintf(stderr, "need to patch %s into cache\n", thisProxy->runtimePath().c_str()); + const launch_cache::Image imageInCache = dyldCacheGroup.image(overrideOf.indexInGroup()); + const mach_header* imageInCacheMH = (mach_header*)((char*)(_dyldCache.cacheHeader()) + imageInCache.cacheOffset()); + MachOParser inCacheParser(imageInCacheMH, _dyldCache.cacheIsMappedRaw()); + // walk all exported symbols in dylib in cache + inCacheParser.forEachExportedSymbol(diag, ^(const char* symbolName, uint64_t imageOffset, bool isReExport, bool &stop) { + if ( isReExport ) + return; + uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + imageOffset); + //fprintf(stderr, " patch cache offset 0x%08X which is %s\n", cacheOffsetOfSymbol, symbolName); + // for each exported symbol, see if it is in patch table (used by something else in cache) + uint32_t patchTableIndex; + if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) { + //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol); + // lookup address of symbol in override dylib and add patch info + MachOParser::FoundSymbol foundInfo; + if ( imageParser.findExportedSymbol(diag, symbolName, nullptr, foundInfo, nullptr) ) { + DyldCacheOverride cacheOverride; + assert(patchTableIndex < (1 << 24)); + assert(thisProxy->indexInGroup() < (1 << 8)); + assert(foundInfo.value < (1ULL << 32)); + cacheOverride.patchTableIndex = patchTableIndex; + cacheOverride.imageIndex = thisProxy->indexInGroup(); + cacheOverride.imageOffset = foundInfo.value; + _cacheOverrides.push_back(cacheOverride); + } + } + }); + } + // save off all images in closure with weak defines + if ( thisProxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK) ) { + staticImagesWithWeakDefs.insert(thisProxy); + } + } + // if any dylibs in the closure override a weak symbol in a cached dylib, then record the cache patches needed + if ( !staticImagesWithWeakDefs.empty() ) { + // build list of all weak def symbol names + __block std::unordered_map weakSymbols; + for (ImageProxy* proxy : staticImagesWithWeakDefs ) { + MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw()); + weakDefParser.forEachWeakDef(diag, ^(bool strongDef, uint32_t segIndex, uint64_t segOffset, uint64_t addend, const char* symbolName, bool& stop) { + weakSymbols[symbolName] = { 0, 0, 0 }; + }); + } + // do a flat namespace walk of all images + std::vector flatSearchOrder = flatLookupOrder(); + for (ImageProxy* proxy : flatSearchOrder) { + // only look at images that participate in weak coalescing + if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 ) + continue; + // look only at images in closure + if ( proxy->groupNum() == 2 ) { + MachOParser weakDefParser(proxy->mh(), _dyldCache.cacheIsMappedRaw()); + // check if this closure image defines any of the not-yet found weak symbols + for (auto& entry : weakSymbols ) { + if ( entry.second.imageOffset != 0 ) + continue; + Diagnostics weakDiag; + MachOParser::FoundSymbol foundInfo; + if ( weakDefParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) { + assert(proxy->indexInGroup() < (1 << 8)); + assert(foundInfo.value < (1ULL << 32)); + entry.second.imageIndex = proxy->indexInGroup(); + entry.second.imageOffset = foundInfo.value; + } + } + } + } + for (ImageProxy* proxy : flatSearchOrder) { + // only look at images that participate in weak coalescing + if ( (proxy->mh()->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) == 0 ) + continue; + // look only at images in dyld cache + if ( proxy->groupNum() == 0 ) { + const launch_cache::Image imageInCache = dyldCacheGroup.image(proxy->indexInGroup()); + MachOParser inCacheParser(proxy->mh(), _dyldCache.cacheIsMappedRaw()); + Diagnostics cacheDiag; + for (auto& entry : weakSymbols) { + if ( entry.second.imageOffset == 0 ) + continue; + Diagnostics weakDiag; + MachOParser::FoundSymbol foundInfo; + if ( inCacheParser.findExportedSymbol(weakDiag, entry.first.c_str(), nullptr, foundInfo, nullptr) ) { + uint32_t cacheOffsetOfSymbol = (uint32_t)(imageInCache.cacheOffset() + foundInfo.value); + // see if this symbol is in patch table (used by something else in cache) + uint32_t patchTableIndex; + if ( dyldCacheGroup.hasPatchTableIndex(cacheOffsetOfSymbol, patchTableIndex) ) { + //fprintf(stderr, " need patch cache offset 0x%08X\n", cacheOffsetOfSymbol); + DyldCacheOverride cacheOverride; + cacheOverride.patchTableIndex = patchTableIndex; + cacheOverride.imageIndex = entry.second.imageIndex; + cacheOverride.imageOffset = entry.second.imageOffset; + _cacheOverrides.push_back(cacheOverride); + } + } + } + } + } + } + } + // record fixups for each image + for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) { + groupWriter.setImageFixups(diag, imageIndex, fixupInfos[imageIndex].fixups, fixupInfos[imageIndex].hasTextRelocs); + } + } + + // pass 5: invalidate any images dependent on invalid images) + if ( someBadFixups && continueIfErrors ) { + __block bool somethingInvalidated = false; + do { + somethingInvalidated = false; + for (uint32_t i=0; i < imageCount; ++i) { + if ( groupWriter.isInvalid(i) ) + continue; + uint32_t depCount = groupWriter.imageDependentsCount(i); + for (uint32_t depIndex=0; depIndex < depCount; ++depIndex) { + launch_cache::binary_format::ImageRef ref = groupWriter.imageDependent(i, depIndex); + if ( ref.groupNum() == _groupNum ) { + if ( groupWriter.isInvalid(ref.indexInGroup()) ) { + // this image depends on something invalid, so mark it invalid + //fprintf(stderr, "warning: image %s depends on invalid %s\n", _images[i]->runtimePath().c_str(), _images[ref.index()]->runtimePath().c_str()); + groupWriter.setImageInvalid(i); + somethingInvalidated = true; + break; + } + } + } + } + } while (somethingInvalidated); + } + + // pass 6: compute initializer lists for each image + const bool log = false; + for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) { + if ( groupWriter.isInvalid(imageIndex) ) + continue; + + auto inits = _images[imageIndex]->getInitBeforeList(*this); + if ( log && buildingDylibsInCache ) { + fprintf(stderr, "%s\n init list: ", _images[imageIndex]->runtimePath().c_str()); + for (launch_cache::binary_format::ImageRef ref : inits) { + if ( ref.groupNum() == 0 ) { + std::string dep = _images[ref.indexInGroup()]->runtimePath(); + size_t off = dep.rfind('/'); + fprintf(stderr, "%s, ", dep.substr(off+1).c_str()); + } + } + fprintf(stderr, "\n"); + } + groupWriter.setImageInitBefore(imageIndex, inits); + } + + // pass 7: compute DOFs + for (uint32_t imageIndex=0; imageIndex < imageCount; ++imageIndex) { + if ( groupWriter.isInvalid(imageIndex) ) + continue; + + auto inits = _images[imageIndex]->getInitBeforeList(*this); + if ( log && buildingDylibsInCache ) { + fprintf(stderr, "%s\n DOFs: ", _images[imageIndex]->runtimePath().c_str()); + for (launch_cache::binary_format::ImageRef ref : inits) { + if ( ref.groupNum() == 0 ) { + std::string dep = _images[ref.indexInGroup()]->runtimePath(); + size_t off = dep.rfind('/'); + fprintf(stderr, "%s, ", dep.substr(off+1).c_str()); + } + } + fprintf(stderr, "\n"); + } + groupWriter.setImageInitBefore(imageIndex, inits); + } + + // pass 8: add patch table entries iff this is dyld cache ImageGroup + assert(buildingDylibsInCache == (_patchTable != nullptr)); + if ( _patchTable != nullptr ) { + for (uint32_t i=0; i < imageCount; ++i) { + const auto pos = _patchTable->find(_images[i]->mh()); + if ( pos != _patchTable->end() ) { + for (const auto& entry : pos->second ) { + uint32_t defFunctionOffset = entry.first; + groupWriter.setImagePatchLocations(i, defFunctionOffset, entry.second); + } + } + } + } + + // if this is a main closure group with an interposing dylib, add cache overrides + if ( !_cacheOverrides.empty() ) { + groupWriter.setGroupCacheOverrides(_cacheOverrides); + } + + // align string pool + groupWriter.alignStringPool(); +} + + + +} // namespace dyld3 + + diff --git a/dyld/dyld3/shared-cache/ImageProxy.h b/dyld/dyld3/shared-cache/ImageProxy.h new file mode 100644 index 0000000..35bf42c --- /dev/null +++ b/dyld/dyld3/shared-cache/ImageProxy.h @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2017 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 ImageProxy_h +#define ImageProxy_h + +#include + +#include +#include +#include +#include + +#include "DyldSharedCache.h" +#include "Diagnostics.h" +#include "LaunchCache.h" +#include "LaunchCacheWriter.h" +#include "PathOverrides.h" +#include "ClosureBuffer.h" +#include "DyldCacheParser.h" + + +namespace dyld3 { + +typedef launch_cache::binary_format::Image BinaryImageData; +typedef launch_cache::binary_format::ImageGroup BinaryImageGroupData; +typedef launch_cache::binary_format::Closure BinaryClosureData; +typedef launch_cache::binary_format::ImageRef ImageRef; +typedef launch_cache::Image::LinkKind LinkKind; +typedef launch_cache::ImageGroupWriter::FixUp FixUp; +typedef launch_cache::binary_format::DyldCacheOverride DyldCacheOverride; +typedef launch_cache::ImageGroupList ImageGroupList; + + + + +class ImageProxyGroup; + +class ImageProxy +{ +public: + ImageProxy(const mach_header* mh, const BinaryImageData* image, uint32_t indexInGroup, bool dyldCacheIsRaw); + ImageProxy(const DyldSharedCache::MappedMachO& mapping, uint32_t groupNum, uint32_t indexInGroup, bool dyldCacheIsRaw); + + struct RPathChain { + ImageProxy* inProxy; + const RPathChain* prev; + const std::vector& rpaths; + }; + + struct InitOrderInfo { + bool beforeHas(ImageRef); + bool upwardHas(ImageProxy*); + void removeRedundantUpwards(); + std::vector initBefore; + std::vector danglingUpward; + }; + + struct FixupInfo { + std::vector fixups; + bool hasTextRelocs = false; + }; + + void recursiveBuildInitBeforeInfo(ImageProxyGroup& owningGroup); + void addDependentsShallow(ImageProxyGroup& owningGroup, RPathChain* chain=nullptr); + void addDependentsDeep(ImageProxyGroup& owningGroup, RPathChain* chain, bool staticallyReferenced); + void markInvalid() { _invalid = true; } + + uint32_t groupNum() const { return _groupNum; } + uint32_t indexInGroup() const { return _indexInGroup; } + const mach_header* mh() const { return _mh; } + const std::string& runtimePath() const { return _runtimePath; } + uint64_t sliceFileOffset() const { return _sliceFileOffset; } + uint64_t fileModTime() const { return _modTime; } + uint64_t fileInode() const { return _inode; } + bool isSetUID() const { return _isSetUID; } + bool invalid() const { return _invalid; } + bool staticallyReferenced() const { return _staticallyReferenced; } + bool cwdMustBeThisDir() const { return _cwdMustBeThisDir; } + bool isPlatformBinary() const { return _platformBinary; } + bool isProxyForCachedDylib() const { return _imageBinaryData != nullptr; } + const Diagnostics& diagnostics() const { return _diag; } + ImageRef overrideOf() const { return _overrideOf; } + bool inLibSystem() const; + void setCwdMustBeThisDir() { _cwdMustBeThisDir = true; } + void setPlatformBinary() { _platformBinary = true; } + void setOverrideOf(uint32_t groupNum, uint32_t indexInGroup); + void checkIfImageOverride(const std::string& runtimeLoadPath); + void forEachDependent(void (^handler)(ImageProxy* dep, LinkKind)) const; + FixupInfo buildFixups(Diagnostics& diag, uint64_t cacheUnslideBaseAddress, launch_cache::ImageGroupWriter& groupWriter) const; + bool findExportedSymbol(Diagnostics& diag, const char* symbolName, MachOParser::FoundSymbol& foundInfo) const; + void convertInitBeforeInfoToArray(ImageProxyGroup& owningGroup); + void addToFlatLookup(std::vector& imageList); + const std::vector& getInitBeforeList(ImageProxyGroup& owningGroup); + const std::vector& rpaths() { return _rpaths; } + +private: + void processRPaths(ImageProxyGroup& owningGroup); + + const mach_header* const _mh; + uint64_t const _sliceFileOffset; + uint64_t const _modTime; + uint64_t const _inode; + const BinaryImageData* const _imageBinaryData; // only used if proxy is for image in shared cache + std::string const _runtimePath; + bool const _isSetUID; + bool const _dyldCacheIsRaw; + uint32_t const _groupNum; + uint32_t const _indexInGroup; + bool _platformBinary; + Diagnostics _diag; + std::vector _dependents; + std::vector _dependentsKind; + std::vector _rpaths; + InitOrderInfo _initBeforesInfo; + std::vector _initBeforesArray; + ImageRef _overrideOf; + bool _directDependentsSet; + bool _deepDependentsSet; + bool _initBeforesArraySet; + bool _initBeforesComputed; + bool _invalid; + bool _staticallyReferenced; + bool _cwdMustBeThisDir; +}; + + +class ImageProxyGroup +{ +public: + ~ImageProxyGroup(); + + + typedef std::unordered_map>> PatchTable; + + + // used when building dyld shared cache + static ImageProxyGroup* makeDyldCacheDylibsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, const std::vector& cachedDylibs, + const std::vector& buildTimePrefixes, const PatchTable& patchTable, + bool stubEliminated, bool dylibsExpectedOnDisk); + + // used when building dyld shared cache + static ImageProxyGroup* makeOtherOsGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup, + const std::vector& otherDylibsAndBundles, + bool inodesAreSameAsRuntime, const std::vector& buildTimePrefixes); + + const BinaryImageGroupData* makeImageGroupBinary(Diagnostics& diag, const char* const neverEliminateStubs[]=nullptr); + + // used when building dyld shared cache + static BinaryClosureData* makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, ImageProxyGroup* cachedDylibsGroup, + ImageProxyGroup* otherOsDylibs, const DyldSharedCache::MappedMachO& mainProg, + bool inodesAreSameAsRuntime, const std::vector& buildTimePrefixes); + + // used by closured for dlopen of unknown dylibs + static const BinaryImageGroupData* makeDlopenGroup(Diagnostics& diag, const DyldCacheParser& dyldCache, uint32_t groupNum, + const std::vector& existingGroups, + const std::string& imagePath, const std::vector& envVars); + + static const BinaryImageGroupData* makeDlopenGroup(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector& buildTimePrefixes={}); + + static BinaryClosureData* makeClosure(Diagnostics& diag, const ClosureBuffer& buffer, task_t requestor, const std::vector& buildTimePrefixes={}); + + + // + // Creates a binary launch closure for the specified main executable. + // Used by closured and dyld_closure_util + // + // The closure is allocated with malloc(). Use free() to release when done. + // The size of the closure can be determined using Closure::size(). + // If the closure cannot be built (e.g. app needs a symbol not exported by a framework), + // the reason for the failure is returned as a string in the diag parameter. + // The mainProgRuntimePath path is the path the program will be at runtime. + // The buildTimePrefixes is a list of prefixes to add to each path during closure + // creation to find the files at buildtime. + // + static BinaryClosureData* makeClosure(Diagnostics& diag, const DyldCacheParser& dyldCache, + const std::string& mainProgRuntimePath, bool includeDylibsInDir, + const std::vector& buildTimePrefixes={}, + const std::vector& envVars={}); + + +private: + friend class ImageProxy; + + ImageProxyGroup(uint32_t groupNum, const DyldCacheParser& dyldCache, const BinaryImageGroupData* basedOn, + ImageProxyGroup* next, const std::string& mainProgRuntimePath, + const std::vector& knownGroups, + const std::vector& buildTimePrefixes, + const std::vector& envVars, + bool stubsEliminated=false, bool dylibsExpectedOnDisk=true, bool inodesAreSameAsRuntime=true); + + ImageProxy* findImage(Diagnostics& diag, const std::string& runtimePath, bool canBeMissing, ImageProxy::RPathChain*); + ImageProxy* findAbsoluteImage(Diagnostics& diag, const std::string& runtimePath, bool canBeMissing, bool makeErrorMessage, bool pathIsReal=false); + bool builtImageStillValid(const launch_cache::Image& image); + const std::string& mainProgRuntimePath() { return _mainProgRuntimePath; } + DyldSharedCache::MappedMachO* addMappingIfValidMachO(Diagnostics& diag, const std::string& runtimePath, bool ignoreMainExecutables=false); + BinaryClosureData* makeClosureBinary(Diagnostics& diag, ImageProxy* mainProg, bool includeDylibsInDir); + void findLibdyldEntry(Diagnostics& diag, ImageRef& ref, uint32_t& offset); + void findLibSystem(Diagnostics& diag, bool sim, ImageRef& ref); + void populateGroupWriter(Diagnostics& diag, launch_cache::ImageGroupWriter& groupWriter, const char* const neverEliminateStubs[]=nullptr); + std::string normalizedPath(const std::string& path); + void addExtraMachOsInBundle(const std::string& appDir); + bool addInsertedDylibs(Diagnostics& diag); + std::vector flatLookupOrder(); + + PathOverrides _pathOverrides; + const BinaryImageGroupData* _basedOn; // if not null, then lazily populate _images + const PatchTable* _patchTable; + ImageProxyGroup* const _nextSearchGroup; + const DyldCacheParser _dyldCache; + uint32_t const _groupNum; + bool const _stubEliminated; + bool const _dylibsExpectedOnDisk; + bool const _inodesAreSameAsRuntime; + uint32_t _mainExecutableIndex; + std::vector _knownGroups; + std::vector _images; + std::unordered_map _pathToProxy; + std::vector _ownedMappings; + std::vector _buildTimePrefixes; + std::vector _cacheOverrides; + std::string _mainProgRuntimePath; + std::string _archName; + Platform _platform; + std::set _mustBeMissingFiles; +}; + + + + + +} + +#endif // ImageProxy_h diff --git a/dyld/dyld3/shared-cache/MachOFileAbstraction.hpp b/dyld/dyld3/shared-cache/MachOFileAbstraction.hpp new file mode 100644 index 0000000..a15100f --- /dev/null +++ b/dyld/dyld3/shared-cache/MachOFileAbstraction.hpp @@ -0,0 +1,964 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include + +// suport older versions of mach-o/loader.h +#ifndef LC_UUID +#define LC_UUID 0x1b +struct uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; +#endif + +#ifndef S_16BYTE_LITERALS + #define S_16BYTE_LITERALS 0xE +#endif + +#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 CPU_SUBTYPE_ARM_V7F + #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#endif +#ifndef CPU_SUBTYPE_ARM_V7K + #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#endif +#ifndef CPU_SUBTYPE_ARM_V7S + #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#endif +#ifndef CPU_SUBTYPE_ARM64_ALL + #define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#endif +#ifndef CPU_TYPE_ARM64 + #define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64)) +#endif + +#define ARM64_RELOC_UNSIGNED 0 // for pointers + + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#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 + +#ifndef LC_FUNCTION_STARTS + #define LC_FUNCTION_STARTS 0x26 +#endif + +#ifndef LC_DATA_IN_CODE + #define LC_DATA_IN_CODE 0x29 +#endif + +#ifndef LC_DYLIB_CODE_SIGN_DRS + #define LC_DYLIB_CODE_SIGN_DRS 0x2B +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif + + +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F + +#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 +#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 +#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 +#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 +#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 +#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 +#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 +#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A +#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B +#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C + +#define MH_HAS_OBJC 0x40000000 + +#include "FileAbstraction.hpp" +//#include "Architectures.hpp" + +// utility to pair together a cpu-type and cpu-sub-type +struct ArchPair +{ + uint32_t arch; + uint32_t subtype; + + ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {} + + bool operator<(const ArchPair& other) const { + if ( this->arch != other.arch ) + return (this->arch < other.arch); + return (this->subtype < other.subtype); + } + + bool operator==(const ArchPair& other) const { + return this->arch == other.arch && this->subtype == other.subtype; + } +}; + + +// +// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness +// + +// +// mach-o load command +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

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

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

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

::CMD + }; +private: + macho_routines_content

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

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; + +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + const macho_segment_command

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

* cmds = (macho_load_command

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

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

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

::CMD ) { + const macho_segment_command

* segcmd = (macho_segment_command

*)cmd; + if (0 == strncmp(segname, segcmd->segname(), 16)) { + return segcmd; + } + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + const macho_section

* getSection(const char *segname, const char *sectname) const + { + const macho_segment_command

* segcmd = getSegment(segname); + if (!segcmd) return NULL; + + const macho_section

* sectcmd = (macho_section

*)(segcmd+1); + 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; + } + } + + if (strcmp(segname, "__DATA") == 0) + return getSection("__DATA_CONST", sectname); + return NULL; + } + + const macho_load_command

* getLoadCommand(int query) const + { + const macho_load_command

* cmds = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == query ) { + return cmd; + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + typedef typename P::E E; +private: + macho_header_content

header; +}; + + + +// +// compressed dyld info load command +// +template +class macho_dyld_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } + void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } + + uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } + void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } + + uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } + void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } + + uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } + void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } + + uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } + void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } + + uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } + void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } + + uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } + void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } + + uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } + void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } + + uint32_t export_off() const INLINE { return E::get32(fields.export_off); } + void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } + + uint32_t export_size() const INLINE { return E::get32(fields.export_size); } + void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } + + + typedef typename P::E E; +private: + dyld_info_command fields; +}; + +#ifndef NO_ULEB +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throw "malformed uleb128 extends beyond trie"; + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throw "uleb128 too big for 64-bits"; + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + +inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= (((int64_t)(byte & 0x7f)) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + +#endif + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/dyld/dyld3/shared-cache/Manifest.h b/dyld/dyld3/shared-cache/Manifest.h new file mode 100644 index 0000000..3348cfa --- /dev/null +++ b/dyld/dyld3/shared-cache/Manifest.h @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2017 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 Manifest_h +#define Manifest_h + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#import + +#include "MachOParser.h" +#include "DyldSharedCache.h" +#include "Diagnostics.h" + +extern std::string toolDir(); + +namespace dyld3 { + +struct BuildQueueEntry { + DyldSharedCache::CreateOptions options; + std::vector dylibsForCache; + std::vector otherDylibsAndBundles; + std::vector mainExecutables; + std::string outputPath; + std::set configNames; +}; + +struct Manifest { + struct UUIDInfo { + const mach_header* mh; + uint64_t sliceFileOffset; + std::size_t size; + std::string runtimePath; + std::string buildPath; + std::string installName; + std::string arch; + UUID uuid; + UUIDInfo(const mach_header* M, std::size_t S, uint64_t SO, UUID U, std::string A, std::string RP, std::string BP, std::string IN) + : mh(M), size(S), arch(A), uuid(U), runtimePath(RP), buildPath(BP), installName(IN), sliceFileOffset(SO) {} + UUIDInfo() : UUIDInfo(nullptr, 0, 0, UUID(), "", "", "", "") {} + }; + + struct Project { + std::vector sources; + }; + + struct File { + MachOParser* parser; + File(MachOParser* P) + : parser(P) + { + } + }; + + struct SegmentInfo { + std::string name; + uint64_t startAddr; + uint64_t endAddr; + }; + + struct CacheInfo { + std::vector regions; + std::string cdHash; + }; + + struct CacheImageInfo { + bool included; + std::string exclusionInfo; + UUID uuid; + std::string installname; + std::vector segments; + CacheImageInfo(void) + : included(true) + { + } + }; + + struct Results { + std::string failure; + std::map dylibs; + std::map bundles; + std::map executables; + + std::set warnings; + CacheInfo developmentCache; + CacheInfo productionCache; + CacheImageInfo& dylibForInstallname(const std::string& installname); + void exclude(MachOParser* parser, const std::string& reason); + void exclude(Manifest& manifest, const UUID& uuid, const std::string& reason); + }; + + struct Architecture { + mutable Results results; + + bool operator==(const Architecture& O) const; + bool operator!=(const Architecture& other) const; + }; + + struct Configuration { + std::string platformName; + std::string device; + std::string disposition; + std::string metabomTag; + std::set metabomTags; + std::set metabomExcludeTags; + std::set metabomRestrictTags; + std::set restrictedInstallnames; + std::map architectures; + + bool operator==(const Configuration& O) const; + bool operator!=(const Configuration& other) const; + const Architecture& architecture(const std::string& architecture) const; + void forEachArchitecture(std::function lambda) const; + }; + + const std::map& projects(); + const Configuration& configuration(const std::string& configuration) const; + void forEachConfiguration(std::function lambda) const; + + void addProjectSource(const std::string& project, const std::string& source, bool first = false); + + const std::string projectPath(const std::string& projectName); + const bool empty(void); + const std::string dylibOrderFile() const; + void setDylibOrderFile(const std::string& dylibOrderFile); + + const std::string dirtyDataOrderFile() const; + void setDirtyDataOrderFile(const std::string& dirtyDataOrderFile); + + const std::string metabomFile() const; + void setMetabomFile(const std::string& metabomFile); + + const Platform platform() const; + void setPlatform(const Platform platform); + + const std::string& build() const; + void setBuild(const std::string& build); + const uint32_t version() const; + void setVersion(const uint32_t manifestVersion); + bool normalized; + + Manifest(Diagnostics& D, const std::string& path); + Manifest(Diagnostics& D, const std::string& path, const std::set& overlays); + + BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose); + + void write(const std::string& path); + void writeJSON(const std::string& path); + void canonicalize(void); + void calculateClosure(); + MachOParser parserForUUID(const UUID& uuid) const; + const std::string buildPathForUUID(const UUID& uuid); + const std::string runtimePathForUUID(const UUID& uuid); + DyldSharedCache::MappedMachO machoForPathAndArch(const std::string& path, const std::string& arch) const; + void remove(const std::string& config, const std::string& arch); + const std::string removeLargestLeafDylib(const std::set& configurations, const std::string& architecture); + void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function lambda); + bool filterForConfig(const std::string& configName); + +private: + NSDictionary* _manifestDict; + Diagnostics& _diags; + std::map _uuidMap; + std::map, UUID> _installNameMap; + static dispatch_queue_t _identifierQueue; + uint32_t _manifestVersion; + std::string _build; + std::string _dylibOrderFile; + std::string _dirtyDataOrderFile; + std::string _metabomFile; + Platform _platform; + std::map _projects; + std::map _configurations; + std::map> _metabomTagMap; + std::map> _metabomExcludeTagMap; + std::map> _metabomRestrictedTagMap; + + std::vector dylibsForCache(const std::string& configuration, const std::string& architecture); + std::vector otherDylibsAndBundles(const std::string& configuration, const std::string& architecture); + std::vector mainExecutables(const std::string& configuration, const std::string& architecture); + + const UUIDInfo& infoForUUID(const UUID& uuid) const; + const UUIDInfo infoForInstallNameAndarch(const std::string& installName, const std::string arch) const; + void insert(std::vector& mappedMachOs, const CacheImageInfo& imageInfo); + bool loadParser(const void* p, size_t size, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set& architectures); + bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set& architectures); + void removeDylib(MachOParser parser, const std::string& reason, const std::string& configuration, const std::string& architecture, + std::unordered_set& processedIdentifiers); + void dedupeDispositions(); + void calculateClosure(const std::string& configuration, const std::string& architecture); + void canonicalizeDylib(const std::string& installname); + template + void canonicalizeDylib(const std::string& installname, const uint8_t* p); + void addImplicitAliases(void); +}; +} + +#endif /* Manifest_h */ diff --git a/dyld/dyld3/shared-cache/Manifest.mm b/dyld/dyld3/shared-cache/Manifest.mm new file mode 100644 index 0000000..3dd9f33 --- /dev/null +++ b/dyld/dyld3/shared-cache/Manifest.mm @@ -0,0 +1,1134 @@ +/* + * Copyright (c) 2017 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 "C" { +#include +#include +#include +#include +#include +}; + +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "FileAbstraction.hpp" +#include "Trie.hpp" +#include "FileUtils.h" +#include "StringUtils.h" + +#include +#include + +#include +#include + +#include "Manifest.h" + +namespace { +//FIXME this should be in a class +static inline NSString* cppToObjStr(const std::string& str) { return [NSString stringWithUTF8String:str.c_str()]; } + +template +inline bool is_disjoint(const Set1& set1, const Set2& set2) +{ + if (set1.empty() || set2.empty()) + return true; + + typename Set1::const_iterator it1 = set1.begin(), it1End = set1.end(); + typename Set2::const_iterator it2 = set2.begin(), it2End = set2.end(); + + if (*it1 > *set2.rbegin() || *it2 > *set1.rbegin()) + return true; + + while (it1 != it1End && it2 != it2End) { + if (*it1 == *it2) + return false; + if (*it1 < *it2) { + it1++; + } else { + it2++; + } + } + + return true; +} + +//hACK: If we declare this in manifest +static NSDictionary* gManifestDict; + +} /* Anonymous namespace */ + +namespace dyld3 { +void Manifest::Results::exclude(MachOParser* parser, const std::string& reason) +{ + auto dylibUUID = parser->uuid(); + dylibs[dylibUUID].uuid = dylibUUID; + dylibs[dylibUUID].installname = parser->installName(); + dylibs[dylibUUID].included = false; + dylibs[dylibUUID].exclusionInfo = reason; +} + +void Manifest::Results::exclude(Manifest& manifest, const UUID& uuid, const std::string& reason) +{ + auto parser = manifest.parserForUUID(uuid); + dylibs[uuid].uuid = uuid; + dylibs[uuid].installname = parser.installName(); + dylibs[uuid].included = false; + dylibs[uuid].exclusionInfo = reason; +} + +Manifest::CacheImageInfo& Manifest::Results::dylibForInstallname(const std::string& installname) +{ + auto i = find_if(dylibs.begin(), dylibs.end(), [&installname](std::pair d) { return d.second.installname == installname; }); + assert(i != dylibs.end()); + return i->second; +} + +bool Manifest::Architecture::operator==(const Architecture& O) const +{ + for (auto& dylib : results.dylibs) { + if (dylib.second.included) { + auto Odylib = O.results.dylibs.find(dylib.first); + if (Odylib == O.results.dylibs.end() + || Odylib->second.included == false + || Odylib->second.uuid != dylib.second.uuid) + return false; + } + } + + for (const auto& Odylib : O.results.dylibs) { + if (Odylib.second.included) { + auto dylib = results.dylibs.find(Odylib.first); + if (dylib == results.dylibs.end() + || dylib->second.included == false + || dylib->second.uuid != Odylib.second.uuid) + return false; + } + } + + for (auto& bundle : results.bundles) { + if (bundle.second.included) { + auto Obundle = O.results.bundles.find(bundle.first); + if (Obundle == O.results.bundles.end() + || Obundle->second.included == false + || Obundle->second.uuid != bundle.second.uuid) + return false; + } + } + + for (const auto& Obundle : O.results.bundles) { + if (Obundle.second.included) { + auto bundle = results.bundles.find(Obundle.first); + if (bundle == results.bundles.end() + || bundle->second.included == false + || bundle->second.uuid != Obundle.second.uuid) + return false; + } + } + + for (auto& executable : results.executables) { + if (executable.second.included) { + auto Oexecutable = O.results.executables.find(executable.first); + if (Oexecutable == O.results.executables.end() + || Oexecutable->second.included == false + || Oexecutable->second.uuid != executable.second.uuid) + return false; + } + } + + for (const auto& Oexecutable : O.results.executables) { + if (Oexecutable.second.included) { + auto executable = results.executables.find(Oexecutable.first); + if (executable == results.executables.end() + || executable->second.included == false + || executable->second.uuid != Oexecutable.second.uuid) + return false; + } + } + + return true; +} + +bool Manifest::Configuration::operator==(const Configuration& O) const +{ + return architectures == O.architectures; +} + +bool Manifest::Configuration::operator!=(const Configuration& other) const { return !(*this == other); } + +const Manifest::Architecture& Manifest::Configuration::architecture(const std::string& architecture) const +{ + assert(architectures.find(architecture) != architectures.end()); + return architectures.find(architecture)->second; +} + +void Manifest::Configuration::forEachArchitecture(std::function lambda) const +{ + for (const auto& architecutre : architectures) { + lambda(architecutre.first); + } +} + +bool Manifest::Architecture::operator!=(const Architecture& other) const { return !(*this == other); } + +const std::map& Manifest::projects() +{ + return _projects; +} + +const Manifest::Configuration& Manifest::configuration(const std::string& configuration) const +{ + assert(_configurations.find(configuration) != _configurations.end()); + return _configurations.find(configuration)->second; +} + +void Manifest::forEachConfiguration(std::function lambda) const +{ + for (const auto& configuration : _configurations) { + lambda(configuration.first); + } +} + +void Manifest::addProjectSource(const std::string& project, const std::string& source, bool first) +{ + auto& sources = _projects[project].sources; + if (std::find(sources.begin(), sources.end(), source) == sources.end()) { + if (first) { + sources.insert(sources.begin(), source); + } else { + sources.push_back(source); + } + } +} + +const std::string Manifest::projectPath(const std::string& projectName) +{ + auto project = _projects.find(projectName); + if (project == _projects.end()) + return ""; + if (project->second.sources.size() == 0) + return ""; + return project->second.sources[0]; +} + +const bool Manifest::empty(void) +{ + for (const auto& configuration : _configurations) { + if (configuration.second.architectures.size() != 0) + return false; + } + return true; +} + +const std::string Manifest::dylibOrderFile() const { return _dylibOrderFile; }; +void Manifest::setDylibOrderFile(const std::string& dylibOrderFile) { _dylibOrderFile = dylibOrderFile; }; + +const std::string Manifest::dirtyDataOrderFile() const { return _dirtyDataOrderFile; }; +void Manifest::setDirtyDataOrderFile(const std::string& dirtyDataOrderFile) { _dirtyDataOrderFile = dirtyDataOrderFile; }; + +const std::string Manifest::metabomFile() const { return _metabomFile; }; +void Manifest::setMetabomFile(const std::string& metabomFile) { _metabomFile = metabomFile; }; + +const Platform Manifest::platform() const { return _platform; }; +void Manifest::setPlatform(const Platform platform) { _platform = platform; }; + +const std::string& Manifest::build() const { return _build; }; +void Manifest::setBuild(const std::string& build) { _build = build; }; +const uint32_t Manifest::version() const { return _manifestVersion; }; +void Manifest::setVersion(const uint32_t manifestVersion) { _manifestVersion = manifestVersion; }; + +BuildQueueEntry Manifest::makeQueueEntry(const std::string& outputPath, const std::set& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix, bool verbose) +{ + dyld3::BuildQueueEntry retval; + + DyldSharedCache::CreateOptions options; + options.archName = arch; + options.platform = platform(); + options.excludeLocalSymbols = true; + options.optimizeStubs = optimizeStubs; + options.optimizeObjC = true; + options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ? + DyldSharedCache::Agile : DyldSharedCache::SHA256only; + options.dylibsRemovedDuringMastering = true; + options.inodesAreSameAsRuntime = false; + options.cacheSupportsASLR = true; + options.forSimulator = false; + options.verbose = verbose; + options.evictLeafDylibsOnOverflow = false; + options.loggingPrefix = prefix; + options.pathPrefixes = { "" }; + options.dylibOrdering = loadOrderFile(_dylibOrderFile); + options.dirtyDataSegmentOrdering = loadOrderFile(_dirtyDataOrderFile); + + dyld3::BuildQueueEntry queueEntry; + retval.configNames = configs; + retval.options = options; + retval.outputPath = outputPath; + retval.dylibsForCache = dylibsForCache(*configs.begin(), arch); + retval.otherDylibsAndBundles = otherDylibsAndBundles(*configs.begin(), arch); + retval.mainExecutables = mainExecutables(*configs.begin(), arch); + + return retval; +} + +bool Manifest::loadParser(const void* p, size_t size, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set& architectures) +{ + const mach_header* mh = reinterpret_cast(p); + if (!MachOParser::isValidMachO(_diags, "", _platform, p, size, runtimePath.c_str(), false)) { + return false; + } + + auto parser = MachOParser(mh); + if (_diags.hasError()) { + // Clear the error and punt + _diags.verbose("MachoParser error: %s\n", _diags.errorMessage().c_str()); + _diags.clearError(); + return false; + } + + auto uuid = parser.uuid(); + auto archName = parser.archName(); + + if (parser.fileType() == MH_DYLIB && architectures.count(parser.archName()) != 0) { + std::string installName = parser.installName(); + auto index = std::make_pair(installName, parser.archName()); + auto i = _installNameMap.find(index); + + if ( installName == "/System/Library/Caches/com.apple.xpc/sdk.dylib" + || installName == "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib" ) { + // HACK to deal with device specific dylibs. These must not be inseted into the installNameMap + _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName))); + } else if (i == _installNameMap.end()) { + _installNameMap.insert(std::make_pair(index, uuid)); + _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName))); + if (installName[0] != '@' && installName != runtimePath) { + _diags.warning("Dylib located at '%s' has installname '%s'", runtimePath.c_str(), installName.c_str()); + } + } else { + auto info = infoForUUID(i->second); + _diags.warning("Multiple dylibs claim installname '%s' ('%s' and '%s')", installName.c_str(), runtimePath.c_str(), info.runtimePath.c_str()); + + // This is the "Good" one, overwrite + if (runtimePath == installName) { + _uuidMap.erase(uuid); + _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, installName))); + } + } + } else { + _uuidMap.insert(std::make_pair(uuid, UUIDInfo(mh, size, sliceOffset, uuid, parser.archName(), runtimePath, buildPath, ""))); + } + return true; +} + +//FIXME: assert we have not errored first +bool Manifest::loadParsers(const std::string& buildPath, const std::string& runtimePath, const std::set& architectures) +{ + __block bool retval = false; + const void* p = (uint8_t*)(-1); + struct stat stat_buf; + + std::tie(p, stat_buf) = fileCache.cacheLoad(_diags, buildPath); + + if (p == (uint8_t*)(-1)) { + return false; + } + + if (FatUtil::isFatFile(p)) { + FatUtil::forEachSlice(_diags, p, stat_buf.st_size, ^(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, size_t sliceSize, bool& stop) { + if (loadParser(sliceStart, sliceSize, (uintptr_t)sliceStart-(uintptr_t)p, runtimePath, buildPath, architectures)) + retval = true; + }); + } else { + return loadParser(p, stat_buf.st_size, 0, runtimePath, buildPath, architectures); + } + return retval; +} + +const Manifest::UUIDInfo& Manifest::infoForUUID(const UUID& uuid) const { + auto i = _uuidMap.find(uuid); + assert(i != _uuidMap.end()); + return i->second; +} + +const Manifest::UUIDInfo Manifest::infoForInstallNameAndarch(const std::string& installName, const std::string arch) const { + UUIDInfo retval; + auto uuidI = _installNameMap.find(std::make_pair(installName, arch)); + if (uuidI == _installNameMap.end()) + return UUIDInfo(); + + auto i = _uuidMap.find(uuidI->second); + if (i == _uuidMap.end()) + return UUIDInfo(); + return i->second; +} + +MachOParser Manifest::parserForUUID(const UUID& uuid) const { + return MachOParser(infoForUUID(uuid).mh); +} + +const std::string Manifest::buildPathForUUID(const UUID& uuid) { + return infoForUUID(uuid).buildPath; +} + +const std::string Manifest::runtimePathForUUID(const UUID& uuid) { + return infoForUUID(uuid).runtimePath; +} + +Manifest::Manifest(Diagnostics& D, const std::string& path) : Manifest(D, path, std::set()) +{ +} + +Manifest::Manifest(Diagnostics& D, const std::string& path, const std::set& overlays) : + _diags(D) +{ + NSMutableDictionary* manifestDict = [NSMutableDictionary dictionaryWithContentsOfFile:cppToObjStr(path)]; + NSString* platStr = manifestDict[@"platform"]; + std::set architectures; + + if (platStr == nullptr) + platStr = @"ios"; + std::string platformString = [platStr UTF8String]; + setMetabomFile([manifestDict[@"metabomFile"] UTF8String]); + + if (platformString == "ios") { + setPlatform(dyld3::Platform::iOS); + } else if ( (platformString == "tvos") || (platformString == "atv") ) { + setPlatform(dyld3::Platform::tvOS); + } else if ( (platformString == "watchos") || (platformString == "watch") ) { + setPlatform(dyld3::Platform::watchOS); + } else if ( (platformString == "bridgeos") || (platformString == "bridge") ) { + setPlatform(dyld3::Platform::bridgeOS); + } else if ( (platformString == "macos") || (platformString == "osx") ) { + setPlatform(dyld3::Platform::macOS); + } else { + //Fixme should we error? + setPlatform(dyld3::Platform::iOS); + } + + for (NSString* project in manifestDict[@"projects"]) { + for (NSString* source in manifestDict[@"projects"][project]) { + addProjectSource([project UTF8String], [source UTF8String]); + } + } + + for (NSString* configuration in manifestDict[@"configurations"]) { + std::string configStr = [configuration UTF8String]; + std::string configTag = [manifestDict[@"configurations"][configuration][@"metabomTag"] UTF8String]; + + if (manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) { + for (NSString* excludeTag in manifestDict[@"configurations"][configuration][@"metabomExcludeTags"]) { + _metabomExcludeTagMap[configStr].insert([excludeTag UTF8String]); + _configurations[configStr].metabomExcludeTags.insert([excludeTag UTF8String]); + } + } + + if (manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) { + for (NSString* restrictTag in manifestDict[@"configurations"][configuration][@"metabomRestrictTags"]) { + _metabomRestrictedTagMap[configStr].insert([restrictTag UTF8String]); + _configurations[configStr].metabomRestrictTags.insert([restrictTag UTF8String]); + } + } + + _configurations[configStr].metabomTag = configTag; + _configurations[configStr].metabomTags.insert(configTag); + _configurations[configStr].platformName = + [manifestDict[@"configurations"][configuration][@"platformName"] UTF8String]; + + if (endsWith(configStr, "InternalOS")) { + _configurations[configStr].disposition = "internal"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("InternalOS")); + } else if (endsWith(configStr, "VendorOS")) { + _configurations[configStr].disposition = "internal"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorOS")); + } else if (endsWith(configStr, "VendorUIOS")) { + _configurations[configStr].disposition = "internal"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("VendorUIOS")); + } else if (endsWith(configStr, "CarrierOS")) { + _configurations[configStr].disposition = "internal"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("CarrierOS")); + } else if (endsWith(configStr, "FactoryOS")) { + _configurations[configStr].disposition = "internal"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("FactoryOS")); + } else if (endsWith(configStr, "DesenseOS")) { + _configurations[configStr].disposition = "internal"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DesenseOS")); + } else if (endsWith(configStr, "MinosOS")) { + _configurations[configStr].disposition = "minos"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS")); + } else if (endsWith(configStr, "DemoOS")) { + _configurations[configStr].disposition = "demo"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DemoOS")); + } else if (endsWith(configStr, "MinosOS")) { + _configurations[configStr].disposition = "minos"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("MinosOS")); + } else if (endsWith(configStr, "DeveloperOS")) { + _configurations[configStr].disposition = "user"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("DeveloperOS")); + } else if (endsWith(configStr, "OS")) { + _configurations[configStr].disposition = "user"; + _configurations[configStr].device = configStr.substr(0, configStr.length()-strlen("OS")); + } + + for (NSString* architecutre in manifestDict[@"configurations"][configuration][@"architectures"]) { + //HACK until B&I stops mastering armv7s + if ([architecutre isEqual:@"armv7s"]) break; + _configurations[configStr].architectures[[architecutre UTF8String]] = Architecture(); + architectures.insert([architecutre UTF8String]); + } + } + + setVersion([manifestDict[@"manifest-version"] unsignedIntValue]); + setBuild([manifestDict[@"build"] UTF8String]); + if (manifestDict[@"dylibOrderFile"]) { + setDylibOrderFile([manifestDict[@"dylibOrderFile"] UTF8String]); + } + if (manifestDict[@"dirtyDataOrderFile"]) { + setDirtyDataOrderFile([manifestDict[@"dirtyDataOrderFile"] UTF8String]); + } + + auto metabom = MBMetabomOpen(metabomFile().c_str(), false); + auto metabomEnumerator = MBIteratorNewWithPath(metabom, ".", ""); + MBEntry entry; + + // FIXME error handling (NULL metabom) + + //First we iterate through the bom and build our objects + + while ((entry = MBIteratorNext(metabomEnumerator))) { + BOMFSObject fsObject = MBEntryGetFSObject(entry); + BOMFSObjType entryType = BOMFSObjectType(fsObject); + std::string entryPath = BOMFSObjectPathName(fsObject); + if (entryPath[0] == '.') { + entryPath.erase(0, 1); + } + + // Skip artifacts that happen to be in the build chain + if ( startsWith(entryPath, "/Applications/Xcode.app") ) { + continue; + } + + // Skip variants we can't deal with + if ( endsWith(entryPath, "_profile.dylib") || endsWith(entryPath, "_debug.dylib") || endsWith(entryPath, "_profile") || endsWith(entryPath, "_debug") || endsWith(entryPath, "/CoreADI") ) { + continue; + } + + // Skip images that are only used in InternalOS variants + if ( startsWith(entryPath, "/AppleInternal/") || startsWith(entryPath, "/usr/local/") || startsWith(entryPath, "/Developer/")) { + continue; + } + + // Skip genCache generated dylibs + if ( endsWith(entryPath, "/System/Library/Caches/com.apple.xpc/sdk.dylib") || endsWith(entryPath, "/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib")) { + continue; + } + + MBTag tag; + auto tagCount = MBEntryGetNumberOfProjectTags(entry); + if (entryType == BOMFileType && BOMFSObjectIsBinaryObject(fsObject) && MBEntryGetNumberOfProjectTags(entry) != 0 && tagCount != 0) { + if (tagCount == 1) { + MBEntryGetProjectTags(entry, &tag); + } else { + MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount); + MBEntryGetProjectTags(entry, tags); + + //Sigh, we can have duplicate entries for the same tag, so build a set to work with + std::set tagStrs; + std::map tagStrMap; + for (auto i = 0; i < tagCount; ++i) { + tagStrs.insert(MBMetabomGetProjectForTag(metabom, tags[i])); + tagStrMap.insert(std::make_pair(MBMetabomGetProjectForTag(metabom, tags[i]), tags[i])); + } + + if (tagStrs.size() > 1) { + std::string projects; + for (const auto& tagStr : tagStrs) { + if (!projects.empty()) + projects += ", "; + + projects += "'" + tagStr + "'"; + } + _diags.warning("Bom entry '%s' is claimed by multiple projects: %s, taking first entry", entryPath.c_str(), projects.c_str()); + } + tag = tagStrMap[*tagStrs.begin()]; + free(tags); + } + + std::string projectName = MBMetabomGetProjectForTag(metabom, tag); + tagCount = MBEntryGetNumberOfPackageTags(entry); + MBTag* tags = (MBTag*)malloc(sizeof(MBTag) * tagCount); + MBEntryGetPackageTags(entry, tags); + std::set tagStrs; + + for (auto i = 0; i < tagCount; ++i) { + tagStrs.insert(MBMetabomGetPackageForTag(metabom, tags[i])); + } + + _metabomTagMap.insert(std::make_pair(entryPath, tagStrs)); + bool foundParser = false; + for (const auto& overlay : overlays) { + if (loadParsers(overlay + "/" + entryPath, entryPath, architectures)) { + foundParser = true; + break; + } + } + + if (!foundParser) { + (void)loadParsers(projectPath(projectName) + "/" + entryPath, entryPath, architectures); + } + } + } + + MBIteratorFree(metabomEnumerator); + MBMetabomFree(metabom); +} + +void Manifest::insert(std::vector& mappedMachOs, const CacheImageInfo& imageInfo) { + auto info = infoForUUID(imageInfo.uuid); + auto runtimePath = info.runtimePath; + mappedMachOs.emplace_back(runtimePath, info.mh, info.size, false, false, info.sliceFileOffset, 0, 0); +} + +std::vector Manifest::dylibsForCache(const std::string& configuration, const std::string& architecture) +{ + std::vector retval; + const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs; + for (const auto& dylib : dylibs) { + if (dylib.second.included) { + insert(retval, dylib.second); + } + } + return retval; +} + +std::vector Manifest::otherDylibsAndBundles(const std::string& configuration, const std::string& architecture) +{ + std::vector retval; + const auto& dylibs = _configurations[configuration].architectures[architecture].results.dylibs; + for (const auto& dylib : dylibs) { + if (!dylib.second.included) { + insert(retval, dylib.second); + } + } + + const auto& bundles = _configurations[configuration].architectures[architecture].results.bundles; + for (const auto& bundle : bundles) { + insert(retval, bundle.second); + } + + return retval; +} + +std::vector Manifest::mainExecutables(const std::string& configuration, const std::string& architecture) +{ + std::vector retval; + const auto& executables = _configurations[configuration].architectures[architecture].results.executables; + for (const auto& executable : executables) { + insert(retval, executable.second); + } + + return retval; +} + +bool Manifest::filterForConfig(const std::string& configName) +{ + for (const auto configuration : _configurations) { + if (configName == configuration.first) { + std::map filteredConfigs; + filteredConfigs[configName] = configuration.second; + + _configurations = filteredConfigs; + + for (auto& arch : configuration.second.architectures) { + arch.second.results = Manifest::Results(); + } + return true; + } + } + return false; +} + +void Manifest::dedupeDispositions(void) { + // Since this is all hacky and inference based for now only do it for iOS until XBS + // is reved to give us real info. All the other platforms are way smaller anyway. + if (_platform != Platform::iOS) + return; + + std::map, std::set> dispositionSets; + + for (const auto& configuration : _configurations) { + dispositionSets[std::make_pair(configuration.second.device, configuration.second.disposition)].insert(configuration.first); + } + + for (const auto& dSet : dispositionSets) { + for (const auto &c1 : dSet.second) { + for (const auto &c2 : dSet.second) { + _configurations[c1].metabomTags.insert(_configurations[c2].metabomTag); + } + } + } +} + +void Manifest::calculateClosure() +{ + auto closureSemaphore = dispatch_semaphore_create(32); + auto closureGroup = dispatch_group_create(); + auto closureQueue = dispatch_queue_create("com.apple.dyld.cache.closure", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0)); + + dedupeDispositions(); + for (auto& config : _configurations) { + for (auto& arch : config.second.architectures) { + dispatch_semaphore_wait(closureSemaphore, DISPATCH_TIME_FOREVER); + dispatch_group_async(closureGroup, closureQueue, [&] { + calculateClosure(config.first, arch.first); + dispatch_semaphore_signal(closureSemaphore); + }); + } + } + + dispatch_group_wait(closureGroup, DISPATCH_TIME_FOREVER); +} + +void Manifest::remove(const std::string& config, const std::string& arch) +{ + if (_configurations.count(config)) + _configurations[config].architectures.erase(arch); +} + +void Manifest::removeDylib(MachOParser parser, const std::string& reason, const std::string& configuration, + const std::string& architecture, std::unordered_set& processedIdentifiers) +{ +#if 0 + auto configIter = _configurations.find(configuration); + if (configIter == _configurations.end()) + return; + auto archIter = configIter->second.architectures.find( architecture ); + if ( archIter == configIter->second.architectures.end() ) return; + auto& archManifest = archIter->second; + + if (archManifest.results.dylibs.count(parser->uuid()) == 0) { + archManifest.results.dylibs[parser->uuid()].uuid = parser->uuid(); + archManifest.results.dylibs[parser->uuid()].installname = parser->installName(); + processedIdentifiers.insert(parser->uuid()); + } + archManifest.results.exclude(MachOProxy::forIdentifier(parser->uuid(), architecture), reason); + + processedIdentifiers.insert(parser->uuid()); + + for (const auto& dependent : proxy->dependentIdentifiers) { + auto dependentProxy = MachOProxy::forIdentifier(dependent, architecture); + auto dependentResultIter = archManifest.results.dylibs.find(dependentProxy->identifier); + if ( dependentProxy && + ( dependentResultIter == archManifest.results.dylibs.end() || dependentResultIter->second.included == true ) ) { + removeDylib(dependentProxy, "Missing dependency: " + proxy->installName, configuration, architecture, + processedIdentifiers); + } + } +#endif +} + +const std::string Manifest::removeLargestLeafDylib(const std::set& configurations, const std::string& architecture) +{ + // Find the leaf nodes + __block std::map dependentCounts; + for (const auto& dylib : _configurations[*configurations.begin()].architectures[architecture].results.dylibs) { + if (!dylib.second.included) + continue; + std::string installName; + auto info = infoForUUID(dylib.first); + auto parser = MachOParser(info.mh); + dependentCounts[parser.installName()] = 0; + } + + for (const auto& dylib : _configurations[*configurations.begin()].architectures[architecture].results.dylibs) { + if (!dylib.second.included) + continue; + auto info = infoForUUID(dylib.first); + auto parser = MachOParser(info.mh); + parser.forEachDependentDylib(^(const char *loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) { + if (!isWeak) { + dependentCounts[loadPath]++; + } + }); + } + + // Figure out which leaf is largest + UUIDInfo largestLeaf; + + for (const auto& dependentCount : dependentCounts) { + if (dependentCount.second == 0) { + auto info = infoForInstallNameAndarch(dependentCount.first, architecture); + assert(info.mh != nullptr); + if (info.size > largestLeaf.size) { + largestLeaf = info; + } + } + } + + if (largestLeaf.mh == nullptr) { + _diags.error("Fatal overflow, could not evict more dylibs"); + return ""; + } + + // Remove it ferom all configs + for (const auto& config : configurations) { + configuration(config).architecture(architecture).results.exclude(*this, largestLeaf.uuid, "Cache Overflow"); + } + + return largestLeaf.installName; +} + +void Manifest::calculateClosure(const std::string& configuration, const std::string& architecture) +{ + __block auto& configManifest = _configurations[configuration]; + __block auto& archManifest = _configurations[configuration].architectures[architecture]; + __block std::set newUuids; + std::set processedUuids; + std::set cachedUUIDs; + + // Seed anchors + for (auto& uuidInfo : _uuidMap) { + auto info = uuidInfo.second; + if (info.arch != architecture) { + continue; + } + + auto i = _metabomTagMap.find(info.runtimePath); + assert(i != _metabomTagMap.end()); + auto tags = i->second; + if (!is_disjoint(tags, configManifest.metabomTags)) { + newUuids.insert(info.uuid); + + } + } + + // Pull in all dependencies + while (!newUuids.empty()) { + std::set uuidsToProcess = newUuids; + newUuids.clear(); + + for (const auto& uuid : uuidsToProcess) { + if (processedUuids.count(uuid) > 0) { + continue; + } + processedUuids.insert(uuid); + + auto parser = parserForUUID(uuid); + auto runtimePath = runtimePathForUUID(uuid); + assert(parser.header() != 0); + + parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + auto i = _installNameMap.find(std::make_pair(loadPath, architecture)); + if (i != _installNameMap.end()) + newUuids.insert(i->second); + }); + + if (parser.fileType() == MH_DYLIB) { + // Add the dylib to the results + if (archManifest.results.dylibs.count(uuid) == 0 ) { + archManifest.results.dylibs[uuid].uuid = uuid; + archManifest.results.dylibs[uuid].installname = parser.installName(); + } + + // HACK to insert device specific dylib closures into all caches + if ( parser.installName() == std::string("/System/Library/Caches/com.apple.xpc/sdk.dylib") + || parser.installName() == std::string("/System/Library/Caches/com.apple.xpcd/xpcd_cache.dylib") ) { + archManifest.results.exclude(&parser, "Device specific dylib"); + continue; + } + + std::set reasons; + if (parser.canBePlacedInDyldCache(runtimePath, reasons)) { + auto i = _metabomTagMap.find(runtimePath); + assert(i != _metabomTagMap.end()); + auto restrictions = _metabomRestrictedTagMap.find(configuration); + if (restrictions != _metabomRestrictedTagMap.end() && !is_disjoint(restrictions->second, i->second)) { + archManifest.results.exclude(&parser, "Dylib '" + runtimePath + "' removed due to explict restriction"); + } + + // It can be placed in the cache, grab its dependents and queue them for inclusion + cachedUUIDs.insert(parser.uuid()); + } else { + // It can't be placed in the cache, print out the reasons why + std::string reasonString = "Rejected from cached dylibs: " + runtimePath + " " + architecture + " (\""; + for (auto i = reasons.begin(); i != reasons.end(); ++i) { + reasonString += *i; + if (i != --reasons.end()) { + reasonString += "\", \""; + } + } + reasonString += "\")"; + archManifest.results.exclude(&parser, reasonString); + } + } else if (parser.fileType() == MH_BUNDLE) { + if (archManifest.results.bundles.count(uuid) == 0) { + archManifest.results.bundles[uuid].uuid = uuid; + } + } else if (parser.fileType() == MH_EXECUTE) { + //HACK exclude all launchd and installd variants until we can do something about xpcd_cache.dylib and friends + if (runtimePath == "/sbin/launchd" + || runtimePath == "/usr/local/sbin/launchd.debug" + || runtimePath == "/usr/local/sbin/launchd.development" + || runtimePath == "/usr/libexec/installd") { + continue; + } + if (archManifest.results.executables.count(uuid) == 0) { + archManifest.results.executables[uuid].uuid = uuid; + } + } + } + } + + __block std::set removedUUIDs; + __block bool doAgain = true; + + //Trim out dylibs that are missing dependencies + while ( doAgain ) { + doAgain = false; + for (const auto& uuid : cachedUUIDs) { + __block std::set badDependencies; + __block auto parser = parserForUUID(uuid); + parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + if (isWeak) + return; + + auto i = _installNameMap.find(std::make_pair(loadPath, architecture)); + if (i == _installNameMap.end() || removedUUIDs.count(i->second)) { + removedUUIDs.insert(uuid); + badDependencies.insert(loadPath); + doAgain = true; + } + + if (badDependencies.size()) { + std::string reasonString = "Rejected from cached dylibs: " + std::string(parser.installName()) + " " + architecture + " (\""; + for (auto i = badDependencies.begin(); i != badDependencies.end(); ++i) { + reasonString += *i; + if (i != --badDependencies.end()) { + reasonString += "\", \""; + } + } + reasonString += "\")"; + archManifest.results.exclude(&parser, reasonString); + } + }); + } + + for (const auto& removedUUID : removedUUIDs) { + cachedUUIDs.erase(removedUUID); + } + } + + //Trim out excluded leaf dylibs + __block std::set linkedDylibs; + + for(const auto& uuid : cachedUUIDs) { + auto parser = parserForUUID(uuid); + parser.forEachDependentDylib(^(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop) { + linkedDylibs.insert(loadPath); + }); + } + + for(const auto& uuid : cachedUUIDs) { + auto info = infoForUUID(uuid); + auto i = _metabomTagMap.find(info.runtimePath); + assert(i != _metabomTagMap.end()); + auto exclusions = _metabomExcludeTagMap.find(configuration); + if (exclusions == _metabomExcludeTagMap.end() || is_disjoint(exclusions->second, i->second)) + continue; + + if (linkedDylibs.count(info.installName) != 0) + continue; + + archManifest.results.exclude(*this, info.uuid, "Dylib '" + info.runtimePath + "' excluded leaf node"); + } +} + +void Manifest::writeJSON(const std::string& path) { + NSMutableDictionary* jsonDict = [[NSMutableDictionary alloc] init]; + for (auto& configuration : _configurations) { + jsonDict[cppToObjStr(configuration.first)] = [[NSMutableDictionary alloc] init]; + + for (auto& arch : configuration.second.architectures) { + NSMutableOrderedSet* includedDylibsSet = [[NSMutableOrderedSet alloc] init]; + NSMutableOrderedSet* executablesSet = [[NSMutableOrderedSet alloc] init]; + NSMutableOrderedSet* otherSet = [[NSMutableOrderedSet alloc] init]; + for (auto& dylib : arch.second.results.dylibs) { + NSString *runtimePath = cppToObjStr(runtimePathForUUID(dylib.second.uuid)); + if (dylib.second.included) { + [includedDylibsSet addObject:runtimePath]; + } else { + [otherSet addObject:runtimePath]; + } + } + + for (auto& executable : arch.second.results.executables) { + NSString *runtimePath = cppToObjStr(runtimePathForUUID(executable.second.uuid)); + [executablesSet addObject:runtimePath]; + } + + for (auto& bundle : arch.second.results.bundles) { + NSString *runtimePath = cppToObjStr(runtimePathForUUID(bundle.second.uuid)); + [otherSet addObject:runtimePath]; + } + + [includedDylibsSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { + return [obj1 compare:obj2]; + }]; + + [executablesSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { + return [obj1 compare:obj2]; + }]; + + [otherSet sortUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) { + return [obj1 compare:obj2]; + }]; + + jsonDict[cppToObjStr(configuration.first)][cppToObjStr(arch.first)] = @{ @"cachedDylibs" : [includedDylibsSet array], @"mainExecutables" : [executablesSet array], @"other" : [otherSet array]};; + } + } + + NSError* error = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:jsonDict options:0x0 error:&error]; + (void)[jsonData writeToFile:cppToObjStr(path) atomically:YES]; +} + +void Manifest::write(const std::string& path) +{ + if (path.empty()) + return; + + NSMutableDictionary* cacheDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* projectDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* configurationsDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* resultsDict = [[NSMutableDictionary alloc] init]; + + cacheDict[@"manifest-version"] = @(version()); + cacheDict[@"build"] = cppToObjStr(build()); + cacheDict[@"dylibOrderFile"] = cppToObjStr(dylibOrderFile()); + cacheDict[@"dirtyDataOrderFile"] = cppToObjStr(dirtyDataOrderFile()); + cacheDict[@"metabomFile"] = cppToObjStr(metabomFile()); + + cacheDict[@"projects"] = projectDict; + cacheDict[@"results"] = resultsDict; + cacheDict[@"configurations"] = configurationsDict; + + for (const auto& project : projects()) { + NSMutableArray* sources = [[NSMutableArray alloc] init]; + + for (const auto& source : project.second.sources) { + [sources addObject:cppToObjStr(source)]; + } + + projectDict[cppToObjStr(project.first)] = sources; + } + + for (auto& configuration : _configurations) { + NSMutableArray* archArray = [[NSMutableArray alloc] init]; + for (auto& arch : configuration.second.architectures) { + [archArray addObject:cppToObjStr(arch.first)]; + } + + NSMutableArray* excludeTags = [[NSMutableArray alloc] init]; + for (const auto& excludeTag : configuration.second.metabomExcludeTags) { + [excludeTags addObject:cppToObjStr(excludeTag)]; + } + + configurationsDict[cppToObjStr(configuration.first)] = @{ + @"platformName" : cppToObjStr(configuration.second.platformName), + @"metabomTag" : cppToObjStr(configuration.second.metabomTag), + @"metabomExcludeTags" : excludeTags, + @"architectures" : archArray + }; + } + + for (auto& configuration : _configurations) { + NSMutableDictionary* archResultsDict = [[NSMutableDictionary alloc] init]; + for (auto& arch : configuration.second.architectures) { + NSMutableDictionary* dylibsDict = [[NSMutableDictionary alloc] init]; + NSMutableArray* warningsArray = [[NSMutableArray alloc] init]; + NSMutableDictionary* devRegionsDict = [[NSMutableDictionary alloc] init]; + NSMutableDictionary* prodRegionsDict = [[NSMutableDictionary alloc] init]; + NSString* prodCDHash = cppToObjStr(arch.second.results.productionCache.cdHash); + NSString* devCDHash = cppToObjStr(arch.second.results.developmentCache.cdHash); + + for (auto& dylib : arch.second.results.dylibs) { + NSMutableDictionary* dylibDict = [[NSMutableDictionary alloc] init]; + if (dylib.second.included) { + dylibDict[@"included"] = @YES; + } else { + dylibDict[@"included"] = @NO; + dylibDict[@"exclusionInfo"] = cppToObjStr(dylib.second.exclusionInfo); + } + dylibsDict[cppToObjStr(dylib.second.installname)] = dylibDict; + } + + for (auto& warning : arch.second.results.warnings) { + [warningsArray addObject:cppToObjStr(warning)]; + } + + BOOL built = arch.second.results.failure.empty(); + archResultsDict[cppToObjStr(arch.first)] = @{ + @"dylibs" : dylibsDict, + @"built" : @(built), + @"failure" : cppToObjStr(arch.second.results.failure), + @"productionCache" : @{ @"cdhash" : prodCDHash, @"regions" : prodRegionsDict }, + @"developmentCache" : @{ @"cdhash" : devCDHash, @"regions" : devRegionsDict }, + @"warnings" : warningsArray + }; + } + resultsDict[cppToObjStr(configuration.first)] = archResultsDict; + } + + switch (platform()) { + case Platform::iOS: + cacheDict[@"platform"] = @"ios"; + break; + case Platform::tvOS: + cacheDict[@"platform"] = @"tvos"; + break; + case Platform::watchOS: + cacheDict[@"platform"] = @"watchos"; + break; + case Platform::bridgeOS: + cacheDict[@"platform"] = @"bridgeos"; + break; + case Platform::macOS: + cacheDict[@"platform"] = @"macos"; + break; + case Platform::unknown: + cacheDict[@"platform"] = @"unknown"; + break; + } + + NSError* error = nil; + NSData* outData = [NSPropertyListSerialization dataWithPropertyList:cacheDict + format:NSPropertyListBinaryFormat_v1_0 + options:0 + error:&error]; + (void)[outData writeToFile:cppToObjStr(path) atomically:YES]; +} +} diff --git a/dyld/dyld3/shared-cache/ObjC1Abstraction.hpp b/dyld/dyld3/shared-cache/ObjC1Abstraction.hpp new file mode 100644 index 0000000..93bf751 --- /dev/null +++ b/dyld/dyld3/shared-cache/ObjC1Abstraction.hpp @@ -0,0 +1,231 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-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@ + */ + +#define OBJC_IMAGE_SUPPORTS_GC (1<<1) +#define OBJC_IMAGE_REQUIRES_GC (1<<2) + +template +struct objc_image_info { + uint32_t version; + uint32_t flags; + + uint32_t getFlags() INLINE { return P::E::get32(flags); } + + bool supportsGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_SUPPORTS_GC; } + bool requiresGCFlagSet() INLINE { return getFlags() & OBJC_IMAGE_REQUIRES_GC; } + + void setFlag(uint32_t bits) INLINE { uint32_t old = P::E::get32(flags); P::E::set32(flags, old | bits); } + void setOptimizedByDyld() INLINE { setFlag(1<<3); } +}; + +template +struct objc_method { + uint32_t method_name; // SEL + uint32_t method_types; // char * + uint32_t method_imp; // IMP + + uint32_t getName() const INLINE { return P::E::get32(method_name); } + void setName(uint32_t newName) INLINE { P::E::set32(method_name, newName); } +}; + +template +struct objc_method_list { + enum { OBJC_FIXED_UP = 1771 }; + uint32_t obsolete; // struct objc_method_list * + uint32_t method_count; // int + struct objc_method

method_list[0]; + + uint32_t getCount() const INLINE { return P::E::get32(method_count); } + void setFixedUp(bool fixed) INLINE { P::E::set32(obsolete, fixed ? OBJC_FIXED_UP : 0); } +}; + +template +struct objc_class { + uint32_t isa; // struct objc_class * + uint32_t super_class; // struct objc_class * + uint32_t name; // const char * + uint32_t version; // long + uint32_t info; // long + uint32_t instance_size; // long + uint32_t ivars; // struct objc_ivar_list * + uint32_t methodList; // struct objc_method_list * + uint32_t method_cache; // struct objc_cache * + uint32_t protocols; // objc_protocol_list * + uint32_t ivar_layout; // const char * + uint32_t ext; // struct objc_class_ext * + + struct objc_class

*getIsa(ContentAccessor* cache) const INLINE { return (struct objc_class

*)cache->contentForVMAddr(P::E::get32(isa)); } + struct objc_method_list

*getMethodList(ContentAccessor* cache) const INLINE { return (struct objc_method_list

*)cache->contentForVMAddr(P::E::get32(methodList)); } +}; + +template +struct objc_category { + uint32_t category_name; // char * + uint32_t class_name; // char * + uint32_t instance_methods; // struct objc_method_list * + uint32_t class_methods; // struct objc_method_list * + uint32_t protocols; // objc_protocol_list * + uint32_t size; // uint32_t + uint32_t instance_properties; // struct objc_property_list * + + struct objc_method_list

*getInstanceMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list

*)cache->contentForVMAddr(P::E::get32(instance_methods)); } + struct objc_method_list

*getClassMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list

*)cache->contentForVMAddr(P::E::get32(class_methods)); } +}; + +template +struct objc_symtab { + uint32_t sel_ref_cnt; // unsigned long + uint32_t refs; // SEL * + uint16_t cls_def_cnt; // unsigned short + uint16_t cat_def_cnt; // unsigned short + uint32_t defs[0]; // void * + + uint16_t getClassCount(void) const INLINE { return P::E::get16(cls_def_cnt); } + uint16_t getCategoryCount(void) const INLINE { return P::E::get16(cat_def_cnt); } + struct objc_class

*getClass(ContentAccessor* cache, int index) const INLINE { return (struct objc_class

*)cache->contentForVMAddr(P::E::get32(defs[index])); } + struct objc_category

*getCategory(ContentAccessor* cache, int index) const INLINE { return (struct objc_category

*)cache->contentForVMAddr(P::E::get32(defs[getClassCount() + index])); } +}; + +template +struct objc_module { + uint32_t version; // unsigned long + uint32_t size; // unsigned long + uint32_t name; // char* + uint32_t symtab; // Symtab + + struct objc_symtab

*getSymtab(ContentAccessor* cache) const INLINE { return (struct objc_symtab

*)cache->contentForVMAddr(P::E::get32(symtab)); } +}; + +template +struct objc_method_description { + uint32_t name; // SEL + uint32_t types; // char * + + uint32_t getName() const INLINE { return P::E::get32(name); } + void setName(uint32_t newName) INLINE { P::E::set32(name, newName); } +}; + +template +struct objc_method_description_list { + uint32_t count; // int + struct objc_method_description

list[0]; + + uint32_t getCount() const INLINE { return P::E::get32(count); } +}; + +template +struct objc_protocol { + uint32_t isa; // danger! contains strange values! + uint32_t protocol_name; // const char * + uint32_t protocol_list; // struct objc_protocol_list + uint32_t instance_methods; // struct objc_method_description_list * + uint32_t class_methods; // struct objc_method_description_list * + + struct objc_method_description_list

*getInstanceMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list

*)cache->contentForVMAddr(P::E::get32(instance_methods)); } + struct objc_method_description_list

*getClassMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list

*)cache->contentForVMAddr(P::E::get32(class_methods)); } +}; + + +template +class LegacySelectorUpdater { + typedef typename P::uint_t pint_t; + + static void visitMethodList(objc_method_list

*mlist, V& visitor) + { + for (uint32_t m = 0; m < mlist->getCount(); m++) { + pint_t oldValue = mlist->method_list[m].getName(); + pint_t newValue = visitor.visit(oldValue); + mlist->method_list[m].setName((uint32_t)newValue); + } + mlist->setFixedUp(true); + } + + static void visitMethodDescriptionList(objc_method_description_list

*mlist, V& visitor) + { + for (pint_t m = 0; m < mlist->getCount(); m++) { + pint_t oldValue = mlist->list[m].getName(); + pint_t newValue = visitor.visit(oldValue); + mlist->list[m].setName((uint32_t)newValue); + } + } + +public: + + static void update(ContentAccessor* cache, const macho_header

* header, V& visitor) + { + ArraySection > + modules(cache, header, "__OBJC", "__module_info"); + for (uint64_t m = 0; m < modules.count(); m++) { + objc_symtab

*symtab = modules.get(m).getSymtab(cache); + if (!symtab) continue; + + // Method lists in classes + for (int c = 0; c < symtab->getClassCount(); c++) { + objc_class

*cls = symtab->getClass(cache, c); + objc_class

*isa = cls->getIsa(cache); + objc_method_list

*mlist; + if ((mlist = cls->getMethodList(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = isa->getMethodList(cache))) { + visitMethodList(mlist, visitor); + } + } + + // Method lists from categories + for (int c = 0; c < symtab->getCategoryCount(); c++) { + objc_category

*cat = symtab->getCategory(cache, c); + objc_method_list

*mlist; + if ((mlist = cat->getInstanceMethods(cache))) { + visitMethodList(mlist, visitor); + } + if ((mlist = cat->getClassMethods(cache))) { + visitMethodList(mlist, visitor); + } + } + } + + // Method description lists from protocols + ArraySection> + protocols(cache, header, "__OBJC", "__protocol"); + for (uint64_t p = 0; p < protocols.count(); p++) { + objc_protocol

& protocol = protocols.get(p); + objc_method_description_list

*mlist; + if ((mlist = protocol.getInstanceMethodDescriptions(cache))) { + visitMethodDescriptionList(mlist, visitor); + } + if ((mlist = protocol.getClassMethodDescriptions(cache))) { + visitMethodDescriptionList(mlist, visitor); + } + } + + // Message refs + PointerSection selrefs(cache, header, "__OBJC", "__message_refs"); + for (pint_t s = 0; s < selrefs.count(); s++) { + pint_t oldValue = selrefs.getVMAddress(s); + pint_t newValue = visitor.visit(oldValue); + selrefs.setVMAddress(s, newValue); + } + } +}; diff --git a/dyld/dyld3/shared-cache/ObjC2Abstraction.hpp b/dyld/dyld3/shared-cache/ObjC2Abstraction.hpp new file mode 100644 index 0000000..59aa455 --- /dev/null +++ b/dyld/dyld3/shared-cache/ObjC2Abstraction.hpp @@ -0,0 +1,1217 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-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 +#include +#include + +// iterate an entsize-based list +// typedef entsize_iterator, type_list_t

> type_iterator; +template +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& operator += (ptrdiff_t count) { + current = (T*)((uint8_t *)current + count*entsize); + index += count; + return *this; + } + const entsize_iterator& operator -= (ptrdiff_t count) { + current = (T*)((uint8_t *)current - count*entsize); + index -= count; + return *this; + } + const entsize_iterator operator + (ptrdiff_t count) const { + return entsize_iterator(*this) += count; + } + const entsize_iterator operator - (ptrdiff_t count) const { + return entsize_iterator(*this) -= count; + } + + entsize_iterator& operator ++ () { *this += 1; return *this; } + entsize_iterator& operator -- () { *this -= 1; return *this; } + entsize_iterator operator ++ (int) { + entsize_iterator result(*this); *this += 1; return result; + } + entsize_iterator operator -- (int) { + entsize_iterator result(*this); *this -= 1; return result; + } + + ptrdiff_t operator - (const entsize_iterator& rhs) const { + return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index; + } + + T& operator * () { return *current; } + 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& rhs) { + return this->current == rhs.current; + } + bool operator != (const entsize_iterator& rhs) { + return this->current != rhs.current; + } + + bool operator < (const entsize_iterator& rhs) { + return this->current < rhs.current; + } + bool operator > (const entsize_iterator& rhs) { + return this->current > rhs.current; + } + + + static void overwrite(entsize_iterator& dst, const Tlist* srcList) + { + entsize_iterator src; + uint32_t ee = srcList->getEntsize(); + for (src = srcList->begin(); src != srcList->end(); ++src) { + memcpy(&*dst, &*src, ee); + ++dst; + } + } +}; + +template +class objc_header_info_rw_t { + + typedef typename P::uint_t pint_t; + + pint_t data; // loaded:1, allRealised:1, objc_header_info *:ptr + +public: + objc_header_info_rw_t(ContentAccessor* cache, const macho_header

* mh) + : data(0) { + } +}; + +template +class objc_header_info_ro_t { + + typedef typename P::uint_t pint_t; + + pint_t mhdr_offset; // offset to mach_header or mach_header_64 + pint_t info_offset; // offset to objc_image_info * + +public: + objc_header_info_ro_t(ContentAccessor* cache, const macho_header

* mh) + : mhdr_offset(0), info_offset(0) { + P::setP(mhdr_offset, (uint64_t)cache->vmAddrForContent((void*)mh) - (uint64_t)cache->vmAddrForContent(&mhdr_offset)); + assert(header_vmaddr(cache) == (uint64_t)cache->vmAddrForContent((void*)mh)); + const macho_section

* sect = mh->getSection("__DATA", "__objc_imageinfo"); + if (sect) { + P::setP(info_offset, (uint64_t)sect->addr() - (uint64_t)cache->vmAddrForContent(&info_offset)); + // set bit in mach_header.flags to tell dyld that this image has objc content + macho_header

* rwmh = const_cast*>(mh); + rwmh->set_flags(mh->flags() | MH_HAS_OBJC); + } + else + P::setP(info_offset, - (uint64_t)cache->vmAddrForContent(&info_offset)); + } + + pint_t header_vmaddr(ContentAccessor* cache) const { + return (pint_t)(((uint64_t)cache->vmAddrForContent(&mhdr_offset)) + mhdr_offset); + } +}; + + +template +class objc_method_list_t; // forward reference + + +template +class objc_method_t { + typedef typename P::uint_t pint_t; + pint_t name; // SEL + pint_t types; // const char * + pint_t imp; // IMP + friend class objc_method_list_t

; +public: + pint_t getName() const { return (pint_t)P::getP(name); } + void setName(pint_t newName) { P::setP(name, newName); } + + struct SortBySELAddress : + public std::binary_function&, + const objc_method_t

&, bool> + { + bool operator() (const objc_method_t

& lhs, + const objc_method_t

& rhs) + { + return lhs.getName() < rhs.getName(); + } + }; +}; + +template +class objc_method_list_t { + uint32_t entsize; + uint32_t count; + objc_method_t

first; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator, objc_method_list_t

> method_iterator; + + uint32_t getCount() const { return P::E::get32(count); } + + uint32_t getEntsize() const {return P::E::get32(entsize)&~(uint32_t)3;} + + objc_method_t

& get(uint32_t i) const { return *(objc_method_t

*)((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

)) { + return sizeof(objc_method_list_t

) - sizeof(objc_method_t

) + 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() { P::E::set32(entsize, getEntsize() | 3); } + + void getPointers(std::set& pointersToRemove) { + for(method_iterator it = begin(); it != end(); ++it) { + objc_method_t

& entry = *it; + pointersToRemove.insert(&(entry.name)); + pointersToRemove.insert(&(entry.types)); + pointersToRemove.insert(&(entry.imp)); + } + } + + static void addPointers(uint8_t* methodList, std::vector& pointersToAdd) { + objc_method_list_t

* mlist = (objc_method_list_t

*)methodList; + for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) { + objc_method_t

& entry = *it; + pointersToAdd.push_back(&(entry.name)); + pointersToAdd.push_back(&(entry.types)); + pointersToAdd.push_back(&(entry.imp)); + } + } + + static objc_method_list_t

* newMethodList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_method_list_t

(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_method_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_method_t

)) + : entsize(newEntsize), count(newCount) + { } + +private: + // use newMethodList instead + void* operator new (size_t); +}; + + +template +class objc_ivar_t { + typedef typename P::uint_t pint_t; + + pint_t offset; // uint32_t* (uint64_t* on x86_64) + pint_t name; // const char* + pint_t type; // const char* + uint32_t alignment; + uint32_t size; + +public: + const char* getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + bool hasOffset() const { return offset != 0; } + uint32_t getOffset(ContentAccessor* cache) const { return P::E::get32(*(uint32_t*)(cache->contentForVMAddr(P::getP(offset)))); } + void setOffset(ContentAccessor* cache, uint32_t newOffset) { P::E::set32(*(uint32_t*)(cache->contentForVMAddr(P::getP(offset))), newOffset); } + + + uint32_t getAlignment() { + uint32_t a = P::E::get32(alignment); + return (a == (uint32_t)-1) ? sizeof(pint_t) : 1< +class objc_ivar_list_t { + typedef typename P::uint_t pint_t; + uint32_t entsize; + uint32_t count; + objc_ivar_t

first; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator, objc_ivar_list_t

> ivar_iterator; + + uint32_t getCount() const { return P::E::get32(count); } + + uint32_t getEntsize() const { return P::E::get32(entsize); } + + objc_ivar_t

& get(pint_t i) const { return *(objc_ivar_t

*)((uint8_t *)&first + i * 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

)) { + return sizeof(objc_ivar_list_t

) - sizeof(objc_ivar_t

) + 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

* newIvarList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_ivar_list_t

(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_ivar_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_ivar_t

)) + : entsize(newEntsize), count(newCount) + { } +private: + // use newIvarList instead + void* operator new (size_t); +}; + + +template class objc_property_list_t; // forward + +template +class objc_property_t { + typedef typename P::uint_t pint_t; + pint_t name; + pint_t attributes; + friend class objc_property_list_t

; +public: + + const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + const char * getAttributes(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(attributes)); } +}; + +template +class objc_property_list_t { + uint32_t entsize; + uint32_t count; + objc_property_t

first; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + typedef entsize_iterator, objc_property_list_t

> property_iterator; + + uint32_t getCount() const { return P::E::get32(count); } + + uint32_t getEntsize() const { return P::E::get32(entsize); } + + objc_property_t

& get(uint32_t i) const { return *(objc_property_t

*)((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

)) { + return sizeof(objc_property_list_t

) - sizeof(objc_property_t

) + 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& pointersToRemove) { + for(property_iterator it = begin(); it != end(); ++it) { + objc_property_t

& entry = *it; + pointersToRemove.insert(&(entry.name)); + pointersToRemove.insert(&(entry.attributes)); + } + } + + static void addPointers(uint8_t* propertyList, std::vector& pointersToAdd) { + objc_property_list_t

* plist = (objc_property_list_t

*)propertyList; + for(property_iterator it = plist->begin(); it != plist->end(); ++it) { + objc_property_t

& entry = *it; + pointersToAdd.push_back(&(entry.name)); + pointersToAdd.push_back(&(entry.attributes)); + } + } + + static objc_property_list_t

* newPropertyList(size_t newCount, uint32_t newEntsize) { + void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1); + return new (buf) objc_property_list_t

(newCount, newEntsize); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_property_list_t(uint32_t newCount, + uint32_t newEntsize = sizeof(objc_property_t

)) + : entsize(newEntsize), count(newCount) + { } +private: + // use newPropertyList instead + void* operator new (size_t); +}; + + +template class objc_protocol_list_t; // forward reference + +template +class objc_protocol_t { + typedef typename P::uint_t pint_t; + + pint_t isa; + pint_t name; + pint_t protocols; + pint_t instanceMethods; + pint_t classMethods; + pint_t optionalInstanceMethods; + pint_t optionalClassMethods; + pint_t instanceProperties; + uint32_t size; + uint32_t flags; + pint_t extendedMethodTypes; + pint_t demangledName; + pint_t classProperties; + +public: + pint_t getIsaVMAddr() const { return (pint_t)P::getP(isa); } + void setIsaVMAddr(pint_t newIsa) { P::setP(isa, newIsa); } + + const char *getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + uint32_t getSize() const { return P::E::get32(size); } + void setSize(uint32_t newSize) { P::E::set32(size, newSize); } + + uint32_t getFlags() const { return P::E::get32(flags); } + + void setFixedUp() { P::E::set32(flags, getFlags() | (1<<30)); } + + objc_protocol_list_t

*getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t

*)cache->contentForVMAddr(P::getP(protocols)); } + + objc_method_list_t

*getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(instanceMethods)); } + + objc_method_list_t

*getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(classMethods)); } + + objc_method_list_t

*getOptionalInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(optionalInstanceMethods)); } + + objc_method_list_t

*getOptionalClassMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(optionalClassMethods)); } + + objc_property_list_t

*getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t

*)cache->contentForVMAddr(P::getP(instanceProperties)); } + + pint_t *getExtendedMethodTypes(ContentAccessor* cache) const { + if (getSize() < offsetof(objc_protocol_t

, extendedMethodTypes) + sizeof(extendedMethodTypes)) { + return NULL; + } + return (pint_t *)cache->contentForVMAddr(P::getP(extendedMethodTypes)); + } + + const char *getDemangledName(ContentAccessor* cache) const { + if (sizeof(*this) < offsetof(objc_protocol_t

, demangledName) + sizeof(demangledName)) { + return NULL; + } + return (const char *)cache->contentForVMAddr(P::getP(demangledName)); + } + + void setDemangledName(ContentAccessor* cache, const char *newName, Diagnostics& diag) { + if (sizeof(*this) < offsetof(objc_protocol_t

, demangledName) + sizeof(demangledName)) + diag.error("objc protocol has the wrong size"); + else + P::setP(demangledName, cache->vmAddrForContent((void*)newName)); + } + + void addPointers(std::vector& pointersToAdd) + { + pointersToAdd.push_back(&isa); + pointersToAdd.push_back(&name); + if (protocols) pointersToAdd.push_back(&protocols); + if (instanceMethods) pointersToAdd.push_back(&instanceMethods); + if (classMethods) pointersToAdd.push_back(&classMethods); + if (optionalInstanceMethods) pointersToAdd.push_back(&optionalInstanceMethods); + if (optionalClassMethods) pointersToAdd.push_back(&optionalClassMethods); + if (instanceProperties) pointersToAdd.push_back(&instanceProperties); + if (extendedMethodTypes) pointersToAdd.push_back(&extendedMethodTypes); + if (demangledName) pointersToAdd.push_back(&demangledName); + } +}; + + +template +class objc_protocol_list_t { + typedef typename P::uint_t pint_t; + pint_t count; + pint_t list[0]; + + void* operator new (size_t, void* buf) { return buf; } + +public: + + pint_t getCount() const { return (pint_t)P::getP(count); } + + pint_t getVMAddress(pint_t i) { + return (pint_t)P::getP(list[i]); + } + + objc_protocol_t

* get(ContentAccessor* cache, pint_t i) { + return (objc_protocol_t

*)cache->contentForVMAddr(getVMAddress(i)); + } + + void setVMAddress(pint_t i, pint_t protoVMAddr) { + P::setP(list[i], protoVMAddr); + } + + void set(ContentAccessor* cache, pint_t i, objc_protocol_t

* proto) { + setVMAddress(i, cache->vmAddrForContent(proto)); + } + + uint32_t byteSize() const { + return byteSizeForCount(getCount()); + } + static uint32_t byteSizeForCount(pint_t c) { + return sizeof(objc_protocol_list_t

) + c*sizeof(pint_t); + } + + void getPointers(std::set& pointersToRemove) { + for(int i=0 ; i < count; ++i) { + pointersToRemove.insert(&list[i]); + } + } + + static void addPointers(uint8_t* protocolList, std::vector& pointersToAdd) { + objc_protocol_list_t

* plist = (objc_protocol_list_t

*)protocolList; + for(int i=0 ; i < plist->count; ++i) { + pointersToAdd.push_back(&plist->list[i]); + } + } + + static objc_protocol_list_t

* newProtocolList(pint_t newCount) { + void *buf = ::calloc(byteSizeForCount(newCount), 1); + return new (buf) objc_protocol_list_t

(newCount); + } + + void operator delete(void * p) { + ::free(p); + } + + objc_protocol_list_t(uint32_t newCount) : count(newCount) { } +private: + // use newProtocolList instead + void* operator new (size_t); +}; + + +template +class objc_class_data_t { + typedef typename P::uint_t pint_t; + uint32_t flags; + uint32_t instanceStart; + // 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; + pint_t pad; + } instanceSize; + pint_t ivarLayout; + pint_t name; + pint_t baseMethods; + pint_t baseProtocols; + pint_t ivars; + pint_t weakIvarLayout; + pint_t baseProperties; + +public: + bool isMetaClass() { return P::E::get32(flags) & (1 << 0); } + bool isRootClass() { return P::E::get32(flags) & (1 << 1); } + + uint32_t getInstanceStart() { return P::E::get32(instanceStart); } + void setInstanceStart(uint32_t newStart) { P::E::set32(instanceStart, newStart); } + + uint32_t getInstanceSize() { return P::E::get32(instanceSize.instanceSize); } + void setInstanceSize(uint32_t newSiz) { P::E::set32(instanceSize.instanceSize, newSiz); } + + objc_method_list_t

*getMethodList(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(baseMethods)); } + + objc_protocol_list_t

*getProtocolList(ContentAccessor* cache) const { return (objc_protocol_list_t

*)cache->contentForVMAddr(P::getP(baseProtocols)); } + + objc_ivar_list_t

*getIvarList(ContentAccessor* cache) const { return (objc_ivar_list_t

*)cache->contentForVMAddr(P::getP(ivars)); } + + objc_property_list_t

*getPropertyList(ContentAccessor* cache) const { return (objc_property_list_t

*)cache->contentForVMAddr(P::getP(baseProperties)); } + + const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + void setMethodList(ContentAccessor* cache, objc_method_list_t

* mlist) { + P::setP(baseMethods, cache->vmAddrForContent(mlist)); + } + + void setProtocolList(ContentAccessor* cache, objc_protocol_list_t

* protolist) { + P::setP(baseProtocols, cache->vmAddrForContent(protolist)); + } + + void setPropertyList(ContentAccessor* cache, objc_property_list_t

* proplist) { + P::setP(baseProperties, cache->vmAddrForContent(proplist)); + } + + void addMethodListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseMethods); + } + + void addPropertyListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseProperties); + } + + void addProtocolListPointer(std::vector& pointersToAdd) { + pointersToAdd.push_back(&this->baseProtocols); + } +}; + +template +class objc_class_t { + typedef typename P::uint_t pint_t; + + pint_t isa; + pint_t superclass; + pint_t method_cache; + pint_t vtable; + pint_t data; + +public: + bool isMetaClass(ContentAccessor* cache) const { return getData(cache)->isMetaClass(); } + bool isRootClass(ContentAccessor* cache) const { return getData(cache)->isRootClass(); } + + objc_class_t

*getIsa(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(isa)); } + + objc_class_t

*getSuperclass(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(superclass)); } + + // Low bit marks Swift classes. + objc_class_data_t

*getData(ContentAccessor* cache) const { return (objc_class_data_t

*)cache->contentForVMAddr(P::getP(data & ~0x1LL)); } + + objc_method_list_t

*getMethodList(ContentAccessor* cache) const { + objc_class_data_t

* d = getData(cache); + return d->getMethodList(cache); + } + + objc_protocol_list_t

*getProtocolList(ContentAccessor* cache) const { return getData(cache)->getProtocolList(cache); } + + objc_property_list_t

*getPropertyList(ContentAccessor* cache) const { return getData(cache)->getPropertyList(cache); } + + const char* getName(ContentAccessor* cache) const { + return getData(cache)->getName(cache); + } + + void setMethodList(ContentAccessor* cache, objc_method_list_t

* mlist) { + getData(cache)->setMethodList(cache, mlist); + } + + void setProtocolList(ContentAccessor* cache, objc_protocol_list_t

* protolist) { + getData(cache)->setProtocolList(cache, protolist); + } + + void setPropertyList(ContentAccessor* cache, objc_property_list_t

* proplist) { + getData(cache)->setPropertyList(cache, proplist); + } + + void addMethodListPointer(ContentAccessor* cache, std::vector& pointersToAdd) { + getData(cache)->addMethodListPointer(pointersToAdd); + } + + void addPropertyListPointer(ContentAccessor* cache, std::vector& pointersToAdd) { + getData(cache)->addPropertyListPointer(pointersToAdd); + } + + void addProtocolListPointer(ContentAccessor* cache, std::vector& pointersToAdd) { + getData(cache)->addProtocolListPointer(pointersToAdd); + } + +}; + + + +template +class objc_category_t { + typedef typename P::uint_t pint_t; + + pint_t name; + pint_t cls; + pint_t instanceMethods; + pint_t classMethods; + pint_t protocols; + pint_t instanceProperties; + +public: + + const char * getName(ContentAccessor* cache) const { return (const char *)cache->contentForVMAddr(P::getP(name)); } + + objc_class_t

*getClass(ContentAccessor* cache) const { return (objc_class_t

*)cache->contentForVMAddr(P::getP(cls)); } + + objc_method_list_t

*getInstanceMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(instanceMethods)); } + + objc_method_list_t

*getClassMethods(ContentAccessor* cache) const { return (objc_method_list_t

*)cache->contentForVMAddr(P::getP(classMethods)); } + + objc_protocol_list_t

*getProtocols(ContentAccessor* cache) const { return (objc_protocol_list_t

*)cache->contentForVMAddr(P::getP(protocols)); } + + objc_property_list_t

*getInstanceProperties(ContentAccessor* cache) const { return (objc_property_list_t

*)cache->contentForVMAddr(P::getP(instanceProperties)); } + + void getPointers(std::set& pointersToRemove) { + pointersToRemove.insert(&name); + pointersToRemove.insert(&cls); + pointersToRemove.insert(&instanceMethods); + pointersToRemove.insert(&classMethods); + pointersToRemove.insert(&protocols); + pointersToRemove.insert(&instanceProperties); + } + + +}; + +template +class objc_message_ref_t { + typedef typename P::uint_t pint_t; + + pint_t imp; + pint_t sel; + +public: + pint_t getName() const { return (pint_t)P::getP(sel); } + + void setName(pint_t newName) { P::setP(sel, newName); } +}; + +// Call visitor.visitIvar() on every ivar in a given class. +template +class IvarWalker { + typedef typename P::uint_t pint_t; + V& ivarVisitor; +public: + + IvarWalker(V& visitor) : ivarVisitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header, objc_class_t

*cls) + { + objc_class_data_t

*data = cls->getData(cache); + objc_ivar_list_t

*ivars = data->getIvarList(cache); + if (ivars) { + for (pint_t i = 0; i < ivars->getCount(); i++) { + objc_ivar_t

& ivar = ivars->get(i); + //fprintf(stderr, "visiting ivar: %s\n", ivar.getName(cache)); + ivarVisitor.visitIvar(cache, header, cls, &ivar); + } + } else { + //fprintf(stderr, "no ivars\n"); + } + } + + void visitClass(ContentAccessor* cache, const macho_header

* header, objc_class_t

*cls) + { + walk(cache, header, cls); + } +}; + +// Call visitor.visitClass() on every class. +template +class ClassWalker { + typedef typename P::uint_t pint_t; + V& _visitor; +public: + + ClassWalker(V& visitor) : _visitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header) + { + PointerSection*> classList(cache, header, "__DATA", "__objc_classlist"); + + for (pint_t i = 0; i < classList.count(); i++) { + objc_class_t

* cls = classList.get(i); + //fprintf(stderr, "visiting class: %s\n", cls->getName(cache)); + if (cls) _visitor.visitClass(cache, header, cls); + } + } +}; + +// Call visitor.visitProtocol() on every protocol. +template +class ProtocolWalker { + typedef typename P::uint_t pint_t; + V& _protocolVisitor; +public: + + ProtocolWalker(V& visitor) : _protocolVisitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header) + { + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

*proto = protocols.get(i); + _protocolVisitor.visitProtocol(cache, header, proto); + } + } +}; + +// Call visitor.visitProtocolReference() on every protocol. +template +class ProtocolReferenceWalker { + typedef typename P::uint_t pint_t; + V& _visitor; + + void visitProtocolList(ContentAccessor* cache, + objc_protocol_list_t

* protolist) + { + if (!protolist) return; + for (pint_t i = 0; i < protolist->getCount(); i++) { + pint_t oldValue = protolist->getVMAddress(i); + pint_t newValue = _visitor.visitProtocolReference(cache, oldValue); + protolist->setVMAddress(i, newValue); + } + } + + friend class ClassWalker>; + + void visitClass(ContentAccessor* cache, const macho_header

*, + objc_class_t

* cls) + { + visitProtocolList(cache, cls->getProtocolList(cache)); + visitProtocolList(cache, cls->getIsa(cache)->getProtocolList(cache)); + } + +public: + + ProtocolReferenceWalker(V& visitor) : _visitor(visitor) { } + void walk(ContentAccessor* cache, const macho_header

* header) + { + // @protocol expressions + PointerSection *> + protorefs(cache, header, "__DATA", "__objc_protorefs"); + for (pint_t i = 0; i < protorefs.count(); i++) { + pint_t oldValue = protorefs.getVMAddress(i); + pint_t newValue = _visitor.visitProtocolReference(cache, oldValue); + protorefs.setVMAddress(i, newValue); + } + + // protocol lists in classes + ClassWalker> classes(*this); + classes.walk(cache, header); + + // protocol lists in protocols + // __objc_protolists itself is NOT updated + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

* proto = protocols.get(i); + visitProtocolList(cache, proto->getProtocols(cache)); + // not recursive: every old protocol object + // must be in some protolist section somewhere + } + } +}; + +// Call visitor.visitMethodList(mlist) on every +// class and category method list in a header. +// Call visitor.visitProtocolMethodList(mlist, typelist) on every +// protocol method list in a header. +template +class MethodListWalker { + + typedef typename P::uint_t pint_t; + + V& mVisitor; + +public: + + MethodListWalker(V& visitor) : mVisitor(visitor) { } + + void walk(ContentAccessor* cache, const macho_header

* header) + { + // Method lists in classes + PointerSection *> + classes(cache, header, "__DATA", "__objc_classlist"); + + for (pint_t i = 0; i < classes.count(); i++) { + objc_class_t

*cls = classes.get(i); + objc_method_list_t

*mlist; + if ((mlist = cls->getMethodList(cache))) { + mVisitor.visitMethodList(mlist); + } + if ((mlist = cls->getIsa(cache)->getMethodList(cache))) { + mVisitor.visitMethodList(mlist); + } + } + + // Method lists from categories + PointerSection *> + cats(cache, header, "__DATA", "__objc_catlist"); + for (pint_t i = 0; i < cats.count(); i++) { + objc_category_t

*cat = cats.get(i); + objc_method_list_t

*mlist; + if ((mlist = cat->getInstanceMethods(cache))) { + mVisitor.visitMethodList(mlist); + } + if ((mlist = cat->getClassMethods(cache))) { + mVisitor.visitMethodList(mlist); + } + } + + // Method description lists from protocols + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

*proto = protocols.get(i); + objc_method_list_t

*mlist; + pint_t *typelist = proto->getExtendedMethodTypes(cache); + + if ((mlist = proto->getInstanceMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getClassMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getOptionalInstanceMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + if ((mlist = proto->getOptionalClassMethods(cache))) { + mVisitor.visitProtocolMethodList(mlist, typelist); + if (typelist) typelist += mlist->getCount(); + } + } + } +}; + + +// Update selector references. The visitor performs recording and uniquing. +template +class SelectorOptimizer { + + typedef typename P::uint_t pint_t; + + V& mVisitor; + + friend class MethodListWalker >; + void visitMethodList(objc_method_list_t

*mlist) + { + // Gather selectors. Update method names. + for (uint32_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. + } + + void visitProtocolMethodList(objc_method_list_t

*mlist, pint_t *types) + { + visitMethodList(mlist); + } + +public: + + SelectorOptimizer(V& visitor) : mVisitor(visitor) { } + + void optimize(ContentAccessor* cache, const macho_header

* header) + { + // method lists in classes, categories, and protocols + MethodListWalker > mw(*this); + mw.walk(cache, header); + + // @selector references + PointerSection + selrefs(cache, header, "__DATA", "__objc_selrefs"); + for (pint_t i = 0; i < selrefs.count(); i++) { + pint_t oldValue = selrefs.getVMAddress(i); + pint_t newValue = mVisitor.visit(oldValue); + selrefs.setVMAddress(i, newValue); + } + + // message references + ArraySection > + msgrefs(cache, header, "__DATA", "__objc_msgrefs"); + for (pint_t i = 0; i < msgrefs.count(); i++) { + objc_message_ref_t

& msg = msgrefs.get(i); + pint_t oldValue = msg.getName(); + pint_t newValue = mVisitor.visit(oldValue); + msg.setName(newValue); + } + } +}; + + +// Update selector references. The visitor performs recording and uniquing. +template +class IvarOffsetOptimizer { + uint32_t _slide; + uint32_t _maxAlignment; + uint32_t _optimized; + +public: + + IvarOffsetOptimizer() : _optimized(0) { } + + size_t optimized() const { return _optimized; } + + // dual purpose ivar visitor function + // if slide!=0 then slides the ivar by that amount, otherwise computes _maxAlignment + void visitIvar(ContentAccessor* cache, const macho_header

* /*unused, may be NULL*/, objc_class_t

*cls, objc_ivar_t

*ivar) + { + if (_slide == 0) { + uint32_t alignment = ivar->getAlignment(); + if (alignment > _maxAlignment) _maxAlignment = alignment; + } else { + // skip anonymous bitfields + if (ivar->hasOffset()) { + uint32_t oldOffset = (uint32_t)ivar->getOffset(cache); + ivar->setOffset(cache, oldOffset + _slide); + _optimized++; + //fprintf(stderr, "%d -> %d for %s.%s\n", oldOffset, oldOffset + _slide, cls->getName(cache), ivar->getName(cache)); + } else { + //fprintf(stderr, "NULL offset\n"); + } + } + } + + // Class visitor function. Evaluates whether to slide ivars and performs slide if needed. + // The slide algorithm is also implemented in objc. Any changes here should be reflected there also. + void visitClass(ContentAccessor* cache, const macho_header

* /*unused, may be NULL*/, objc_class_t

*cls) + { + objc_class_t

*super = cls->getSuperclass(cache); + if (super) { + // Recursively visit superclasses to ensure we have the correct superclass start + // Note that we don't need the macho_header, so just pass NULL. + visitClass(cache, nullptr, super); + + objc_class_data_t

*data = cls->getData(cache); + objc_class_data_t

*super_data = super->getData(cache); + int32_t diff = super_data->getInstanceSize() - data->getInstanceStart(); + if (diff > 0) { + IvarWalker > ivarVisitor(*this); + _maxAlignment = 1; + _slide = 0; + + // This walk computes _maxAlignment + ivarVisitor.walk(cache, nullptr, cls); + + // Compute a slide value that preserves that alignment + uint32_t alignMask = _maxAlignment - 1; + if (diff & alignMask) diff = (diff + alignMask) & ~alignMask; + + // Slide all of this class's ivars en masse + _slide = diff; + if (_slide != 0) { + //fprintf(stderr, "Sliding ivars in %s by %u (superclass was %d, now %d)\n", cls->getName(cache), _slide, data->getInstanceStart(), super_data->getInstanceSize()); + ivarVisitor.walk(cache, nullptr, cls); + data->setInstanceStart(data->getInstanceStart() + _slide); + data->setInstanceSize(data->getInstanceSize() + _slide); + } + } + } + } + + // Enumerates objc classes in the module and performs any ivar slides + void optimize(ContentAccessor* cache, const macho_header

* header) + { + // The slide code cannot fix up GC layout strings so skip modules that support or require GC + const macho_section

*imageInfoSection = header->getSection("__DATA", "__objc_imageinfo"); + if (imageInfoSection) { + objc_image_info

*info = (objc_image_info

*)cache->contentForVMAddr(imageInfoSection->addr()); + if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) { + ClassWalker > classVisitor(*this); + classVisitor.walk(cache, header); + } else { + //fprintf(stderr, "GC support present - skipped module\n"); + } + } + } +}; + + +// Detect classes that have missing weak-import superclasses. +template +class WeakClassDetector { + bool noMissing; + + friend class ClassWalker>; + void visitClass(ContentAccessor* cache, const macho_header

*, + objc_class_t

* cls) + { + auto supercls = cls->getSuperclass(cache); + if (supercls) { + // okay: class with superclass + // Note that the superclass itself might have a missing superclass. + // That is fine for mere detection because we will visit the + // superclass separately. + } else if (cls->isRootClass(cache)) { + // okay: root class is expected to have no superclass + } else { + // bad: cls's superclass is missing. + cache->diagnostics().warning("Superclass of class '%s' is weak-import and missing.", + cls->getName(cache)); + noMissing = false; + } + } + +public: + bool noMissingWeakSuperclasses(ContentAccessor* cache, + std::vector*> dylibs) + { + noMissing = true; + ClassWalker> classes(*this); + for (auto mh : dylibs) { + classes.walk(cache, mh); + } + return noMissing; + } +}; + + +// Sort methods in place by selector. +template +class MethodListSorter { + + typedef typename P::uint_t pint_t; + + uint32_t _optimized; + + friend class MethodListWalker >; + void visitMethodList(objc_method_list_t

*mlist) + { + typename objc_method_t

::SortBySELAddress sorter; + std::stable_sort(mlist->begin(), mlist->end(), sorter); + mlist->setFixedUp(); + _optimized++; + } + + void visitProtocolMethodList(objc_method_list_t

*mlist, pint_t *typelist) + { + typename objc_method_t

::SortBySELAddress sorter; + // can't easily use std::stable_sort here + for (uint32_t i = 0; i < mlist->getCount(); i++) { + for (uint32_t j = i+1; j < mlist->getCount(); j++) { + objc_method_t

& mi = mlist->get(i); + objc_method_t

& mj = mlist->get(j); + if (! sorter(mi, mj)) { + std::swap(mi, mj); + if (typelist) std::swap(typelist[i], typelist[j]); + } + } + } + + mlist->setFixedUp(); + _optimized++; + } + +public: + MethodListSorter() : _optimized(0) { } + + size_t optimized() const { return _optimized; } + + void optimize(ContentAccessor* cache, const macho_header

* header) + { + MethodListWalker > mw(*this); + mw.walk(cache, header); + } +}; + + +template +class HeaderInfoOptimizer { +public: + + typedef typename P::uint_t pint_t; + + HeaderInfoOptimizer() : _hInfos(0), _count(0) { } + + const char* init(uint32_t count, uint8_t*& buf, size_t& bufSize) { + if (count == 0) + return nullptr; + + size_t requiredSize = + 2*sizeof(uint32_t) + count*sizeof(InfoT); + if (bufSize < requiredSize) { + return "libobjc's read/write section is too small (metadata not optimized)"; + } + + uint32_t *buf32 = (uint32_t *)buf; + P::E::set32(buf32[0], count); + P::E::set32(buf32[1], sizeof(InfoT)); + _hInfos = (InfoT*)(buf32+2); + + buf += requiredSize; + bufSize -= requiredSize; + + return nullptr; + } + + void update(ContentAccessor* cache, const macho_header

* mh, std::vector& pointersInData) { + InfoT* hi = new(&_hInfos[_count++]) InfoT(cache, mh); + (void)hi; + } + + InfoT* hinfoForHeader(ContentAccessor* cache, const macho_header

* mh) { + // FIXME: could be binary search + uint64_t mh_vmaddr = cache->vmAddrForContent((void*)mh); + for (size_t i = 0; i < _count; i++) { + InfoT* hi = &_hInfos[i]; + if (hi->header_vmaddr(cache) == mh_vmaddr) return hi; + } + return nullptr; + } +private: + InfoT* _hInfos; + size_t _count; +}; diff --git a/dyld/dyld3/shared-cache/OptimizerBranches.cpp b/dyld/dyld3/shared-cache/OptimizerBranches.cpp new file mode 100644 index 0000000..332e045 --- /dev/null +++ b/dyld/dyld3/shared-cache/OptimizerBranches.cpp @@ -0,0 +1,1466 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "StringUtils.h" +#include "Trie.hpp" +#include "MachOFileAbstraction.hpp" +#include "MachOParser.h" +#include "Diagnostics.h" +#include "DyldSharedCache.h" +#include "CacheBuilder.h" + +static const bool verbose = false; + +// These are functions that are interposed by Instruments.app or ASan +static const char* sNeverStubEliminateSymbols[] = { + "___bzero", + "___cxa_atexit", + "___cxa_throw", + "__longjmp", + "__objc_autoreleasePoolPop", + "_accept", + "_access", + "_asctime", + "_asctime_r", + "_asprintf", + "_atoi", + "_atol", + "_atoll", + "_calloc", + "_chmod", + "_chown", + "_close", + "_confstr", + "_ctime", + "_ctime_r", + "_dispatch_after", + "_dispatch_after_f", + "_dispatch_async", + "_dispatch_async_f", + "_dispatch_barrier_async_f", + "_dispatch_group_async", + "_dispatch_group_async_f", + "_dispatch_source_set_cancel_handler", + "_dispatch_source_set_event_handler", + "_dispatch_sync_f", + "_dlclose", + "_dlopen", + "_dup", + "_dup2", + "_endgrent", + "_endpwent", + "_ether_aton", + "_ether_hostton", + "_ether_line", + "_ether_ntoa", + "_ether_ntohost", + "_fchmod", + "_fchown", + "_fclose", + "_fdopen", + "_fflush", + "_fopen", + "_fork", + "_fprintf", + "_free", + "_freopen", + "_frexp", + "_frexpf", + "_frexpl", + "_fscanf", + "_fstat", + "_fstatfs", + "_fstatfs64", + "_fsync", + "_ftime", + "_getaddrinfo", + "_getattrlist", + "_getcwd", + "_getgrent", + "_getgrgid", + "_getgrgid_r", + "_getgrnam", + "_getgrnam_r", + "_getgroups", + "_gethostbyaddr", + "_gethostbyname", + "_gethostbyname2", + "_gethostent", + "_getifaddrs", + "_getitimer", + "_getnameinfo", + "_getpass", + "_getpeername", + "_getpwent", + "_getpwnam", + "_getpwnam_r", + "_getpwuid", + "_getpwuid_r", + "_getsockname", + "_getsockopt", + "_gmtime", + "_gmtime_r", + "_if_indextoname", + "_if_nametoindex", + "_index", + "_inet_aton", + "_inet_ntop", + "_inet_pton", + "_initgroups", + "_ioctl", + "_lchown", + "_lgamma", + "_lgammaf", + "_lgammal", + "_link", + "_listxattr", + "_localtime", + "_localtime_r", + "_longjmp", + "_lseek", + "_lstat", + "_malloc", + "_malloc_create_zone", + "_malloc_default_purgeable_zone", + "_malloc_default_zone", + "_malloc_good_size", + "_malloc_make_nonpurgeable", + "_malloc_make_purgeable", + "_malloc_set_zone_name", + "_mbsnrtowcs", + "_mbsrtowcs", + "_mbstowcs", + "_memchr", + "_memcmp", + "_memcpy", + "_memmove", + "_memset", + "_mktime", + "_mlock", + "_mlockall", + "_modf", + "_modff", + "_modfl", + "_munlock", + "_munlockall", + "_objc_autoreleasePoolPop", + "_objc_setProperty", + "_objc_setProperty_atomic", + "_objc_setProperty_atomic_copy", + "_objc_setProperty_nonatomic", + "_objc_setProperty_nonatomic_copy", + "_objc_storeStrong", + "_open", + "_opendir", + "_poll", + "_posix_memalign", + "_pread", + "_printf", + "_pthread_attr_getdetachstate", + "_pthread_attr_getguardsize", + "_pthread_attr_getinheritsched", + "_pthread_attr_getschedparam", + "_pthread_attr_getschedpolicy", + "_pthread_attr_getscope", + "_pthread_attr_getstack", + "_pthread_attr_getstacksize", + "_pthread_condattr_getpshared", + "_pthread_create", + "_pthread_getschedparam", + "_pthread_join", + "_pthread_mutex_lock", + "_pthread_mutex_unlock", + "_pthread_mutexattr_getprioceiling", + "_pthread_mutexattr_getprotocol", + "_pthread_mutexattr_getpshared", + "_pthread_mutexattr_gettype", + "_pthread_rwlockattr_getpshared", + "_pwrite", + "_rand_r", + "_read", + "_readdir", + "_readdir_r", + "_readv", + "_readv$UNIX2003", + "_realloc", + "_realpath", + "_recv", + "_recvfrom", + "_recvmsg", + "_remquo", + "_remquof", + "_remquol", + "_scanf", + "_send", + "_sendmsg", + "_sendto", + "_setattrlist", + "_setgrent", + "_setitimer", + "_setlocale", + "_setpwent", + "_shm_open", + "_shm_unlink", + "_sigaction", + "_sigemptyset", + "_sigfillset", + "_siglongjmp", + "_signal", + "_sigpending", + "_sigprocmask", + "_sigwait", + "_snprintf", + "_sprintf", + "_sscanf", + "_stat", + "_statfs", + "_statfs64", + "_strcasecmp", + "_strcat", + "_strchr", + "_strcmp", + "_strcpy", + "_strdup", + "_strerror", + "_strerror_r", + "_strlen", + "_strncasecmp", + "_strncat", + "_strncmp", + "_strncpy", + "_strptime", + "_strtoimax", + "_strtol", + "_strtoll", + "_strtoumax", + "_tempnam", + "_time", + "_times", + "_tmpnam", + "_tsearch", + "_unlink", + "_valloc", + "_vasprintf", + "_vfprintf", + "_vfscanf", + "_vprintf", + "_vscanf", + "_vsnprintf", + "_vsprintf", + "_vsscanf", + "_wait", + "_wait$UNIX2003", + "_wait3", + "_wait4", + "_waitid", + "_waitid$UNIX2003", + "_waitpid", + "_waitpid$UNIX2003", + "_wcslen", + "_wcsnrtombs", + "_wcsrtombs", + "_wcstombs", + "_wordexp", + "_write", + "_writev", + "_writev$UNIX2003", + // always use stubs for C++ symbols that can be overridden + "__ZdaPv", + "__ZdlPv", + "__Znam", + "__Znwm", + + nullptr +}; + + +static uint64_t branchPoolTextSize(const std::string& archName) +{ + if ( startsWith(archName, "arm64") ) + return 0x0000C000; // 48KB + else + return 0; +} + +static uint64_t branchPoolLinkEditSize(const std::string& archName) +{ + if ( startsWith(archName, "arm64") ) + return 0x00100000; // 1MB + else + return 0; +} + + +template +class BranchPoolDylib { +public: + BranchPoolDylib(DyldSharedCache* cache, uint64_t startAddr, + uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags); + + uint64_t addr() { return _startAddr; } + uint64_t getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools); + uint64_t getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools); + void finalizeLoadCommands(); + void printStats(); + +private: + Diagnostics& _diagnostics; + uint64_t indexToAddr(uint32_t index) { return _startAddr + _firstStubOffset + sizeof(uint32_t)*index; } + + static const int64_t b128MegLimit = 0x07FFFFFF; + + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + DyldSharedCache* _cacheBuffer; + uint64_t _startAddr; + std::unordered_map _targetToIslandIndex; + std::unordered_map _islandIndexToName; + macho_symtab_command

* _symbolTableCmd; + macho_dysymtab_command

* _dynamicSymbolTableCmd; + macho_uuid_command

* _uuidCmd; + uint32_t _maxStubs; + uint32_t _nextIndex; + uint32_t _firstStubOffset; + uint32_t* _stubInstructions; + macho_nlist

* _symbolTable; + char* _nextString; + char* _stringPoolStart; + char* _stringPoolEnd; +}; + +template +BranchPoolDylib

::BranchPoolDylib(DyldSharedCache* cache, uint64_t poolStartAddr, + uint64_t textRegionStartAddr, uint64_t poolLinkEditStartAddr, uint64_t poolLinkEditStartOffset, Diagnostics& diags) + : _cacheBuffer(cache), _startAddr(poolStartAddr), _nextIndex(0), _firstStubOffset(0x280), _diagnostics(diags) +{ + std::string archName = cache->archName(); + bool is64 = (sizeof(typename P::uint_t) == 8); + + const uint64_t textSegSize = branchPoolTextSize(archName); + const uint64_t linkEditSegSize = branchPoolLinkEditSize(archName); + const unsigned stubCount = (unsigned)((textSegSize - _firstStubOffset)/4); + const uint32_t linkeditOffsetSymbolTable = 0; + const uint32_t linkeditOffsetIndirectSymbolTable = stubCount*sizeof(macho_nlist

); + const uint32_t linkeditOffsetSymbolPoolOffset = linkeditOffsetIndirectSymbolTable + stubCount*sizeof(uint32_t); + _maxStubs = stubCount; + + // write mach_header and load commands for pseudo dylib + macho_header

* mh = (macho_header

*)((uint8_t*)cache + poolStartAddr - textRegionStartAddr); + mh->set_magic(is64 ? MH_MAGIC_64 : MH_MAGIC); + mh->set_cputype(dyld3::MachOParser::cpuTypeFromArchName(archName)); + mh->set_cpusubtype(dyld3::MachOParser::cpuSubtypeFromArchName(archName)); + mh->set_filetype(MH_DYLIB); + mh->set_ncmds(6); + mh->set_sizeofcmds(is64 ? 0x210 : 100); // FIXME: 32-bit size + mh->set_flags(0x80000000); + // LC_SEGMENT + macho_load_command

* cmd = (macho_load_command

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

)); + macho_segment_command

* textSegCmd = (macho_segment_command

*)cmd; + textSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT); + textSegCmd->set_cmdsize(sizeof(macho_segment_command

)*2+sizeof(macho_section

)); + textSegCmd->set_segname("__TEXT"); + textSegCmd->set_vmaddr(poolStartAddr); + textSegCmd->set_vmsize(textSegSize); + textSegCmd->set_fileoff(poolStartAddr - textRegionStartAddr); + textSegCmd->set_filesize(branchPoolTextSize(archName)); + textSegCmd->set_maxprot(PROT_READ|PROT_EXEC); + textSegCmd->set_initprot(PROT_READ|PROT_EXEC); + textSegCmd->set_nsects(1); + textSegCmd->set_flags(0); + macho_section

* stubSection = (macho_section

*)((uint8_t*)textSegCmd + sizeof(macho_segment_command

)); + stubSection->set_sectname("__stubs"); + stubSection->set_segname("__TEXT"); + stubSection->set_addr(poolStartAddr + _firstStubOffset); + stubSection->set_size(textSegSize - _firstStubOffset); + stubSection->set_offset((uint32_t)(poolStartAddr + _firstStubOffset - textRegionStartAddr)); + stubSection->set_align(2); + stubSection->set_reloff(0); + stubSection->set_nreloc(0); + stubSection->set_flags(S_SYMBOL_STUBS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS); + stubSection->set_reserved1(0); // start index in indirect table + stubSection->set_reserved2(4); // size of stubs + // LC_SEGMENT + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + macho_segment_command

* linkEditSegCmd = (macho_segment_command

*)cmd; + linkEditSegCmd->set_cmd(is64 ? LC_SEGMENT_64 : LC_SEGMENT); + linkEditSegCmd->set_cmdsize(sizeof(macho_segment_command

)); + linkEditSegCmd->set_segname("__LINKEDIT"); + linkEditSegCmd->set_vmaddr(poolLinkEditStartAddr); + linkEditSegCmd->set_vmsize(linkEditSegSize); + linkEditSegCmd->set_fileoff(poolLinkEditStartOffset); + linkEditSegCmd->set_filesize(linkEditSegSize); + linkEditSegCmd->set_maxprot(PROT_READ); + linkEditSegCmd->set_initprot(PROT_READ); + linkEditSegCmd->set_nsects(0); + linkEditSegCmd->set_flags(0); + // LC_ID_DYLIB + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + macho_dylib_command

* installNameCmd = (macho_dylib_command

*)cmd; + installNameCmd->set_cmd(LC_ID_DYLIB); + installNameCmd->set_cmdsize(sizeof(macho_dylib_command

) + 48); + installNameCmd->set_timestamp(2); + installNameCmd->set_current_version(0x10000); + installNameCmd->set_compatibility_version(0x10000); + installNameCmd->set_name_offset(); + strcpy((char*)cmd + sizeof(macho_dylib_command

), "dyld_shared_cache_branch_islands"); + // LC_SYMTAB + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + _symbolTableCmd = (macho_symtab_command

*)cmd; + _symbolTableCmd->set_cmd(LC_SYMTAB); + _symbolTableCmd->set_cmdsize(sizeof(macho_symtab_command

)); + _symbolTableCmd->set_nsyms(stubCount); + _symbolTableCmd->set_symoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetSymbolTable)); + _symbolTableCmd->set_stroff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetSymbolPoolOffset)); + _symbolTableCmd->set_strsize((uint32_t)(linkEditSegSize - linkeditOffsetSymbolPoolOffset)); + // LC_DYSYMTAB + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + _dynamicSymbolTableCmd = (macho_dysymtab_command

*)cmd; + _dynamicSymbolTableCmd->set_cmd(LC_DYSYMTAB); + _dynamicSymbolTableCmd->set_cmdsize(sizeof(macho_dysymtab_command

)); + _dynamicSymbolTableCmd->set_ilocalsym(0); + _dynamicSymbolTableCmd->set_nlocalsym(0); + _dynamicSymbolTableCmd->set_iextdefsym(0); + _dynamicSymbolTableCmd->set_nextdefsym(0); + _dynamicSymbolTableCmd->set_iundefsym(0); + _dynamicSymbolTableCmd->set_nundefsym(stubCount); + _dynamicSymbolTableCmd->set_tocoff(0); + _dynamicSymbolTableCmd->set_ntoc(0); + _dynamicSymbolTableCmd->set_modtaboff(0); + _dynamicSymbolTableCmd->set_nmodtab(0); + _dynamicSymbolTableCmd->set_extrefsymoff(0); + _dynamicSymbolTableCmd->set_nextrefsyms(0); + _dynamicSymbolTableCmd->set_indirectsymoff((uint32_t)(poolLinkEditStartOffset + linkeditOffsetIndirectSymbolTable)); + _dynamicSymbolTableCmd->set_nindirectsyms(stubCount); + _dynamicSymbolTableCmd->set_extreloff(0); + _dynamicSymbolTableCmd->set_nextrel(0); + _dynamicSymbolTableCmd->set_locreloff(0); + _dynamicSymbolTableCmd->set_nlocrel(0); + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + // LC_UUID + _uuidCmd = (macho_uuid_command

*)cmd; + _uuidCmd->set_cmd(LC_UUID); + _uuidCmd->set_cmdsize(sizeof(macho_uuid_command

)); + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + + // write stubs section content + _stubInstructions = (uint32_t*)((uint8_t*)mh + _firstStubOffset); + for (int i=0; i < stubCount; ++i) { + E::set32(_stubInstructions[i], 0xD4200000); + } + + // write linkedit content + uint8_t* linkeditBufferStart = (uint8_t*)cache + poolLinkEditStartOffset; + // write symbol table + _symbolTable = (macho_nlist

*)(linkeditBufferStart); + for (int i=0; i < stubCount; ++i) { + _symbolTable[i].set_n_strx(1); + _symbolTable[i].set_n_type(N_UNDF | N_EXT); + _symbolTable[i].set_n_sect(0); + _symbolTable[i].set_n_desc(0); + _symbolTable[i].set_n_value(0); + } + // write indirect symbol table + uint32_t* indirectSymboTable = (uint32_t*)(linkeditBufferStart + linkeditOffsetIndirectSymbolTable); + for (int i=0; i < stubCount; ++i) { + P::E::set32(indirectSymboTable[i], i); + } + // write string pool + _stringPoolStart = (char*)(linkeditBufferStart + linkeditOffsetSymbolPoolOffset); + _stringPoolEnd = _stringPoolStart + linkEditSegSize - linkeditOffsetSymbolPoolOffset; + _stringPoolStart[0] = '\0'; + strcpy(&_stringPoolStart[1], ""); + _nextString = &_stringPoolStart[10]; +} + + +template +void BranchPoolDylib

::finalizeLoadCommands() +{ + _symbolTableCmd->set_nsyms(_nextIndex); + _symbolTableCmd->set_strsize((uint32_t)(_nextString - _stringPoolStart)); + _dynamicSymbolTableCmd->set_nundefsym(_nextIndex); + + uint8_t digest[CC_MD5_DIGEST_LENGTH]; + CC_MD5(_stubInstructions, _maxStubs*sizeof(uint64_t), digest); + _uuidCmd->set_uuid(digest); + + if ( verbose ) { + _diagnostics.verbose("branch islands in image at 0x%0llX:\n", _startAddr); + for (uint32_t i=0; i < _nextIndex; ++i) { + _diagnostics.verbose(" 0x%llX %s\n", indexToAddr(i), _islandIndexToName[i]); + } + } +} + +template +uint64_t BranchPoolDylib

::getForwardBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools) +{ + // check if we can re-used existing branch island + const auto& pos = _targetToIslandIndex.find(finalTargetAddr); + if ( pos != _targetToIslandIndex.end() ) + return indexToAddr(pos->second); + + // skip if instruction pool is full + if ( _nextIndex >= _maxStubs ) + return 0; + + // skip if string pool is full + if ( (_nextString + strlen(name)+1) >= _stringPoolEnd ) + return 0; + + uint64_t branchIslandTargetAddr = finalTargetAddr; + // if final target is too far, we need to use branch island in next pool + if ( (finalTargetAddr - _startAddr) > b128MegLimit ) { + BranchPoolDylib

* nextPool = nullptr; + for (size_t i=0; i < branchIslandPools.size()-1; ++i) { + if ( branchIslandPools[i] == this ) { + nextPool = branchIslandPools[i+1]; + break; + } + } + + if (nextPool == nullptr) { + _diagnostics.warning("BranchPoolDylib

::getForwardBranch: nextPool unreachable"); + return 0; + } + + branchIslandTargetAddr = nextPool->getForwardBranch(finalTargetAddr, name, branchIslandPools); + if ( branchIslandTargetAddr == 0 ) + return 0; // next pool is full + } + + // write branch instruction in stubs section + uint32_t index = _nextIndex++; + int64_t branchDelta = branchIslandTargetAddr - indexToAddr(index); + uint32_t branchInstr = 0x14000000 + ((branchDelta/4) & 0x03FFFFFF); + E::set32(_stubInstructions[index], branchInstr); + + // update symbol table + _symbolTable[index].set_n_strx((uint32_t)(_nextString - _stringPoolStart)); + strcpy(_nextString, name); + _nextString += (strlen(name) +1); + + // record island + _targetToIslandIndex[finalTargetAddr] = index; + _islandIndexToName[index] = name; + return indexToAddr(index); +} + +template +uint64_t BranchPoolDylib

::getBackBranch(uint64_t finalTargetAddr, const char* name, std::vector*>& branchIslandPools) +{ + // check if we can re-used existing branch island + const auto& pos = _targetToIslandIndex.find(finalTargetAddr); + if ( pos != _targetToIslandIndex.end() ) + return indexToAddr(pos->second); + + // skip if instruction pool is full + if ( _nextIndex >= _maxStubs ) + return 0; + + // skip if string pool is full + if ( (_nextString + strlen(name)+1) >= _stringPoolEnd ) + return 0; + + uint64_t branchIslandTargetAddr = finalTargetAddr; + // if final target is too far, we need to use branch island in next pool + if ( (indexToAddr(_nextIndex) - finalTargetAddr) > b128MegLimit ) { + BranchPoolDylib

* nextPool = nullptr; + for (long i=branchIslandPools.size()-1; i > 0; --i) { + if ( branchIslandPools[i] == this ) { + nextPool = branchIslandPools[i-1]; + break; + } + } + + if (nextPool == nullptr) { + _diagnostics.warning("BranchPoolDylib

::getBackBranch: nextPool unreachable"); + return 0; + } + + branchIslandTargetAddr = nextPool->getBackBranch(finalTargetAddr, name, branchIslandPools); + if ( branchIslandTargetAddr == 0 ) + return 0; // next pool is full + } + + // write branch instruction in stubs section + uint32_t index = _nextIndex++; + int64_t branchDelta = branchIslandTargetAddr - indexToAddr(index); + uint32_t branchInstr = 0x14000000 + ((branchDelta/4) & 0x03FFFFFF); + E::set32(_stubInstructions[index], branchInstr); + + // update symbol table + _symbolTable[index].set_n_strx((uint32_t)(_nextString - _stringPoolStart)); + strcpy(_nextString, name); + _nextString += (strlen(name) +1); + + // record island + _targetToIslandIndex[finalTargetAddr] = index; + _islandIndexToName[index] = name; + return indexToAddr(index); +} + +template +void BranchPoolDylib

::printStats() +{ + _diagnostics.verbose(" island pool at 0x%0llX has %u stubs and stringPool size=%lu\n", _startAddr, _nextIndex, _nextString - _stringPoolStart); +} + + + +template +class StubOptimizer { +public: + StubOptimizer(void* cacheBuffer, macho_header

* mh, Diagnostics& diags); + void buildStubMap(const std::unordered_set& neverStubEliminate); + void optimizeStubs(std::unordered_map>& targetToBranchIslands); + void bypassStubs(std::unordered_map>& targetToBranchIslands); + void optimizeCallSites(std::vector*>& branchIslandPools); + const char* installName() { return _installName; } + const uint8_t* exportsTrie() { return (uint8_t*)_cacheBuffer + _dyldInfo->export_off(); } + uint32_t exportsTrieSize() { return _dyldInfo->export_size(); } + + uint32_t _stubCount = 0; + uint32_t _stubOptimizedCount = 0; + uint32_t _branchesCount = 0; + uint32_t _branchesModifiedCount = 0; + uint32_t _branchesDirectCount = 0; + uint32_t _branchesIslandCount = 0; + +private: + Diagnostics _diagnostics; + typedef std::function CallSiteHandler; + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + void forEachCallSiteToAStub(CallSiteHandler); + void optimizeArm64CallSites(std::vector*>& branchIslandPools); + void optimizeArmCallSites(); + void optimizeArmStubs(); + uint64_t lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr); + uint32_t lazyPointerAddrFromArmStub(const uint8_t* stubInstructions, uint32_t stubVMAddr); + int32_t getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr); + uint32_t setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, + int32_t displacement, bool targetIsThumb); + + + struct AddressAndName { pint_t targetVMAddr; const char* targetName; }; + typedef std::unordered_map StubVMAddrToTarget; + + static const int64_t b128MegLimit = 0x07FFFFFF; + static const int64_t b16MegLimit = 0x00FFFFFF; + + + macho_header

* _mh; + void* _cacheBuffer; + uint32_t _linkeditSize = 0; + uint32_t _linkeditCacheOffset = 0; + uint64_t _linkeditAddr = 0; + const uint8_t* _linkeditBias = nullptr; + const char* _installName = nullptr; + const macho_symtab_command

* _symTabCmd = nullptr; + const macho_dysymtab_command

* _dynSymTabCmd = nullptr; + const macho_dyld_info_command

* _dyldInfo = nullptr; + macho_linkedit_data_command

* _splitSegInfoCmd = nullptr; + const macho_section

* _textSection = nullptr; + const macho_section

* _stubSection = nullptr; + uint32_t _textSectionIndex = 0; + uint32_t _stubSectionIndex = 0; + pint_t _textSegStartAddr = 0; + uint32_t _textSegCacheOffset = 0; + std::vector*> _segCmds; + std::unordered_map _stubAddrToLPAddr; + std::unordered_map _lpAddrToTargetAddr; + std::unordered_map _targetAddrToName; +}; + +template +StubOptimizer

::StubOptimizer(void* cacheBuffer, macho_header

* mh, Diagnostics& diags) +: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diags) +{ + _linkeditBias = (uint8_t*)cacheBuffer; + const macho_load_command

* const cmds = (macho_load_command

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

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

* segCmd; + uint32_t sectionIndex = 0; + const macho_load_command

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

*)cmd)->name(); + break; + case LC_SYMTAB: + _symTabCmd = (macho_symtab_command

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

*)cmd; + break; + case LC_SEGMENT_SPLIT_INFO: + _splitSegInfoCmd = (macho_linkedit_data_command

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

*)cmd; + break; + case macho_segment_command

::CMD: + segCmd =( macho_segment_command

*)cmd; + _segCmds.push_back(segCmd); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + _linkeditSize = (uint32_t)segCmd->vmsize(); + _linkeditCacheOffset = (uint32_t)segCmd->fileoff(); + _linkeditAddr = segCmd->vmaddr(); + } + else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) { + _textSegStartAddr = (pint_t)segCmd->vmaddr(); + _textSegCacheOffset = (uint32_t)((uint8_t*)mh - (uint8_t*)cacheBuffer); + const macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + const macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + ++sectionIndex; + if ( strcmp(sect->sectname(), "__text") == 0 ) { + _textSection = sect; + _textSectionIndex = sectionIndex; + } + else if ( ((sect->flags() & SECTION_TYPE) == S_SYMBOL_STUBS) && (sect->size() != 0) ) { + _stubSection = sect; + _stubSectionIndex = sectionIndex; + } + } + } + break; + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + + +template +uint32_t StubOptimizer

::lazyPointerAddrFromArmStub(const uint8_t* stubInstructions, uint32_t stubVMAddr) +{ + uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions); + uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions+4)); + uint32_t stubInstr3 = E::get32(*(uint32_t*)(stubInstructions+8)); + int32_t stubData = E::get32(*(uint32_t*)(stubInstructions+12)); + if ( stubInstr1 != 0xe59fc004 ) { + _diagnostics.warning("first instruction of stub (0x%08X) is not 'ldr ip, pc + 12' for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + if ( stubInstr2 != 0xe08fc00c ) { + _diagnostics.warning("second instruction of stub (0x%08X) is not 'add ip, pc, ip' for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + if ( stubInstr3 != 0xe59cf000 ) { + _diagnostics.warning("third instruction of stub (0x%08X) is not 'ldr pc, [ip]' for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + return stubVMAddr + 12 + stubData; +} + + +template +uint64_t StubOptimizer

::lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr) +{ + uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions); + if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) { + _diagnostics.warning("first instruction of stub (0x%08X) is not ADRP for stub at addr 0x%0llX in %s", + stubInstr1, (uint64_t)stubVMAddr, _installName); + return 0; + } + int32_t adrpValue = ((stubInstr1 & 0x00FFFFE0) >> 3) | ((stubInstr1 & 0x60000000) >> 29); + if ( stubInstr1 & 0x00800000 ) + adrpValue |= 0xFFF00000; + uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4)); + if ( (stubInstr2 & 0xFFC003FF) != 0xF9400210 ) { + _diagnostics.warning("second instruction of stub (0x%08X) is not LDR for stub at addr 0x%0llX in %s", + stubInstr2, (uint64_t)stubVMAddr, _installName); + return 0; + } + uint32_t ldrValue = ((stubInstr2 >> 10) & 0x00000FFF); + return (stubVMAddr & (-4096)) + adrpValue*4096 + ldrValue*8; +} + + + +template +void StubOptimizer

::buildStubMap(const std::unordered_set& neverStubEliminate) +{ + // find all stubs and lazy pointers + const macho_nlist

* symbolTable = (const macho_nlist

*)(((uint8_t*)_cacheBuffer) + _symTabCmd->symoff()); + const char* symbolStrings = (char*)_cacheBuffer + _symTabCmd->stroff(); + const uint32_t* const indirectTable = (uint32_t*)(((uint8_t*)_cacheBuffer) + _dynSymTabCmd->indirectsymoff()); + const macho_load_command

* const cmds = (macho_load_command

*)((uint8_t*)_mh + sizeof(macho_header

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

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

::CMD ) { + macho_segment_command

* seg = (macho_segment_command

*)cmd; + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect = sectionsStart; sect < sectionsEnd; ++sect) { + if ( sect->size() == 0 ) + continue; + unsigned sectionType = (sect->flags() & SECTION_TYPE); + const uint32_t indirectTableOffset = sect->reserved1(); + if ( sectionType == S_SYMBOL_STUBS ) { + const uint32_t stubSize = sect->reserved2(); + _stubCount = (uint32_t)(sect->size() / stubSize); + pint_t stubVMAddr = (pint_t)sect->addr(); + for (uint32_t j=0; j < _stubCount; ++j, stubVMAddr += stubSize) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + if ( symbolIndex >= _symTabCmd->nsyms() ) { + _diagnostics.warning("symbol index out of range (%d of %d) for stub at addr 0x%0llX in %s", + symbolIndex, _symTabCmd->nsyms(), (uint64_t)stubVMAddr, _installName); + continue; + } + const macho_nlist

* sym = &symbolTable[symbolIndex]; + uint32_t stringOffset = sym->n_strx(); + if ( stringOffset > _symTabCmd->strsize() ) { + _diagnostics.warning("symbol string offset out of range (%u of %u) for stub at addr 0x%0llX in %s", + stringOffset, sym->n_strx(), (uint64_t)stubVMAddr, _installName); + continue; + } + const char* symName = &symbolStrings[stringOffset]; + if ( neverStubEliminate.count(symName) ) { + //verboseLog("not bypassing stub to %s in %s because target is interposable\n", symName, _installName); + continue; + } + const uint8_t* stubInstrs = (uint8_t*)_cacheBuffer + sect->offset() + stubVMAddr - sect->addr(); + pint_t targetLPAddr = 0; + switch ( _mh->cputype() ) { + case CPU_TYPE_ARM64: + targetLPAddr = (pint_t)lazyPointerAddrFromArm64Stub(stubInstrs, stubVMAddr); + break; + case CPU_TYPE_ARM: + targetLPAddr = (pint_t)lazyPointerAddrFromArmStub(stubInstrs, (uint32_t)stubVMAddr); + break; + } + if ( targetLPAddr != 0 ) + _stubAddrToLPAddr[stubVMAddr] = targetLPAddr; + break; + } + } + } + else if ( sectionType == S_LAZY_SYMBOL_POINTERS ) { + pint_t lpVMAddr; + pint_t* lpContent = (pint_t*)(((uint8_t*)_cacheBuffer) + sect->offset()); + uint32_t elementCount = (uint32_t)(sect->size() / sizeof(pint_t)); + uint64_t textSegStartAddr = _segCmds[0]->vmaddr(); + uint64_t textSegEndAddr = _segCmds[0]->vmaddr() + _segCmds[0]->vmsize(); + pint_t lpValue; + for (uint32_t j=0; j < elementCount; ++j) { + uint32_t symbolIndex = E::get32(indirectTable[indirectTableOffset + j]); + switch ( symbolIndex ) { + case INDIRECT_SYMBOL_ABS: + case INDIRECT_SYMBOL_LOCAL: + break; + default: + lpValue = (pint_t)P::getP(lpContent[j]); + lpVMAddr = (pint_t)sect->addr() + j * sizeof(pint_t); + if ( symbolIndex >= _symTabCmd->nsyms() ) { + _diagnostics.warning("symbol index out of range (%d of %d) for lazy pointer at addr 0x%0llX in %s", + symbolIndex, _symTabCmd->nsyms(), (uint64_t)lpVMAddr, _installName); + continue; + } + const macho_nlist

* sym = &symbolTable[symbolIndex]; + uint32_t stringOffset = sym->n_strx(); + if ( stringOffset > _symTabCmd->strsize() ) { + _diagnostics.warning("symbol string offset out of range (%u of %u) for lazy pointer at addr 0x%0llX in %s", + stringOffset, sym->n_strx(), (uint64_t)lpVMAddr, _installName); + continue; + } + const char* symName = &symbolStrings[stringOffset]; + if ( (lpValue > textSegStartAddr) && (lpValue< textSegEndAddr) ) { + //verboseLog("skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", lpVMAddr, symName, _installName); + } + else if ( (sizeof(pint_t) == 8) && ((lpValue % 4) != 0) ) { + _diagnostics.warning("lazy pointer at 0x%0llX does not point to 4-byte aligned address(0x%0llX) in %s", + (uint64_t)lpVMAddr, (uint64_t)lpValue, _installName); + } + else { + _lpAddrToTargetAddr[lpVMAddr] = lpValue; + _targetAddrToName[lpValue] = symName; + } + break; + } + } + } + } + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +template +void StubOptimizer

::forEachCallSiteToAStub(CallSiteHandler handler) +{ + if (_diagnostics.hasError()) + return; + const uint8_t* infoStart = &_linkeditBias[_splitSegInfoCmd->dataoff()]; + const uint8_t* infoEnd = &infoStart[_splitSegInfoCmd->datasize()]; + if ( *infoStart++ != DYLD_CACHE_ADJ_V2_FORMAT ) { + _diagnostics.error("malformed split seg info in %s", _installName); + return; + } + + uint8_t* textSectionContent = (uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr; + + // Whole :== FromToSection+ + // FromToSection :== ToOffset+ + // ToOffset :== FromOffset+ + // FromOffset :== + const uint8_t* p = infoStart; + uint64_t sectionCount = read_uleb128(p, infoEnd); + for (uint64_t i=0; i < sectionCount; ++i) { + uint64_t fromSectionIndex = read_uleb128(p, infoEnd); + uint64_t toSectionIndex = read_uleb128(p, infoEnd); + uint64_t toOffsetCount = read_uleb128(p, infoEnd); + uint64_t toSectionOffset = 0; + for (uint64_t j=0; j < toOffsetCount; ++j) { + uint64_t toSectionDelta = read_uleb128(p, infoEnd); + uint64_t fromOffsetCount = read_uleb128(p, infoEnd); + toSectionOffset += toSectionDelta; + for (uint64_t k=0; k < fromOffsetCount; ++k) { + uint64_t kind = read_uleb128(p, infoEnd); + if (kind > 12) { + _diagnostics.error("bad kind (%llu) value in %s", kind, _installName); + } + uint64_t fromSectDeltaCount = read_uleb128(p, infoEnd); + uint64_t fromSectionOffset = 0; + for (uint64_t l=0; l < fromSectDeltaCount; ++l) { + uint64_t delta = read_uleb128(p, infoEnd); + fromSectionOffset += delta; + if ( (fromSectionIndex == _textSectionIndex) && (toSectionIndex == _stubSectionIndex) ) { + uint32_t* instrPtr = (uint32_t*)(textSectionContent + fromSectionOffset); + uint64_t instrAddr = _textSection->addr() + fromSectionOffset; + uint64_t stubAddr = _stubSection->addr() + toSectionOffset; + uint32_t instruction = E::get32(*instrPtr); + _branchesCount++; + if ( handler(kind, instrAddr, stubAddr, instruction) ) { + _branchesModifiedCount++; + E::set32(*instrPtr, instruction); + } + } + } + } + } + } +} + + +/// Extract displacement from a thumb b/bl/blx instruction. +template +int32_t StubOptimizer

::getDisplacementFromThumbBranch(uint32_t instruction, uint32_t instrAddr) +{ + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + uint32_t s = (instruction >> 10) & 0x1; + uint32_t j1 = (instruction >> 29) & 0x1; + uint32_t j2 = (instruction >> 27) & 0x1; + uint32_t imm10 = instruction & 0x3FF; + uint32_t imm11 = (instruction >> 16) & 0x7FF; + uint32_t i1 = (j1 == s); + uint32_t i2 = (j2 == s); + uint32_t dis = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1); + int32_t sdis = dis; + int32_t result = s ? (sdis | 0xFE000000) : sdis; + if ( is_blx && (instrAddr & 0x2) ) { + // The thumb blx instruction always has low bit of imm11 as zero. The way + // a 2-byte aligned blx can branch to a 4-byte aligned ARM target is that + // the blx instruction always 4-byte aligns the pc before adding the + // displacement from the blx. We must emulate that when decoding this. + result -= 2; + } + return result; +} + +/// Update a thumb b/bl/blx instruction, switching bl <-> blx as needed. +template +uint32_t StubOptimizer

::setDisplacementInThumbBranch(uint32_t instruction, uint32_t instrAddr, + int32_t displacement, bool targetIsThumb) { + if ( (displacement > 16777214) || (displacement < (-16777216)) ) { + _diagnostics.error("thumb branch out of range at 0x%0X in %s", instrAddr, _installName); + return 0; + } + bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + bool is_b = ((instruction & 0xD000F800) == 0x9000F000); + uint32_t newInstruction = (instruction & 0xD000F800); + if (is_bl || is_blx) { + if (targetIsThumb) { + newInstruction = 0xD000F000; // Use bl + } + else { + newInstruction = 0xC000F000; // Use blx + // See note in getDisplacementFromThumbBranch() about blx. + if (instrAddr & 0x2) + displacement += 2; + } + } + else if (is_b) { + if ( !targetIsThumb ) { + _diagnostics.error("no pc-rel thumb branch instruction that switches to arm mode at 0x%0X in %s", instrAddr, _installName); + return 0; + } + } + else { + _diagnostics.error("not b/bl/blx at 0x%0X in %s", instrAddr, _installName); + return 0; + } + uint32_t s = (uint32_t)(displacement >> 24) & 0x1; + uint32_t i1 = (uint32_t)(displacement >> 23) & 0x1; + uint32_t i2 = (uint32_t)(displacement >> 22) & 0x1; + uint32_t imm10 = (uint32_t)(displacement >> 12) & 0x3FF; + uint32_t imm11 = (uint32_t)(displacement >> 1) & 0x7FF; + uint32_t j1 = (i1 == s); + uint32_t j2 = (i2 == s); + uint32_t nextDisp = (j1 << 13) | (j2 << 11) | imm11; + uint32_t firstDisp = (s << 10) | imm10; + newInstruction |= (nextDisp << 16) | firstDisp; + return newInstruction; +} + + +template +void StubOptimizer

::optimizeArmCallSites() +{ + forEachCallSiteToAStub([&](uint8_t kind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction) -> bool { + if ( kind == DYLD_CACHE_ADJ_V2_THUMB_BR22 ) { + bool is_bl = ((instruction & 0xD000F800) == 0xD000F000); + bool is_blx = ((instruction & 0xD000F800) == 0xC000F000); + bool is_b = ((instruction & 0xD000F800) == 0x9000F000); + if ( !is_bl && !is_blx && !is_b ){ + _diagnostics.warning("non-branch instruction at 0x%0llX in %s", callSiteAddr, _installName); + return false; + } + int32_t brDelta = getDisplacementFromThumbBranch(instruction, (uint32_t)callSiteAddr); + pint_t targetAddr = (pint_t)callSiteAddr + 4 + brDelta; + if ( targetAddr != stubAddr ) { + _diagnostics.warning("stub target mismatch at callsite 0x%0llX in %s", callSiteAddr, _installName); + return false; + } + // ignore branch if not to a known stub + const auto& pos = _stubAddrToLPAddr.find(targetAddr); + if ( pos == _stubAddrToLPAddr.end() ) + return false; + // ignore branch if lazy pointer is not known (could be resolver based) + pint_t lpAddr = pos->second; + const auto& pos2 = _lpAddrToTargetAddr.find(lpAddr); + if ( pos2 == _lpAddrToTargetAddr.end() ) + return false; + uint64_t finalTargetAddr = pos2->second; + int64_t deltaToFinalTarget = finalTargetAddr - (callSiteAddr + 4); + // if final target within range, change to branch there directly + if ( (deltaToFinalTarget > -b16MegLimit) && (deltaToFinalTarget < b16MegLimit) ) { + bool targetIsThumb = finalTargetAddr & 1; + instruction = setDisplacementInThumbBranch(instruction, (uint32_t)callSiteAddr, (int32_t)deltaToFinalTarget, targetIsThumb); + if (_diagnostics.hasError()) + return false; + _branchesDirectCount++; + return true; + } + } + else if ( kind == DYLD_CACHE_ADJ_V2_ARM_BR24 ) { + // too few of these to be worth trying to optimize + } + + return false; + }); + if (_diagnostics.hasError()) + return; +} + + +template +void StubOptimizer

::optimizeArmStubs() +{ + for (const auto& stubEntry : _stubAddrToLPAddr) { + pint_t stubVMAddr = stubEntry.first; + pint_t lpVMAddr = stubEntry.second; + const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr); + if ( pos == _lpAddrToTargetAddr.end() ) + return; + pint_t targetVMAddr = pos->second; + + int32_t delta = (int32_t)(targetVMAddr - (stubVMAddr + 12)); + const uint32_t* stubInstructions = (uint32_t*)((uint8_t*)_cacheBuffer + _stubSection->offset() + stubVMAddr - _stubSection->addr()); + E::set32(*(uint32_t*)&stubInstructions[0], 0xe59fc000); // ldr ip, L0 + E::set32(*(uint32_t*)&stubInstructions[1], 0xe08ff00c); // add pc, pc, ip + E::set32(*(uint32_t*)&stubInstructions[2], delta); // L0: .long xxxx + E::set32(*(uint32_t*)&stubInstructions[3], 0xe7ffdefe); // trap + _stubOptimizedCount++; + } +} + + + + +template +void StubOptimizer

::optimizeArm64CallSites(std::vector*>& branchIslandPools) +{ + forEachCallSiteToAStub([&](uint8_t kind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction) -> bool { + if ( kind != DYLD_CACHE_ADJ_V2_ARM64_BR26 ) + return false; + // skip all but BL or B + if ( (instruction & 0x7C000000) != 0x14000000 ) + return false; + // compute target of branch instruction + int32_t brDelta = (instruction & 0x03FFFFFF) << 2; + if ( brDelta & 0x08000000 ) + brDelta |= 0xF0000000; + uint64_t targetAddr = callSiteAddr + (int64_t)brDelta; + if ( targetAddr != stubAddr ) { + _diagnostics.warning("stub target mismatch"); + return false; + } + // ignore branch if not to a known stub + const auto& pos = _stubAddrToLPAddr.find((pint_t)targetAddr); + if ( pos == _stubAddrToLPAddr.end() ) + return false; + // ignore branch if lazy pointer is not known (could be resolver based) + uint64_t lpAddr = pos->second; + const auto& pos2 = _lpAddrToTargetAddr.find((pint_t)lpAddr); + if ( pos2 == _lpAddrToTargetAddr.end() ) + return false; + uint64_t finalTargetAddr = pos2->second; + int64_t deltaToFinalTarget = finalTargetAddr - callSiteAddr; + // if final target within range, change to branch there directly + if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) { + instruction= (instruction & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF); + _branchesDirectCount++; + return true; + } + // find closest branch island pool between instruction and target and get island + const auto& pos3 = _targetAddrToName.find((pint_t)finalTargetAddr); + if ( pos3 == _targetAddrToName.end() ) + return false; + const char* targetName = pos3->second; + if ( finalTargetAddr > callSiteAddr ) { + // target is after branch so find first pool after branch + for ( BranchPoolDylib

* pool : branchIslandPools ) { + if ( (pool->addr() > callSiteAddr) && (pool->addr() < finalTargetAddr) ) { + uint64_t brIslandAddr = pool->getForwardBranch(finalTargetAddr, targetName, branchIslandPools); + if ( brIslandAddr == 0 ) { + // branch island pool full + _diagnostics.warning("pool full. Can't optimizer branch to %s from 0x%llX in %s\n", targetName, callSiteAddr, _installName); + break; + } + int64_t deltaToTarget = brIslandAddr - callSiteAddr; + instruction = (instruction & 0xFC000000) | ((deltaToTarget >> 2) & 0x03FFFFFF); + _branchesIslandCount++; + return true; + } + } + } + else { + // target is before branch so find closest pool before branch + for (size_t j = branchIslandPools.size(); j > 0; --j) { + BranchPoolDylib

* pool = branchIslandPools[j-1]; + if ( (pool->addr() < callSiteAddr) && (pool->addr() > finalTargetAddr) ) { + uint64_t brIslandAddr = pool->getBackBranch(finalTargetAddr, targetName, branchIslandPools); + if ( brIslandAddr == 0 ) { + // branch island pool full + _diagnostics.warning("pool full. Can't optimizer branch to %s from 0x%llX in %s\n", targetName, callSiteAddr, _installName); + break; + } + int64_t deltaToTarget = brIslandAddr - callSiteAddr; + instruction = (instruction & 0xFC000000) | ((deltaToTarget >> 2) & 0x03FFFFFF); + _branchesIslandCount++; + return true; + } + } + } + return false; + }); + if (_diagnostics.hasError()) + return; +} + + +template +void StubOptimizer

::optimizeCallSites(std::vector*>& branchIslandPools) +{ + if ( _textSection == NULL ) + return; + if ( _stubSection == NULL ) + return; + + + switch ( _mh->cputype() ) { + case CPU_TYPE_ARM64: + optimizeArm64CallSites(branchIslandPools); + if ( verbose ) { + _diagnostics.verbose("%5u branches in __text, %5u changed to direct branches, %5u changed to use islands for %s\n", + _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName); + } + break; + case CPU_TYPE_ARM: + optimizeArmCallSites(); + optimizeArmStubs(); + if ( verbose ) { + _diagnostics.verbose("%3u of %3u stubs optimized. %5u branches in __text, %5u changed to direct branches for %s\n", + _stubOptimizedCount, _stubCount, _branchesCount, _branchesDirectCount, _installName); + } + break; + } +} + +template +void bypassStubs(DyldSharedCache* cache, const std::string& archName, const std::vector& branchPoolStartAddrs, + const char* const neverStubEliminateDylibs[], Diagnostics& diags) +{ + diags.verbose("Stub elimination optimization:\n"); + + // construct a StubOptimizer for each image + __block std::vector*> optimizers; + cache->forEachImage(^(const mach_header* mh, const char* installName) { + optimizers.push_back(new StubOptimizer

((void*)cache, (macho_header

*)mh, diags)); + }); + + // construct a BranchPoolDylib for each pool + std::vector*> pools; + + if ( startsWith(archName, "arm64") ) { + // Find hole at end of linkedit region for branch pool linkedits + __block uint64_t textRegionStartAddr = 0; + __block uint64_t linkEditRegionStartAddr = 0; + __block uint64_t linkEditRegionEndAddr = 0; + __block uint64_t linkEditRegionStartCacheOffset = 0; + cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if ( permissions == (PROT_READ|PROT_EXEC) ) { + textRegionStartAddr = vmAddr; + } + else if ( permissions == PROT_READ ) { + linkEditRegionStartAddr = vmAddr; + linkEditRegionEndAddr = vmAddr + size; + linkEditRegionStartCacheOffset = (char*)content - (char*)cache; + } + }); + __block uint64_t lastLinkEditRegionUsedOffset = 0; + cache->forEachImage(^(const mach_header* mh, const char* installName) { + dyld3::MachOParser parser(mh); + parser.forEachSegment(^(const char* segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool& stop) { + if ( strcmp(segName, "__LINKEDIT") == 0 ) { + if ( fileOffset >= lastLinkEditRegionUsedOffset ) + lastLinkEditRegionUsedOffset = fileOffset + vmSize; + } + }); + }); + uint64_t allPoolsLinkEditStartOffset = lastLinkEditRegionUsedOffset; + uint64_t allPoolsLinkEditStartAddr = linkEditRegionStartAddr + allPoolsLinkEditStartOffset - linkEditRegionStartCacheOffset; + uint64_t allPoolsLinkEditSize = linkEditRegionEndAddr - allPoolsLinkEditStartAddr; + if ( !branchPoolStartAddrs.empty() ) { + uint64_t poolLinkEditStartAddr = allPoolsLinkEditStartAddr; + uint64_t poolLinkEditStartOffset = allPoolsLinkEditStartOffset; + const uint64_t poolSize = (allPoolsLinkEditSize/branchPoolStartAddrs.size()) & (-4096); + for (uint64_t poolAddr : branchPoolStartAddrs) { + pools.push_back(new BranchPoolDylib

(cache, poolAddr, textRegionStartAddr, poolLinkEditStartAddr, poolLinkEditStartOffset, diags)); + poolLinkEditStartAddr += poolSize; + poolLinkEditStartOffset += poolSize; + } + } + } + + // build set of functions to never stub-eliminate because tools may need to override them + std::unordered_set neverStubEliminate; + for (const char** p=sNeverStubEliminateSymbols; *p != nullptr; ++p) { + neverStubEliminate.insert(*p); + } + for (const char* const* d=neverStubEliminateDylibs; *d != nullptr; ++d) { + for (StubOptimizer

* op : optimizers) { + if ( strcmp(op->installName(), *d) == 0 ) { + // add all exports + const uint8_t* exportsStart = op->exportsTrie(); + const uint8_t* exportsEnd = exportsStart + op->exportsTrieSize(); + std::vector exports; + if ( !ExportInfoTrie::parseTrie(exportsStart, exportsEnd, exports) ) { + diags.error("malformed exports trie in %s", *d); + return; + } + for(const ExportInfoTrie::Entry& entry : exports) { + neverStubEliminate.insert(entry.name); + } + } + } + } + + // build maps of stubs-to-lp and lp-to-target + for (StubOptimizer

* op : optimizers) + op->buildStubMap(neverStubEliminate); + + // optimize call sites to by-pass stubs or jump through island + for (StubOptimizer

* op : optimizers) + op->optimizeCallSites(pools); + + // final fix ups in branch pools + for (BranchPoolDylib

* pool : pools) { + pool->finalizeLoadCommands(); + pool->printStats(); + } + + // write total optimization info + uint32_t callSiteCount = 0; + uint32_t callSiteDirectOptCount = 0; + uint32_t callSiteOneHopOptCount = 0; + for (StubOptimizer

* op : optimizers) { + callSiteCount += op->_branchesCount; + callSiteDirectOptCount += op->_branchesDirectCount; + callSiteOneHopOptCount += op->_branchesIslandCount; + } + diags.verbose(" cache contains %u call sites of which %u were direct bound and %u were bound through islands\n", callSiteCount, callSiteDirectOptCount, callSiteOneHopOptCount); + + // clean up + for (StubOptimizer

* op : optimizers) + delete op; + for (BranchPoolDylib

* p : pools) + delete p; + +} + +void bypassStubs(DyldSharedCache* cache, const std::vector& branchPoolStartAddrs, const char* const neverStubEliminateDylibs[], Diagnostics& diags) +{ + std::string archName = cache->archName(); + if ( startsWith(archName, "arm64") ) + bypassStubs>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags); + else if ( archName == "armv7k" ) + bypassStubs>(cache, archName, branchPoolStartAddrs, neverStubEliminateDylibs, diags); + // no stub optimization done for other arches +} + + +/* +template +void StubOptimizer

::optimizeStubs(std::unordered_map>& targetToBranchIslands) +{ + for (const auto& stubEntry : _stubAddrToLPAddr) { + pint_t stubVMAddr = stubEntry.first; + pint_t lpVMAddr = stubEntry.second; + const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr); + if ( pos == _lpAddrToTargetAddr.end() ) + continue; + pint_t targetVMAddr = pos->second; + int64_t delta = targetVMAddr - stubVMAddr; + if ( (delta > -b128MegLimit) && (delta < b128MegLimit) ) { + // target within reach, change stub to direct branch + uint32_t* stubInstructions = (uint32_t*)((uint8_t*)_cacheBuffer + _textSegCacheOffset + stubVMAddr -_textSegStartAddr); + uint32_t stubInstr1 = E::get32(stubInstructions[0]); + if ( (stubInstr1 & 0x9F00001F) != 0x90000010 ) { + warning("first instruction of stub (0x%08X) is no longer ADRP for stub at addr 0x%0X in %s\n", + stubInstr1, stubVMAddr, _installName); + continue; + } + uint32_t directBranchInstr = 0x14000000 + ((delta/4) & 0x03FFFFFF); + E::set32(stubInstructions[0], directBranchInstr); + uint32_t brkInstr = 0xD4200000; + E::set32(stubInstructions[1], brkInstr); + E::set32(stubInstructions[2], brkInstr); + _stubOptimizedCount++; + targetToBranchIslands[targetVMAddr].push_back(stubVMAddr); + } + } + verboseLog("%3u of %3u stubs optimized for %s\n", _stubOptimizedCount, _stubCount, _installName); +} + + +template +void StubOptimizer

::bypassStubs(std::unordered_map>& targetToBranchIslands) +{ + if ( _textSection == NULL ) + return; + + // scan __text section looking for B(L) instructions that branch to a stub + unsigned instructionCount = (unsigned)(_textSection->size() / 4); + uint32_t* instructions = (uint32_t*)((uint8_t*)_cacheBuffer + _textSegCacheOffset + _textSection->addr() -_textSegStartAddr); + for (unsigned i=0; i < instructionCount; ++i) { + uint32_t instr = E::get32(instructions[i]); + // skip all but BL or B + if ( (instr & 0x7C000000) != 0x14000000 ) + continue; + // compute target of branch instruction + int32_t brDelta = (instr & 0x03FFFFFF) << 2; + if ( brDelta & 0x08000000 ) + brDelta |= 0xF0000000; + uint64_t branchAddr = _textSection->addr() + i*4; + uint64_t targetAddr = branchAddr + (int64_t)brDelta; + // ignore branch if not to a known stub + const auto& pos = _stubAddrToLPAddr.find(targetAddr); + if ( pos == _stubAddrToLPAddr.end() ) + continue; + _branchesCount++; + // ignore branch if lazy pointer is not known (could be resolver based) + const auto& pos2 = _lpAddrToTargetAddr.find(pos->second); + if ( pos2 == _lpAddrToTargetAddr.end() ) + continue; + uint64_t finalTargetAddr = pos2->second; + int64_t deltaToFinalTarget = finalTargetAddr - branchAddr; + // if final target within range, change to branch there directly + if ( (deltaToFinalTarget > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) { + uint32_t newInstr = (instr & 0xFC000000) | ((deltaToFinalTarget >> 2) & 0x03FFFFFF); + E::set32(instructions[i], newInstr); + _branchesDirectCount++; + continue; + } + // see if there is an existing branch island in range that can be used + std::vector& existingBranchIslands = targetToBranchIslands[finalTargetAddr]; + for (uint64_t branchIslandAddr : existingBranchIslands) { + int64_t deltaToBranchIsland = branchIslandAddr - branchAddr; + // if final target within range, change to branch deltaToBranchIsland directly + if ( (deltaToBranchIsland > -b128MegLimit) && (deltaToFinalTarget < b128MegLimit) ) { + uint32_t newInstr = (instr & 0xFC000000) | ((deltaToBranchIsland >> 2) & 0x03FFFFFF); + E::set32(instructions[i], newInstr); + _branchesIslandCount++; + break; + } + } + } + if ( verbose ) { + verboseLog("%5u branches in __text, %5u changed to direct branches, %5u changed to indirect for %s\n", + _branchesCount, _branchesDirectCount, _branchesIslandCount, _installName); + } +} +*/ + diff --git a/dyld/dyld3/shared-cache/OptimizerLinkedit.cpp b/dyld/dyld3/shared-cache/OptimizerLinkedit.cpp new file mode 100644 index 0000000..84e2a65 --- /dev/null +++ b/dyld/dyld3/shared-cache/OptimizerLinkedit.cpp @@ -0,0 +1,1180 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "MachOFileAbstraction.hpp" +#include "Trie.hpp" +#include "DyldSharedCache.h" +#include "CacheBuilder.h" + + +#define ALIGN_AS_TYPE(value, type) \ + ((value + alignof(type) - 1) & (-alignof(type))) + +namespace { + +template +class SortedStringPool +{ +public: + // add a string and symbol table entry index to be updated later + void add(uint32_t symbolIndex, const char* symbolName) { + _map[symbolName].push_back(symbolIndex); + } + + // copy sorted strings to buffer and update all symbol's string offsets + uint32_t copyPoolAndUpdateOffsets(char* dstStringPool, macho_nlist

* symbolTable) { + // make sorted list of strings + std::vector allStrings; + allStrings.reserve(_map.size()); + for (auto& entry : _map) { + allStrings.push_back(entry.first); + } + std::sort(allStrings.begin(), allStrings.end()); + // walk sorted list of strings + dstStringPool[0] = '\0'; // tradition for start of pool to be empty string + uint32_t poolOffset = 1; + for (const std::string& symName : allStrings) { + // append string to pool + strcpy(&dstStringPool[poolOffset], symName.c_str()); + // set each string offset of each symbol using it + for (uint32_t symbolIndex : _map[symName]) { + symbolTable[symbolIndex].set_n_strx(poolOffset); + } + poolOffset += symName.size() + 1; + } + // return size of pool + return poolOffset; + } + + size_t size() { + size_t size = 1; + for (auto& entry : _map) { + size += (entry.first.size() + 1); + } + return size; + } + + +private: + std::unordered_map> _map; +}; + + + + +struct LocalSymbolInfo +{ + uint32_t dylibOffset; + uint32_t nlistStartIndex; + uint32_t nlistCount; +}; + + +template +class LinkeditOptimizer { +public: + LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, Diagnostics& diag); + + uint32_t linkeditSize() { return _linkeditSize; } + uint32_t linkeditOffset() { return _linkeditCacheOffset; } + uint64_t linkeditAddr() { return _linkeditAddr; } + const char* installName() { return _installName; } + void copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset); + void copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex); + void copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex); + void copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex, + bool redact, std::vector& localSymbolInfos, + std::vector>& unmappedLocalSymbols, SortedStringPool

& localSymbolsStringPool); + void copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset); + void copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset); + void copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset); + void updateLoadCommands(uint32_t linkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize, + uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount, + uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize); + + macho_header

* machHeader() { return _mh; } + const std::vector getDownwardDependents() { return _downDependentPaths; } + const std::vector getAllDependents() { return _allDependentPaths; } + const std::vector getReExportPaths() { return _reExportPaths; } + const std::vector initializerAddresses() { return _initializerAddresses; } + const std::vector*> dofSections() { return _dofSections; } + uint32_t exportsTrieLinkEditOffset() { return _newExportInfoOffset; } + uint32_t exportsTrieLinkEditSize() { return _exportInfoSize; } + uint32_t weakBindingLinkEditOffset() { return _newWeakBindingInfoOffset; } + uint32_t weakBindingLinkEditSize() { return _newWeakBindingSize; } + uint64_t dyldSectionAddress() { return _dyldSectionAddr; } + const std::vector*>& segCmds() { return _segCmds; } + + +private: + + typedef typename P::uint_t pint_t; + typedef typename P::E E; + + macho_header

* _mh; + void* _cacheBuffer; + Diagnostics& _diagnostics; + uint32_t _linkeditSize = 0; + uint32_t _linkeditCacheOffset = 0; + uint64_t _linkeditAddr = 0; + const uint8_t* _linkeditBias = nullptr; + const char* _installName = nullptr; + macho_symtab_command

* _symTabCmd = nullptr; + macho_dysymtab_command

* _dynSymTabCmd = nullptr; + macho_dyld_info_command

* _dyldInfo = nullptr; + macho_linkedit_data_command

* _functionStartsCmd = nullptr; + macho_linkedit_data_command

* _dataInCodeCmd = nullptr; + std::vector*> _segCmds; + std::unordered_map _oldToNewSymbolIndexes; + std::vector _reExportPaths; + std::vector _downDependentPaths; + std::vector _allDependentPaths; + std::vector _initializerAddresses; + std::vector*> _dofSections; + uint32_t _newWeakBindingInfoOffset = 0; + uint32_t _newLazyBindingInfoOffset = 0; + uint32_t _newBindingInfoOffset = 0; + uint32_t _newExportInfoOffset = 0; + uint32_t _exportInfoSize = 0; + uint32_t _newWeakBindingSize = 0; + uint32_t _newExportedSymbolsStartIndex = 0; + uint32_t _newExportedSymbolCount = 0; + uint32_t _newImportedSymbolsStartIndex = 0; + uint32_t _newImportedSymbolCount = 0; + uint32_t _newLocalSymbolsStartIndex = 0; + uint32_t _newLocalSymbolCount = 0; + uint32_t _newFunctionStartsOffset = 0; + uint32_t _newDataInCodeOffset = 0; + uint32_t _newIndirectSymbolTableOffset = 0; + uint64_t _dyldSectionAddr = 0; +}; + + + +template +class AcceleratorTables { +public: + AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector*>& optimizers); + + uint32_t totalSize() const; + void copyTo(uint8_t* buffer); + +private: + typedef typename P::E E; + + struct NodeChain; + + struct DepNode { + std::vector _dependents; + unsigned _depth; + const char* _installName; + + DepNode() : _depth(0), _installName(nullptr) { } + void computeDepth(); + static void verifyUnreachable(DepNode* target, NodeChain& chain, Diagnostics& diag, std::unordered_set& visitedNodes, const std::vector& from); + }; + + struct NodeChain { + NodeChain* prev; + DepNode* node; + }; + + std::unordered_map*, DepNode> _depDAG; + std::vector _extraInfo; + std::vector _trieBytes; + std::vector _reExportArray; + std::vector _dependencyArray; + std::vector _bottomUpArray; + std::vector _initializers; + std::vector _dofSections; + std::vector _rangeTable; + std::unordered_map*, uint32_t> _machHeaderToImageIndex; + std::unordered_map*> _dylibPathToMachHeader; + std::unordered_map*, LinkeditOptimizer

*> _machHeaderToOptimizer; + dyld_cache_accelerator_info _acceleratorInfoHeader; +}; + + +template +void AcceleratorTables

::AcceleratorTables::DepNode::verifyUnreachable(AcceleratorTables

::DepNode* target, struct AcceleratorTables

::NodeChain& chain, Diagnostics& diag, + std::unordered_set& visitedNodes, const std::vector::DepNode*>& from) { + for (DepNode* node : from) { + bool foundCycle = (node == target); + for (NodeChain* c = &chain; c->prev != nullptr; c = c->prev) { + if ( c->node == target ) { + foundCycle = true; + break; + } + } + if ( foundCycle ) { + NodeChain* chp = &chain; + std::string msg = std::string("found cycle for ") + target->_installName; + while (chp != nullptr) { + msg = msg + "\n " + chp->node->_installName; + chp = chp->prev; + } + diag.warning("%s", msg.c_str()); + return; + } + + if ( visitedNodes.count(node) ) + continue; + visitedNodes.insert(node); + NodeChain nextChain; + nextChain.prev = &chain; + nextChain.node = node; + verifyUnreachable(target, nextChain, diag, visitedNodes, node->_dependents); + } +} + +const uint16_t kBranchIslandDylibIndex = 0x7FFF; + +template +AcceleratorTables

::AcceleratorTables(DyldSharedCache* cache, uint64_t linkeditStartAddr, Diagnostics& diag, const std::vector*>& optimizers) +{ + // build table mapping tables to map between mach_header, index, and optimizer + for ( LinkeditOptimizer

* op : optimizers ) { + _machHeaderToOptimizer[op->machHeader()] = op; + } + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((uint8_t*)cache + cache->header.mappingOffset); + uint64_t cacheStartAddress = mappings[0].address; + const dyld_cache_image_info* images = (dyld_cache_image_info*)((uint8_t*)cache + cache->header.imagesOffset); + for (unsigned i=0; i < cache->header.imagesCount; ++i) { + uint64_t segCacheFileOffset = images[i].address - cacheStartAddress; + macho_header

* mhMapped = (macho_header

*)((uint8_t*)cache+segCacheFileOffset); + const char* path = (char*)cache + images[i].pathFileOffset; + _dylibPathToMachHeader[path] = mhMapped; + // don't add alias entries (path offset in pool near start of cache) to header->index map + if ( images[i].pathFileOffset > segCacheFileOffset ) + _machHeaderToImageIndex[mhMapped] = i; + } + + + // build DAG of image dependencies + for (LinkeditOptimizer

* op : optimizers) { + _depDAG[op->machHeader()]._installName = op->installName(); + } + for (LinkeditOptimizer

* op : optimizers) { + DepNode& node = _depDAG[op->machHeader()]; + for (const char* depPath : op->getDownwardDependents()) { + macho_header

* depMH = _dylibPathToMachHeader[depPath]; + assert(depMH != NULL); + DepNode* depNode = &_depDAG[depMH]; + node._dependents.push_back(depNode); + } + } + + // check for cycles in DAG + for (auto& entry : _depDAG) { + DepNode* node = &entry.second; + NodeChain chain; + chain.prev = nullptr; + chain.node = node; + std::unordered_set visitedNodes; + DepNode::verifyUnreachable(node, chain, diag, visitedNodes, node->_dependents); + } + + // compute depth for each DAG node + for (auto& entry : _depDAG) { + entry.second.computeDepth(); + } + + // build sorted (bottom up) list of images + std::vector*> sortedMachHeaders; + sortedMachHeaders.reserve(optimizers.size()); + for (LinkeditOptimizer

* op : optimizers) { + if ( strcmp(op->installName(), "dyld_shared_cache_branch_islands") != 0 ) + sortedMachHeaders.push_back(op->machHeader()); + else + _machHeaderToImageIndex[op->machHeader()] = kBranchIslandDylibIndex; + } + std::sort(sortedMachHeaders.begin(), sortedMachHeaders.end(), + [&](macho_header

* lmh, macho_header

* rmh) -> bool { + if ( _depDAG[lmh]._depth != _depDAG[rmh]._depth ) + return (_depDAG[lmh]._depth < _depDAG[rmh]._depth); + else + return (lmh < rmh); + }); + + // build zeroed array of extra infos + dyld_cache_image_info_extra emptyExtra; + emptyExtra.exportsTrieAddr = 0; + emptyExtra.weakBindingsAddr = 0; + emptyExtra.exportsTrieSize = 0; + emptyExtra.weakBindingsSize = 0; + emptyExtra.dependentsStartArrayIndex = 0; + emptyExtra.reExportsStartArrayIndex = 0; + _extraInfo.insert(_extraInfo.begin(), sortedMachHeaders.size(), emptyExtra); + + //for ( macho_header

* mh : sortedMachHeaders ) { + // fprintf(stderr, "depth: %3d mh: %p path: %s\n", _depDAG[mh]._depth, mh, _machHeaderToOptimizer[mh]->installName()); + //} + + // build dependency table + _dependencyArray.push_back(0xFFFF); // reserve 0 slot to be "no-dependencies" + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + auto depPaths = op->getAllDependents(); + if ( depPaths.empty() ) { + _extraInfo[index].dependentsStartArrayIndex = 0; + } + else { + _extraInfo[index].dependentsStartArrayIndex = (uint32_t)_dependencyArray.size(); + auto downPaths = op->getDownwardDependents(); + for (const char* depPath : depPaths) { + macho_header

* depMH = _dylibPathToMachHeader[depPath]; + uint16_t depIndex = _machHeaderToImageIndex[depMH]; + if ( std::find(downPaths.begin(), downPaths.end(), depPath) == downPaths.end()) + depIndex |= 0x8000; + _dependencyArray.push_back(depIndex); + } + _dependencyArray.push_back(0xFFFF); // mark end of list + } + } + + // build re-exports table + _reExportArray.push_back(0xFFFF); // reserve 0 slot to be "no-re-exports" + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + auto reExPaths = op->getReExportPaths(); + if ( reExPaths.empty() ) { + _extraInfo[index].reExportsStartArrayIndex = 0; + } + else { + _extraInfo[index].reExportsStartArrayIndex = (uint32_t)_reExportArray.size(); + for (const char* reExPath : reExPaths) { + macho_header

* reExMH = _dylibPathToMachHeader[reExPath]; + uint32_t reExIndex = _machHeaderToImageIndex[reExMH]; + _reExportArray.push_back(reExIndex); + } + _reExportArray.push_back(0xFFFF); // mark end of list + } + } + + // build ordered list of initializers + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + _bottomUpArray.push_back(index); + for (uint64_t initializer : op->initializerAddresses()) { + //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName()); + dyld_cache_accelerator_initializer entry; + entry.functionOffset = (uint32_t)(initializer-cacheStartAddress); + entry.imageIndex = _machHeaderToImageIndex[mh]; + _initializers.push_back(entry); + } + } + + // build ordered list of DOF sections + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + assert(op != NULL); + unsigned imageIndex = _machHeaderToImageIndex[mh]; + for (auto& sect : op->dofSections()) { + //fprintf(stderr, "0x%08llX %s\n", initializer, op->installName()); + dyld_cache_accelerator_dof entry; + entry.sectionAddress = sect->addr(); + entry.sectionSize = (uint32_t)sect->size(); + entry.imageIndex = imageIndex; + _dofSections.push_back(entry); + } + } + + // register exports trie and weak binding info in each dylib with image extra info + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned index = _machHeaderToImageIndex[mh]; + _extraInfo[index].exportsTrieAddr = op->exportsTrieLinkEditOffset() + linkeditStartAddr; + _extraInfo[index].exportsTrieSize = op->exportsTrieLinkEditSize(); + _extraInfo[index].weakBindingsAddr = op->weakBindingLinkEditOffset() + linkeditStartAddr; + _extraInfo[index].weakBindingsSize = op->weakBindingLinkEditSize(); + } + + // record location of __DATA/__dyld section in libdyld.dylib + macho_header

* libdyldMH = _dylibPathToMachHeader["/usr/lib/system/libdyld.dylib"]; + LinkeditOptimizer

* libdyldOp = _machHeaderToOptimizer[libdyldMH]; + uint64_t dyldSectionAddr = libdyldOp->dyldSectionAddress(); + + // build range table for fast address->image lookups + for (macho_header

* mh : sortedMachHeaders) { + LinkeditOptimizer

* op = _machHeaderToOptimizer[mh]; + unsigned imageIndex = _machHeaderToImageIndex[mh]; + for (const macho_segment_command

* segCmd : op->segCmds()) { + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) + continue; + dyld_cache_range_entry entry; + entry.startAddress = segCmd->vmaddr(); + entry.size = (uint32_t)segCmd->vmsize(); + entry.imageIndex = imageIndex; + _rangeTable.push_back(entry); + } + } + std::sort(_rangeTable.begin(), _rangeTable.end(), + [&](const dyld_cache_range_entry& lRange, const dyld_cache_range_entry& rRange) -> bool { + return (lRange.startAddress < rRange.startAddress); + }); + + // build trie that maps install names to image index + std::vector dylibEntrys; + for (auto &x : _dylibPathToMachHeader) { + const std::string& path = x.first; + unsigned index = _machHeaderToImageIndex[x.second]; + dylibEntrys.push_back(DylibIndexTrie::Entry(path, DylibIndex(index))); + } + DylibIndexTrie dylibsTrie(dylibEntrys); + dylibsTrie.emit(_trieBytes); + while ( (_trieBytes.size() % 4) != 0 ) + _trieBytes.push_back(0); + + // fill out header + _acceleratorInfoHeader.version = 1; + _acceleratorInfoHeader.imageExtrasCount = (uint32_t)_extraInfo.size(); + _acceleratorInfoHeader.imagesExtrasOffset = ALIGN_AS_TYPE(sizeof(dyld_cache_accelerator_info), dyld_cache_image_info_extra); + _acceleratorInfoHeader.bottomUpListOffset = _acceleratorInfoHeader.imagesExtrasOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(dyld_cache_image_info_extra); + _acceleratorInfoHeader.dylibTrieOffset = _acceleratorInfoHeader.bottomUpListOffset + _acceleratorInfoHeader.imageExtrasCount*sizeof(uint16_t); + _acceleratorInfoHeader.dylibTrieSize = (uint32_t)_trieBytes.size(); + _acceleratorInfoHeader.initializersOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dylibTrieOffset + _acceleratorInfoHeader.dylibTrieSize, dyld_cache_accelerator_initializer); + _acceleratorInfoHeader.initializersCount = (uint32_t)_initializers.size(); + _acceleratorInfoHeader.dofSectionsOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.initializersOffset + _acceleratorInfoHeader.initializersCount*sizeof(dyld_cache_accelerator_initializer), dyld_cache_accelerator_initializer); + _acceleratorInfoHeader.dofSectionsCount = (uint32_t)_dofSections.size(); + _acceleratorInfoHeader.reExportListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.dofSectionsOffset + _acceleratorInfoHeader.dofSectionsCount*sizeof(dyld_cache_accelerator_dof), dyld_cache_accelerator_dof); + _acceleratorInfoHeader.reExportCount = (uint32_t)_reExportArray.size(); + _acceleratorInfoHeader.depListOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.reExportListOffset + _acceleratorInfoHeader.reExportCount*sizeof(uint16_t), uint16_t); + _acceleratorInfoHeader.depListCount = (uint32_t)_dependencyArray.size(); + _acceleratorInfoHeader.rangeTableOffset = ALIGN_AS_TYPE(_acceleratorInfoHeader.depListOffset + _acceleratorInfoHeader.depListCount*sizeof(uint16_t), dyld_cache_range_entry); + _acceleratorInfoHeader.rangeTableCount = (uint32_t)_rangeTable.size(); + _acceleratorInfoHeader.dyldSectionAddr = dyldSectionAddr; +} + + +template +void AcceleratorTables

::DepNode::computeDepth() +{ + if ( _depth != 0 ) + return; + _depth = 1; + for (DepNode* node : _dependents) { + node->computeDepth(); + if ( node->_depth >= _depth ) + _depth = node->_depth + 1; + } +} + +template +uint32_t AcceleratorTables

::totalSize() const +{ + return (uint32_t)align(_acceleratorInfoHeader.rangeTableOffset + _acceleratorInfoHeader.rangeTableCount*sizeof(dyld_cache_range_entry), 14); +} + +template +void AcceleratorTables

::copyTo(uint8_t* buffer) +{ + memcpy(buffer, &_acceleratorInfoHeader, sizeof(dyld_cache_accelerator_info)); + memcpy(&buffer[_acceleratorInfoHeader.imagesExtrasOffset], &_extraInfo[0], _extraInfo.size()*sizeof(dyld_cache_image_info_extra)); + memcpy(&buffer[_acceleratorInfoHeader.bottomUpListOffset], &_bottomUpArray[0], _bottomUpArray.size()*sizeof(uint16_t)); + memcpy(&buffer[_acceleratorInfoHeader.initializersOffset], &_initializers[0], _initializers.size()*sizeof(dyld_cache_accelerator_initializer)); + memcpy(&buffer[_acceleratorInfoHeader.reExportListOffset], &_reExportArray[0], _reExportArray.size()*sizeof(uint16_t)); + memcpy(&buffer[_acceleratorInfoHeader.dofSectionsOffset], &_dofSections[0], _dofSections.size()*sizeof(dyld_cache_accelerator_dof)); + memcpy(&buffer[_acceleratorInfoHeader.depListOffset], &_dependencyArray[0], _dependencyArray.size()*sizeof(uint16_t)); + memcpy(&buffer[_acceleratorInfoHeader.rangeTableOffset], &_rangeTable[0], _rangeTable.size()*sizeof(dyld_cache_range_entry)); + memcpy(&buffer[_acceleratorInfoHeader.dylibTrieOffset], &_trieBytes[0], _trieBytes.size()); +} + + + +template +LinkeditOptimizer

::LinkeditOptimizer(void* cacheBuffer, macho_header

* mh, Diagnostics& diag) +: _mh(mh), _cacheBuffer(cacheBuffer), _diagnostics(diag) +{ + _linkeditBias = (uint8_t*)cacheBuffer; + const unsigned origLoadCommandsSize = mh->sizeofcmds(); + unsigned bytesRemaining = origLoadCommandsSize; + unsigned removedCount = 0; + const macho_load_command

* const cmds = (macho_load_command

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

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

* cmd = cmds; + const macho_dylib_command

* dylibCmd; + const macho_routines_command

* routinesCmd; + macho_segment_command

* segCmd; + for (uint32_t i = 0; i < cmdCount; ++i) { + bool remove = false; + switch (cmd->cmd()) { + case LC_ID_DYLIB: + _installName = ((macho_dylib_command

*)cmd)->name(); + break; + case LC_SYMTAB: + _symTabCmd = (macho_symtab_command

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

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

*)cmd; + _exportInfoSize = _dyldInfo->export_size(); + break; + case LC_FUNCTION_STARTS: + _functionStartsCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_DATA_IN_CODE: + _dataInCodeCmd = (macho_linkedit_data_command

*)cmd; + break; + case LC_ROUTINES: + case LC_ROUTINES_64: + routinesCmd = (macho_routines_command

*)cmd; + _initializerAddresses.push_back(routinesCmd->init_address()); + break; + case LC_REEXPORT_DYLIB: + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylibCmd = (macho_dylib_command

*)cmd; + _allDependentPaths.push_back(dylibCmd->name()); + if ( cmd->cmd() != LC_LOAD_UPWARD_DYLIB ) + _downDependentPaths.push_back(dylibCmd->name()); + if ( cmd->cmd() == LC_REEXPORT_DYLIB ) + _reExportPaths.push_back(dylibCmd->name()); + break; + case macho_segment_command

::CMD: + segCmd = (macho_segment_command

*)cmd; + _segCmds.push_back(segCmd); + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + _linkeditSize = (uint32_t)segCmd->vmsize(); + _linkeditCacheOffset = (uint32_t)segCmd->fileoff(); + _linkeditAddr = segCmd->vmaddr(); + } + else if ( segCmd->nsects() > 0 ) { + macho_section

* const sectionsStart = (macho_section

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

)); + macho_section

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

* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags() & SECTION_TYPE; + if ( type == S_MOD_INIT_FUNC_POINTERS ) { + const pint_t* inits = (pint_t*)((char*)cacheBuffer + sect->offset()); + const size_t count = sect->size() / sizeof(pint_t); + for (size_t j=0; j < count; ++j) { + uint64_t func = P::getP(inits[j]); + _initializerAddresses.push_back(func); + } + } + else if ( type == S_DTRACE_DOF ) { + _dofSections.push_back(sect); + } + else if ( (strcmp(sect->sectname(), "__dyld") == 0) && (strncmp(sect->segname(), "__DATA", 6) == 0) ) { + _dyldSectionAddr = sect->addr(); + } + } + } + break; + case LC_SEGMENT_SPLIT_INFO: + remove = true; + break; + } + uint32_t cmdSize = cmd->cmdsize(); + macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); + if ( remove ) { + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + ++removedCount; + } + else { + bytesRemaining -= cmdSize; + cmd = nextCmd; + } + } + // zero out stuff removed + ::bzero((void*)cmd, bytesRemaining); + // update header + mh->set_ncmds(cmdCount - removedCount); + mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining); +} + +/* +static void dumpLoadCommands(const uint8_t* mheader) +{ + const mach_header* const mh = (mach_header*)mheader; + const uint32_t cmd_count = mh->ncmds; + bool is64 = (mh->magic == MH_MAGIC_64); + const load_command* cmds = (load_command*)(mheader + (is64 ? sizeof(mach_header_64) : sizeof(mach_header))); + const load_command* cmd = cmds; + const segment_command* segCmd; + const segment_command_64* seg64Cmd; + const symtab_command* symTab; + const linkedit_data_command* leData; + const uint8_t* linkEditBias = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT: + segCmd = (const segment_command*)cmd; + printf("LC_SEGMENT\n"); + printf(" segname = %s\n", segCmd->segname); + printf(" vmaddr = 0x%08X\n", segCmd->vmaddr); + printf(" vmsize = 0x%08X\n", segCmd->vmsize); + printf(" fileoff = 0x%08X\n", segCmd->fileoff); + printf(" filesize = 0x%08X\n", segCmd->filesize); + if ( strcmp(segCmd->segname, "__TEXT") == 0 ) { + linkEditBias = mheader - segCmd->fileoff; + } + break; + case LC_SEGMENT_64: + seg64Cmd = (const segment_command_64*)cmd; + printf("LC_SEGMENT_64\n"); + printf(" segname = %s\n", seg64Cmd->segname); + printf(" vmaddr = 0x%09llX\n", seg64Cmd->vmaddr); + printf(" vmsize = 0x%09llX\n", seg64Cmd->vmsize); + printf(" fileoff = 0x%09llX\n", seg64Cmd->fileoff); + printf(" filesize = 0x%09llX\n", seg64Cmd->filesize); + if ( strcmp(seg64Cmd->segname, "__TEXT") == 0 ) { + linkEditBias = mheader - seg64Cmd->fileoff; + } + break; + case LC_SYMTAB: + symTab = (const symtab_command*)cmd; + printf("LC_SYMTAB\n"); + printf(" symoff = 0x%08X\n", symTab->symoff); + printf(" nsyms = 0x%08X\n", symTab->nsyms); + printf(" stroff = 0x%08X\n", symTab->stroff); + printf(" strsize = 0x%08X\n", symTab->strsize); + { + const char* strPool = (char*)&linkEditBias[symTab->stroff]; + const nlist_64* sym0 = (nlist_64*)(&linkEditBias[symTab->symoff]); + printf(" sym[0].n_strx = 0x%08X (%s)\n", sym0->n_un.n_strx, &strPool[sym0->n_un.n_strx]); + printf(" sym[0].n_type = 0x%02X\n", sym0->n_type); + printf(" sym[0].n_sect = 0x%02X\n", sym0->n_sect); + printf(" sym[0].n_desc = 0x%04X\n", sym0->n_desc); + printf(" sym[0].n_value = 0x%llX\n", sym0->n_value); + const nlist_64* sym1 = (nlist_64*)(&linkEditBias[symTab->symoff+16]); + printf(" sym[1].n_strx = 0x%08X (%s)\n", sym1->n_un.n_strx, &strPool[sym1->n_un.n_strx]); + printf(" sym[1].n_type = 0x%02X\n", sym1->n_type); + printf(" sym[1].n_sect = 0x%02X\n", sym1->n_sect); + printf(" sym[1].n_desc = 0x%04X\n", sym1->n_desc); + printf(" sym[1].n_value = 0x%llX\n", sym1->n_value); + } + break; + case LC_FUNCTION_STARTS: + leData = (const linkedit_data_command*)cmd; + printf("LC_FUNCTION_STARTS\n"); + printf(" dataoff = 0x%08X\n", leData->dataoff); + printf(" datasize = 0x%08X\n", leData->datasize); + default: + //printf("0x%08X\n", cmd->cmd); + break; + } + cmd = (const load_command*)(((uint8_t*)cmd)+cmd->cmdsize); + } +} +*/ + +template +void LinkeditOptimizer

::updateLoadCommands(uint32_t mergedLinkeditStartOffset, uint64_t mergedLinkeditAddr, uint64_t newLinkeditSize, + uint32_t sharedSymbolTableStartOffset, uint32_t sharedSymbolTableCount, + uint32_t sharedSymbolStringsOffset, uint32_t sharedSymbolStringsSize) +{ + // update __LINKEDIT segment in all dylibs to overlap the same shared region + for (macho_segment_command

* segCmd : _segCmds) { + if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) { + segCmd->set_vmaddr(mergedLinkeditAddr); + segCmd->set_vmsize(newLinkeditSize); + segCmd->set_fileoff(mergedLinkeditStartOffset); + segCmd->set_filesize(newLinkeditSize); + } + else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) { + // HACK until lldb fixed in: DynamicLoaderMacOSXDYLD fixes for Monarch dyld shared cache + //segCmd->set_fileoff(0); + + } + } + + // update symbol table to point to shared symbol table + _symTabCmd->set_symoff(mergedLinkeditStartOffset + sharedSymbolTableStartOffset + _newLocalSymbolsStartIndex*sizeof(macho_nlist

)); + _symTabCmd->set_nsyms(_newLocalSymbolCount+_newExportedSymbolCount+_newImportedSymbolCount); + _symTabCmd->set_stroff(mergedLinkeditStartOffset + sharedSymbolStringsOffset); + _symTabCmd->set_strsize(sharedSymbolStringsSize); + + // update dynamic symbol table to have proper offsets into shared symbol table + _dynSymTabCmd->set_ilocalsym(0); + _dynSymTabCmd->set_nlocalsym(_newLocalSymbolCount); + _dynSymTabCmd->set_iextdefsym(_newExportedSymbolsStartIndex-_newLocalSymbolsStartIndex); + _dynSymTabCmd->set_nextdefsym(_newExportedSymbolCount); + _dynSymTabCmd->set_iundefsym(_newImportedSymbolsStartIndex-_newLocalSymbolsStartIndex); + _dynSymTabCmd->set_nundefsym(_newImportedSymbolCount); + _dynSymTabCmd->set_tocoff(0); + _dynSymTabCmd->set_ntoc(0); + _dynSymTabCmd->set_modtaboff(0); + _dynSymTabCmd->set_nmodtab(0); + _dynSymTabCmd->set_indirectsymoff(mergedLinkeditStartOffset + _newIndirectSymbolTableOffset); + _dynSymTabCmd->set_extreloff(0); + _dynSymTabCmd->set_locreloff(0); + _dynSymTabCmd->set_nlocrel(0); + + // update dyld info + if ( _dyldInfo != nullptr ) { + _dyldInfo->set_rebase_off(0); + _dyldInfo->set_rebase_size(0); + _dyldInfo->set_bind_off(_dyldInfo->bind_size() ? mergedLinkeditStartOffset + _newBindingInfoOffset : 0); + _dyldInfo->set_weak_bind_off(_dyldInfo->weak_bind_size() ? mergedLinkeditStartOffset + _newWeakBindingInfoOffset : 0 ); + _dyldInfo->set_lazy_bind_off(_dyldInfo->lazy_bind_size() ? mergedLinkeditStartOffset + _newLazyBindingInfoOffset : 0 ); + _dyldInfo->set_export_off(mergedLinkeditStartOffset + _newExportInfoOffset); + } + + // update function-starts + if ( _functionStartsCmd != nullptr ) + _functionStartsCmd->set_dataoff(mergedLinkeditStartOffset+_newFunctionStartsOffset); + + // update data-in-code + if ( _dataInCodeCmd != nullptr ) + _dataInCodeCmd->set_dataoff(mergedLinkeditStartOffset+_newDataInCodeOffset); +} + +template +void LinkeditOptimizer

::copyWeakBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->weak_bind_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->weak_bind_off()], size); + _newWeakBindingInfoOffset = offset; + _newWeakBindingSize = size; + offset += size; + } +} + + +template +void LinkeditOptimizer

::copyLazyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->lazy_bind_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->lazy_bind_off()], size); + _newLazyBindingInfoOffset = offset; + offset += size; + } +} + +template +void LinkeditOptimizer

::copyBindingInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->bind_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->bind_off()], size); + _newBindingInfoOffset = offset; + offset += size; + } +} + +template +void LinkeditOptimizer

::copyExportInfo(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dyldInfo == nullptr ) + return; + unsigned size = _dyldInfo->export_size(); + if ( size != 0 ) { + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dyldInfo->export_off()], size); + _newExportInfoOffset = offset; + offset += size; + } +} + + +template +void LinkeditOptimizer

::copyFunctionStarts(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _functionStartsCmd == nullptr ) + return; + unsigned size = _functionStartsCmd->datasize(); + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_functionStartsCmd->dataoff()], size); + _newFunctionStartsOffset = offset; + offset += size; +} + +template +void LinkeditOptimizer

::copyDataInCode(uint8_t* newLinkEditContent, uint32_t& offset) +{ + if ( _dataInCodeCmd == nullptr ) + return; + unsigned size = _dataInCodeCmd->datasize(); + ::memcpy(&newLinkEditContent[offset], &_linkeditBias[_dataInCodeCmd->dataoff()], size); + _newDataInCodeOffset = offset; + offset += size; +} + + +template +void LinkeditOptimizer

::copyLocalSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex, + bool redact, std::vector& localSymbolInfos, + std::vector>& unmappedLocalSymbols, SortedStringPool

& localSymbolsStringPool) +{ + LocalSymbolInfo localInfo; + localInfo.dylibOffset = (uint32_t)(((uint8_t*)_mh) - (uint8_t*)_cacheBuffer); + localInfo.nlistStartIndex = (uint32_t)unmappedLocalSymbols.size(); + localInfo.nlistCount = 0; + _newLocalSymbolsStartIndex = symbolIndex; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const macho_nlist

* const firstExport = &symbolTable[_dynSymTabCmd->ilocalsym()]; + const macho_nlist

* const lastExport = &symbolTable[_dynSymTabCmd->ilocalsym()+_dynSymTabCmd->nlocalsym()]; + for (const macho_nlist

* entry = firstExport; entry < lastExport; ++entry) { + if ( (entry->n_type() & N_TYPE) != N_SECT) + continue; + if ( (entry->n_type() & N_STAB) != 0) + continue; + const char* name = &strings[entry->n_strx()]; + macho_nlist

* newSymbolEntry = (macho_nlist

*)&newLinkEditContent[offset]; + *newSymbolEntry = *entry; + if ( redact ) { + // if removing local symbols, change __text symbols to "" so backtraces don't have bogus names + if ( entry->n_sect() == 1 ) { + stringPool.add(symbolIndex, ""); + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + // copy local symbol to unmmapped locals area + localSymbolsStringPool.add((uint32_t)unmappedLocalSymbols.size(), name); + unmappedLocalSymbols.push_back(*entry); + unmappedLocalSymbols.back().set_n_strx(0); + } + else { + stringPool.add(symbolIndex, name); + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + } + _newLocalSymbolCount = symbolIndex - _newLocalSymbolsStartIndex; + localInfo.nlistCount = (uint32_t)unmappedLocalSymbols.size() - localInfo.nlistStartIndex; + localSymbolInfos.push_back(localInfo); +} + + +template +void LinkeditOptimizer

::copyExportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex) +{ + _newExportedSymbolsStartIndex = symbolIndex; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const macho_nlist

* const firstExport = &symbolTable[_dynSymTabCmd->iextdefsym()]; + const macho_nlist

* const lastExport = &symbolTable[_dynSymTabCmd->iextdefsym()+_dynSymTabCmd->nextdefsym()]; + uint32_t oldSymbolIndex = _dynSymTabCmd->iextdefsym(); + for (const macho_nlist

* entry = firstExport; entry < lastExport; ++entry, ++oldSymbolIndex) { + if ( (entry->n_type() & N_TYPE) != N_SECT) + continue; + const char* name = &strings[entry->n_strx()]; + if ( strncmp(name, ".objc_", 6) == 0 ) + continue; + if ( strncmp(name, "$ld$", 4) == 0 ) + continue; + macho_nlist

* newSymbolEntry = (macho_nlist

*)&newLinkEditContent[offset]; + *newSymbolEntry = *entry; + newSymbolEntry->set_n_strx(0); + stringPool.add(symbolIndex, name); + _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex; + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + _newExportedSymbolCount = symbolIndex - _newExportedSymbolsStartIndex; +} + +template +void LinkeditOptimizer

::copyImportedSymbols(uint8_t* newLinkEditContent, SortedStringPool

& stringPool, uint32_t& offset, uint32_t& symbolIndex) +{ + _newImportedSymbolsStartIndex = symbolIndex; + const char* strings = (char*)&_linkeditBias[_symTabCmd->stroff()]; + const macho_nlist

* const symbolTable = (macho_nlist

*)(&_linkeditBias[_symTabCmd->symoff()]); + const macho_nlist

* const firstImport = &symbolTable[_dynSymTabCmd->iundefsym()]; + const macho_nlist

* const lastImport = &symbolTable[_dynSymTabCmd->iundefsym()+_dynSymTabCmd->nundefsym()]; + uint32_t oldSymbolIndex = _dynSymTabCmd->iundefsym(); + for (const macho_nlist

* entry = firstImport; entry < lastImport; ++entry, ++oldSymbolIndex) { + if ( (entry->n_type() & N_TYPE) != N_UNDF) + continue; + const char* name = &strings[entry->n_strx()]; + macho_nlist

* newSymbolEntry = (macho_nlist

*)&newLinkEditContent[offset]; + *newSymbolEntry = *entry; + newSymbolEntry->set_n_strx(0); + stringPool.add(symbolIndex, name); + _oldToNewSymbolIndexes[oldSymbolIndex] = symbolIndex - _newLocalSymbolsStartIndex; + ++symbolIndex; + offset += sizeof(macho_nlist

); + } + _newImportedSymbolCount = symbolIndex - _newImportedSymbolsStartIndex; +} + +template +void LinkeditOptimizer

::copyIndirectSymbolTable(uint8_t* newLinkEditContent, uint32_t& offset) +{ + _newIndirectSymbolTableOffset = offset; + const uint32_t* const indirectTable = (uint32_t*)&_linkeditBias[_dynSymTabCmd->indirectsymoff()]; + uint32_t* newIndirectTable = (uint32_t*)&newLinkEditContent[offset]; + for (int i=0; i < _dynSymTabCmd->nindirectsyms(); ++i) { + uint32_t symbolIndex = E::get32(indirectTable[i]); + if ( (symbolIndex == INDIRECT_SYMBOL_ABS) || (symbolIndex == INDIRECT_SYMBOL_LOCAL) ) + E::set32(newIndirectTable[i], symbolIndex); + else + E::set32(newIndirectTable[i], _oldToNewSymbolIndexes[symbolIndex]); + offset += sizeof(uint32_t); + } +} + +template +uint64_t mergeLinkedits(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, std::vector*>& optimizers, Diagnostics& diagnostics, dyld_cache_local_symbols_info** localsInfo) +{ + // allocate space for new linkedit data + uint32_t linkeditStartOffset = 0xFFFFFFFF; + uint32_t linkeditEndOffset = 0; + uint64_t linkeditStartAddr = 0; + for (LinkeditOptimizer

* op : optimizers) { + uint32_t leOffset = op->linkeditOffset(); + if ( leOffset < linkeditStartOffset ) { + linkeditStartOffset = leOffset; + linkeditStartAddr = op->linkeditAddr(); + } + uint32_t leEndOffset = op->linkeditOffset() + op->linkeditSize(); + if ( leEndOffset > linkeditEndOffset ) + linkeditEndOffset = leEndOffset; + } + uint64_t totalUnoptLinkeditsSize = linkeditEndOffset - linkeditStartOffset; + uint8_t* newLinkEdit = (uint8_t*)calloc(totalUnoptLinkeditsSize, 1); + SortedStringPool

stringPool; + uint32_t offset = 0; + + diagnostics.verbose("Merged LINKEDIT:\n"); + + // copy weak binding info + uint32_t startWeakBindInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyWeakBindingInfo(newLinkEdit, offset); + } + diagnostics.verbose(" weak bindings size: %5uKB\n", (uint32_t)(offset-startWeakBindInfosOffset)/1024); + + // copy export info + uint32_t startExportInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyExportInfo(newLinkEdit, offset); + } + diagnostics.verbose(" exports info size: %5uKB\n", (uint32_t)(offset-startExportInfosOffset)/1024); + + // in theory, an optimized cache can drop the binding info + if ( true ) { + // copy binding info + uint32_t startBindingsInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyBindingInfo(newLinkEdit, offset); + } + diagnostics.verbose(" bindings size: %5uKB\n", (uint32_t)(offset-startBindingsInfosOffset)/1024); + + // copy lazy binding info + uint32_t startLazyBindingsInfosOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyLazyBindingInfo(newLinkEdit, offset); + } + diagnostics.verbose(" lazy bindings size: %5uKB\n", (offset-startLazyBindingsInfosOffset)/1024); + } + + // copy symbol table entries + std::vector> unmappedLocalSymbols; + if ( dontMapLocalSymbols ) + unmappedLocalSymbols.reserve(0x01000000); + std::vector localSymbolInfos; + localSymbolInfos.reserve(optimizers.size()); + SortedStringPool

localSymbolsStringPool; + uint32_t symbolIndex = 0; + const uint32_t sharedSymbolTableStartOffset = offset; + uint32_t sharedSymbolTableExportsCount = 0; + uint32_t sharedSymbolTableImportsCount = 0; + for (LinkeditOptimizer

* op : optimizers) { + op->copyLocalSymbols(newLinkEdit, stringPool, offset, symbolIndex, dontMapLocalSymbols, + localSymbolInfos, unmappedLocalSymbols, localSymbolsStringPool); + uint32_t x = symbolIndex; + op->copyExportedSymbols(newLinkEdit, stringPool, offset, symbolIndex); + sharedSymbolTableExportsCount += (symbolIndex-x); + uint32_t y = symbolIndex; + op->copyImportedSymbols(newLinkEdit, stringPool, offset, symbolIndex); + sharedSymbolTableImportsCount += (symbolIndex-y); + } + uint32_t sharedSymbolTableCount = symbolIndex; + const uint32_t sharedSymbolTableEndOffset = offset; + + // copy function starts + uint32_t startFunctionStartsOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyFunctionStarts(newLinkEdit, offset); + } + diagnostics.verbose(" function starts size: %5uKB\n", (offset-startFunctionStartsOffset)/1024); + + // copy data-in-code info + uint32_t startDataInCodeOffset = offset; + for (LinkeditOptimizer

* op : optimizers) { + op->copyDataInCode(newLinkEdit, offset); + } + diagnostics.verbose(" data in code size: %5uKB\n", (offset-startDataInCodeOffset)/1024); + + // copy indirect symbol tables + for (LinkeditOptimizer

* op : optimizers) { + op->copyIndirectSymbolTable(newLinkEdit, offset); + } + // if indirect table has odd number of entries, end will not be 8-byte aligned + if ( (offset % sizeof(typename P::uint_t)) != 0 ) + offset += 4; + + // copy string pool + uint32_t sharedSymbolStringsOffset = offset; + uint32_t sharedSymbolStringsSize = stringPool.copyPoolAndUpdateOffsets((char*)&newLinkEdit[sharedSymbolStringsOffset], (macho_nlist

*)&newLinkEdit[sharedSymbolTableStartOffset]); + offset += sharedSymbolStringsSize; + uint32_t newLinkeditUnalignedSize = offset; + uint64_t newLinkeditEnd = align(linkeditStartOffset+newLinkeditUnalignedSize, 14); + diagnostics.verbose(" symbol table size: %5uKB (%d exports, %d imports)\n", (sharedSymbolTableEndOffset-sharedSymbolTableStartOffset)/1024, sharedSymbolTableExportsCount, sharedSymbolTableImportsCount); + diagnostics.verbose(" symbol string pool size: %5uKB\n", sharedSymbolStringsSize/1024); + + // overwrite mapped LINKEDIT area in cache with new merged LINKEDIT content + diagnostics.verbose("LINKEDITS optimized from %uMB to %uMB\n", (uint32_t)totalUnoptLinkeditsSize/(1024*1024), (uint32_t)newLinkeditUnalignedSize/(1024*1024)); + ::memcpy((char*)cache + linkeditStartOffset, newLinkEdit, newLinkeditUnalignedSize); + ::bzero((char*)cache + linkeditStartOffset+newLinkeditUnalignedSize, totalUnoptLinkeditsSize-newLinkeditUnalignedSize); + ::free(newLinkEdit); + + // If making cache for customers, add extra accelerator tables for dyld + if ( addAcceleratorTables ) { + AcceleratorTables

tables(cache, linkeditStartAddr, diagnostics, optimizers); + uint32_t tablesSize = tables.totalSize(); + if ( tablesSize < (totalUnoptLinkeditsSize-newLinkeditUnalignedSize) ) { + tables.copyTo((uint8_t*)cache+newLinkeditEnd); + newLinkeditEnd += tablesSize; + uint64_t accelInfoAddr = align(linkeditStartAddr + newLinkeditUnalignedSize, 14); + cache->header.accelerateInfoAddr = accelInfoAddr; + cache->header.accelerateInfoSize = tablesSize; + diagnostics.verbose("Accelerator tables %uMB\n", (uint32_t)tablesSize/(1024*1024)); + } + else { + diagnostics.warning("not enough room to add dyld accelerator tables"); + } + } + + // update mapping to reduce linkedit size + dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset); + mappings[2].size = newLinkeditEnd - mappings[2].fileOffset; + + // overwrite end of un-opt linkedits to create a new unmapped region for local symbols + uint64_t newFileSize = newLinkeditEnd; + if ( dontMapLocalSymbols ) { + typedef typename P::E E; + const uint32_t entriesOffset = sizeof(dyld_cache_local_symbols_info); + const uint32_t entriesCount = (uint32_t)localSymbolInfos.size(); + const uint32_t nlistOffset = (uint32_t)align(entriesOffset + entriesCount * sizeof(dyld_cache_local_symbols_info), 4); // 16-byte align start + const uint32_t nlistCount = (uint32_t)unmappedLocalSymbols.size(); + const uint32_t stringsSize = (uint32_t)localSymbolsStringPool.size(); + const uint32_t stringsOffset = nlistOffset + nlistCount * sizeof(macho_nlist

); + // allocate buffer for local symbols + const size_t localsBufferSize = align(stringsOffset + stringsSize, 14); + dyld_cache_local_symbols_info* infoHeader = (dyld_cache_local_symbols_info*)malloc(localsBufferSize); + // fill in header info + infoHeader->nlistOffset = nlistOffset; + infoHeader->nlistCount = nlistCount; + infoHeader->stringsOffset = stringsOffset; + infoHeader->stringsSize = stringsSize; + infoHeader->entriesOffset = entriesOffset; + infoHeader->entriesCount = entriesCount; + // copy info for each dylib + dyld_cache_local_symbols_entry* entries = (dyld_cache_local_symbols_entry*)(((uint8_t*)infoHeader)+entriesOffset); + for (int i=0; i < entriesCount; ++i) { + entries[i].dylibOffset = localSymbolInfos[i].dylibOffset; + entries[i].nlistStartIndex = localSymbolInfos[i].nlistStartIndex; + entries[i].nlistCount = localSymbolInfos[i].nlistCount; + } + // copy nlists + macho_nlist

* newLocalsSymbolTable = (macho_nlist

*)(((uint8_t*)infoHeader)+nlistOffset); + ::memcpy(newLocalsSymbolTable, &unmappedLocalSymbols[0], nlistCount*sizeof(macho_nlist

)); + // copy string pool + localSymbolsStringPool.copyPoolAndUpdateOffsets(((char*)infoHeader)+stringsOffset, newLocalsSymbolTable); + // return buffer of local symbols, caller to free() it + *localsInfo = infoHeader; + } + + // update all load commands to new merged layout + for (LinkeditOptimizer

* op : optimizers) { + op->updateLoadCommands(linkeditStartOffset, linkeditStartAddr, newLinkeditEnd-linkeditStartOffset, + sharedSymbolTableStartOffset, sharedSymbolTableCount, + sharedSymbolStringsOffset, sharedSymbolStringsSize); + } + + return newFileSize; +} + +} // anonymous namespace + +template +uint64_t optimizeLinkedit(DyldSharedCache* cache, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo) +{ + // construct a LinkeditOptimizer for each image + __block std::vector*> optimizers; + cache->forEachImage(^(const mach_header* mh, const char*) { + optimizers.push_back(new LinkeditOptimizer

(cache, (macho_header

*)mh, diag)); + }); +#if 0 + // add optimizer for each branch pool + for (uint64_t poolOffset : branchPoolOffsets) { + macho_header

* mh = (macho_header

*)((char*)cache + poolOffset); + optimizers.push_back(new LinkeditOptimizer

(cache, mh, diag)); + } +#endif + // merge linkedit info + uint64_t newFileSize = mergeLinkedits(cache, dontMapLocalSymbols, addAcceleratorTables, optimizers, diag, localsInfo); + + // delete optimizers + for (LinkeditOptimizer

* op : optimizers) + delete op; + + return newFileSize; +} + +uint64_t optimizeLinkedit(DyldSharedCache* cache, bool is64, bool dontMapLocalSymbols, bool addAcceleratorTables, const std::vector& branchPoolOffsets, Diagnostics& diag, dyld_cache_local_symbols_info** localsInfo) +{ + if ( is64) { + return optimizeLinkedit>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo); + } + else { + return optimizeLinkedit>(cache, dontMapLocalSymbols, addAcceleratorTables, branchPoolOffsets, diag, localsInfo); + } +} + + + diff --git a/dyld/dyld3/shared-cache/OptimizerObjC.cpp b/dyld/dyld3/shared-cache/OptimizerObjC.cpp new file mode 100644 index 0000000..71fe9dc --- /dev/null +++ b/dyld/dyld3/shared-cache/OptimizerObjC.cpp @@ -0,0 +1,820 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include + +#include "DyldSharedCache.h" +#include "Diagnostics.h" +#include "CacheBuilder.h" +#include "FileAbstraction.hpp" +#include "MachOFileAbstraction.hpp" + + +// Scan a C++ or Swift length-mangled field. +static bool scanMangledField(const char *&string, const char *end, + const char *&field, int& length) +{ + // Leading zero not allowed. + if (*string == '0') return false; + + length = 0; + field = string; + while (field < end) { + char c = *field; + if (!isdigit(c)) break; + field++; + if (__builtin_smul_overflow(length, 10, &length)) return false; + if (__builtin_sadd_overflow(length, c - '0', &length)) return false; + } + + string = field + length; + return length > 0 && string <= end; +} + + +// copySwiftDemangledName +// Returns the pretty form of the given Swift-mangled class or protocol name. +// Returns nullptr if the string doesn't look like a mangled Swift name. +// The result must be freed with free(). +static char *copySwiftDemangledName(const char *string, bool isProtocol = false) +{ + if (!string) return nullptr; + + // Swift mangling prefix. + if (strncmp(string, isProtocol ? "_TtP" : "_TtC", 4) != 0) return nullptr; + string += 4; + + const char *end = string + strlen(string); + + // Module name. + const char *prefix; + int prefixLength; + if (string[0] == 's') { + // "s" is the Swift module. + prefix = "Swift"; + prefixLength = 5; + string += 1; + } else { + if (! scanMangledField(string, end, prefix, prefixLength)) return nullptr; + } + + // Class or protocol name. + const char *suffix; + int suffixLength; + if (! scanMangledField(string, end, suffix, suffixLength)) return nullptr; + + if (isProtocol) { + // Remainder must be "_". + if (strcmp(string, "_") != 0) return nullptr; + } else { + // Remainder must be empty. + if (string != end) return nullptr; + } + + char *result; + asprintf(&result, "%.*s.%.*s", prefixLength,prefix, suffixLength,suffix); + return result; +} + + +class ContentAccessor { +public: + ContentAccessor(const DyldSharedCache* cache, Diagnostics& diag) + : _diagnostics(diag) + { + __block int index = 0; + cache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + _regions[index++] = { (uint8_t*)content, (uint8_t*)content+size, vmAddr, vmAddr+size }; + }); + } + + void* contentForVMAddr(uint64_t vmaddr) { + for (const Info& info : _regions) { + if ( (info.startAddr <= vmaddr) && (vmaddr < info.endAddr) ) + return (void*)(info.contentStart + vmaddr - info.startAddr); + } + if ( vmaddr != 0 ) + _diagnostics.error("invalid vmaddr 0x%0llX in ObjC data", vmaddr); + return nullptr; + } + + uint64_t vmAddrForContent(const void* content) { + for (const Info& info : _regions) { + if ( (info.contentStart <= content) && (content < info.contentEnd) ) + return info.startAddr + ((uint8_t*)content - (uint8_t*)info.contentStart); + } + _diagnostics.error("invalid content pointer %p in ObjC data", content); + return 0; + } + + Diagnostics& diagnostics() { return _diagnostics; } + +private: + struct Info { uint8_t* contentStart; uint8_t* contentEnd; uint64_t startAddr; uint64_t endAddr; }; + Diagnostics& _diagnostics; + Info _regions[3]; +}; + + +// Access a section containing a list of pointers +template +class PointerSection +{ + typedef typename P::uint_t pint_t; +public: + PointerSection(ContentAccessor* cache, const macho_header

* mh, + const char* segname, const char* sectname) + : _cache(cache), + _section(mh->getSection(segname, sectname)), + _base(_section ? (pint_t*)cache->contentForVMAddr(_section->addr()) : 0), + _count(_section ? (pint_t)(_section->size() / sizeof(pint_t)) : 0) { + } + + pint_t count() const { return _count; } + + pint_t getVMAddress(pint_t index) const { + if ( index >= _count ) { + _cache->diagnostics().error("index out of range in section %s", _section->sectname()); + return 0; + } + return (pint_t)P::getP(_base[index]); + } + + T get(pint_t index) const { + return (T)_cache->contentForVMAddr(getVMAddress(index)); + } + + void setVMAddress(pint_t index, pint_t value) { + if ( index >= _count ) { + _cache->diagnostics().error("index out of range in section %s", _section->sectname()); + return; + } + P::setP(_base[index], value); + } + + void removeNulls() { + pint_t shift = 0; + for (pint_t i = 0; i < _count; i++) { + pint_t value = _base[i]; + if (value) { + _base[i-shift] = value; + } else { + shift++; + } + } + _count -= shift; + const_cast*>(_section)->set_size(_count * sizeof(pint_t)); + } + +private: + ContentAccessor* const _cache; + const macho_section

* const _section; + pint_t* const _base; + pint_t const _count; +}; + + +// Access a section containing an array of structures +template +class ArraySection +{ +public: + ArraySection(ContentAccessor* cache, const macho_header

* mh, + const char *segname, const char *sectname) + : _cache(cache), + _section(mh->getSection(segname, sectname)), + _base(_section ? (T *)cache->contentForVMAddr(_section->addr()) : 0), + _count(_section ? _section->size() / sizeof(T) : 0) { + } + + uint64_t count() const { return _count; } + + T& get(uint64_t index) const { + if (index >= _count) { + _cache->diagnostics().error("index out of range in section %s", _section->sectname()); + } + return _base[index]; + } + +private: + ContentAccessor* const _cache; + const macho_section

* const _section; + T * const _base; + uint64_t const _count; +}; + + +#define SELOPT_WRITE +#include "objc-shared-cache.h" +#include "ObjC1Abstraction.hpp" +#include "ObjC2Abstraction.hpp" + + +namespace { + + + +template +class ObjCSelectorUniquer +{ +public: + typedef typename P::uint_t pint_t; + + ObjCSelectorUniquer(ContentAccessor* cache) : _cache(cache) { } + + pint_t visit(pint_t oldValue) + { + _count++; + const char *s = (const char *)_cache->contentForVMAddr(oldValue); + objc_opt::string_map::iterator element = + _selectorStrings.insert(objc_opt::string_map::value_type(s, oldValue)).first; + return (pint_t)element->second; + } + + objc_opt::string_map& strings() { + return _selectorStrings; + } + + size_t count() const { return _count; } + +private: + objc_opt::string_map _selectorStrings; + ContentAccessor* _cache; + size_t _count = 0; +}; + + +template +class ClassListBuilder +{ +private: + objc_opt::string_map _classNames; + objc_opt::class_map _classes; + size_t _count = 0; + HeaderInfoOptimizer>& _hInfos; + +public: + + ClassListBuilder(HeaderInfoOptimizer>& hinfos) : _hInfos(hinfos) { } + + void visitClass(ContentAccessor* cache, + const macho_header

* header, + objc_class_t

* cls) + { + if (cls->isMetaClass(cache)) return; + + const char *name = cls->getName(cache); + uint64_t name_vmaddr = cache->vmAddrForContent((void*)name); + uint64_t cls_vmaddr = cache->vmAddrForContent(cls); + uint64_t hinfo_vmaddr = cache->vmAddrForContent(_hInfos.hinfoForHeader(cache, header)); + _classNames.insert(objc_opt::string_map::value_type(name, name_vmaddr)); + _classes.insert(objc_opt::class_map::value_type(name, std::pair(cls_vmaddr, hinfo_vmaddr))); + _count++; + } + + objc_opt::string_map& classNames() { + return _classNames; + } + + objc_opt::class_map& classes() { + return _classes; + } + + size_t count() const { return _count; } +}; + +template +class ProtocolOptimizer +{ +private: + typedef typename P::uint_t pint_t; + + objc_opt::string_map _protocolNames; + objc_opt::protocol_map _protocols; + size_t _protocolCount; + size_t _protocolReferenceCount; + Diagnostics& _diagnostics; + + friend class ProtocolReferenceWalker>; + + pint_t visitProtocolReference(ContentAccessor* cache, pint_t oldValue) + { + objc_protocol_t

* proto = (objc_protocol_t

*) + cache->contentForVMAddr(oldValue); + pint_t newValue = (pint_t)_protocols[proto->getName(cache)]; + if (oldValue != newValue) _protocolReferenceCount++; + return newValue; + } + +public: + + ProtocolOptimizer(Diagnostics& diag) + : _protocolCount(0), _protocolReferenceCount(0), _diagnostics(diag) { + } + + void addProtocols(ContentAccessor* cache, const macho_header

* header) + { + PointerSection *> + protocols(cache, header, "__DATA", "__objc_protolist"); + + for (pint_t i = 0; i < protocols.count(); i++) { + objc_protocol_t

*proto = protocols.get(i); + + const char *name = proto->getName(cache); + if (_protocolNames.count(name) == 0) { + if (proto->getSize() > sizeof(objc_protocol_t

)) { + _diagnostics.error("objc protocol is too big"); + return; + } + + uint64_t name_vmaddr = cache->vmAddrForContent((void*)name); + uint64_t proto_vmaddr = cache->vmAddrForContent(proto); + _protocolNames.insert(objc_opt::string_map::value_type(name, name_vmaddr)); + _protocols.insert(objc_opt::protocol_map::value_type(name, proto_vmaddr)); + _protocolCount++; + } + } + } + + const char *writeProtocols(ContentAccessor* cache, + uint8_t *& rwdest, size_t& rwremaining, + uint8_t *& rodest, size_t& roremaining, + std::vector& pointersInData, + pint_t protocolClassVMAddr) + { + if (_protocolCount == 0) return NULL; + + if (protocolClassVMAddr == 0) { + return "libobjc's Protocol class symbol not found (metadata not optimized)"; + } + + size_t rwrequired = _protocolCount * sizeof(objc_protocol_t

); + if (rwremaining < rwrequired) { + return "libobjc's read-write section is too small (metadata not optimized)"; + } + + for (objc_opt::protocol_map::iterator iter = _protocols.begin(); + iter != _protocols.end(); + ++iter) + { + objc_protocol_t

* oldProto = (objc_protocol_t

*) + cache->contentForVMAddr(iter->second); + + // Create a new protocol object. + objc_protocol_t

* proto = (objc_protocol_t

*)rwdest; + rwdest += sizeof(*proto); + rwremaining -= sizeof(*proto); + + // Initialize it. + uint32_t oldSize = oldProto->getSize(); + memcpy(proto, oldProto, oldSize); + if (!proto->getIsaVMAddr()) { + proto->setIsaVMAddr(protocolClassVMAddr); + } + if (oldSize < sizeof(*proto)) { + // Protocol object is old. Populate new fields. + proto->setSize(sizeof(objc_protocol_t

)); + // missing extendedMethodTypes is already nil + } + // Some protocol objects are big enough to have the + // demangledName field but don't initialize it. + // Initialize it here if it is not already set. + if (!proto->getDemangledName(cache)) { + const char *roName = proto->getName(cache); + char *demangledName = copySwiftDemangledName(roName, true); + if (demangledName) { + size_t length = 1 + strlen(demangledName); + if (roremaining < length) { + return "libobjc's read-only section is too small (metadata not optimized)"; + } + + memmove(rodest, demangledName, length); + roName = (const char *)rodest; + rodest += length; + roremaining -= length; + + free(demangledName); + } + proto->setDemangledName(cache, roName, _diagnostics); + } + proto->setFixedUp(); + + // Redirect the protocol table at our new object. + iter->second = cache->vmAddrForContent(proto); + + // Add new rebase entries. + proto->addPointers(pointersInData); + } + + return NULL; + } + + void updateReferences(ContentAccessor* cache, const macho_header

* header) + { + ProtocolReferenceWalker> refs(*this); + refs.walk(cache, header); + } + + objc_opt::string_map& protocolNames() { + return _protocolNames; + } + + objc_opt::protocol_map& protocols() { + return _protocols; + } + + size_t protocolCount() const { return _protocolCount; } + size_t protocolReferenceCount() const { return _protocolReferenceCount; } +}; + + +static int percent(size_t num, size_t denom) { + if (denom) + return (int)(num / (double)denom * 100); + else + return 100; +} + + +template +void optimizeObjC(DyldSharedCache* cache, bool forProduction, std::vector& pointersForASLR, Diagnostics& diag) +{ + typedef typename P::E E; + typedef typename P::uint_t pint_t; + + diag.verbose("Optimizing objc metadata:\n"); + diag.verbose(" cache type is %s\n", forProduction ? "production" : "development"); + + ContentAccessor cacheAccessor(cache, diag); + + size_t headerSize = P::round_up(sizeof(objc_opt::objc_opt_t)); + if (headerSize != sizeof(objc_opt::objc_opt_t)) { + diag.warning("libobjc's optimization structure size is wrong (metadata not optimized)"); + } + + // + // Find libobjc's empty sections and build list of images with objc metadata + // + __block const macho_section

*optROSection = nullptr; + __block const macho_section

*optRWSection = nullptr; + __block const macho_section

*optPointerListSection = nullptr; + __block std::vector*> objcDylibs; + cache->forEachImage(^(const mach_header* machHeader, const char* installName) { + const macho_header

* mh = (const macho_header

*)machHeader; + if ( strstr(installName, "/libobjc.") != nullptr ) { + optROSection = mh->getSection("__TEXT", "__objc_opt_ro"); + optRWSection = mh->getSection("__DATA", "__objc_opt_rw"); + optPointerListSection = mh->getSection("__DATA", "__objc_opt_ptrs"); + } + if ( mh->getSection("__DATA", "__objc_imageinfo") || mh->getSection("__OBJC", "__image_info") ) { + objcDylibs.push_back(mh); + } + // log("installName %s at mhdr 0x%016lx", installName, (uintptr_t)cacheAccessor.vmAddrForContent((void*)mh)); + }); + if ( optROSection == nullptr ) { + diag.warning("libobjc's read-only section missing (metadata not optimized)"); + return; + } + if ( optRWSection == nullptr ) { + diag.warning("libobjc's read/write section missing (metadata not optimized)"); + return; + } + if ( optPointerListSection == nullptr ) { + diag.warning("libobjc's pointer list section missing (metadata not optimized)"); + return; + } + + uint8_t* optROData = (uint8_t*)cacheAccessor.contentForVMAddr(optROSection->addr()); + if ( optROData == nullptr ) { + diag.warning("libobjc's read-only section has bad content"); + return; + } + size_t optRORemaining = optROSection->size(); + uint8_t* optRWData = (uint8_t*)cacheAccessor.contentForVMAddr(optRWSection->addr()); + size_t optRWRemaining = optRWSection->size(); + if (optRORemaining < headerSize) { + diag.warning("libobjc's read-only section is too small (metadata not optimized)"); + return; + } + objc_opt::objc_opt_t* optROHeader = (objc_opt::objc_opt_t *)optROData; + optROData += headerSize; + optRORemaining -= headerSize; + if (E::get32(optROHeader->version) != objc_opt::VERSION) { + diag.warning("libobjc's read-only section version is unrecognized (metadata not optimized)"); + return; + } + + if (optPointerListSection->size() < sizeof(objc_opt::objc_opt_pointerlist_tt)) { + diag.warning("libobjc's pointer list section is too small (metadata not optimized)"); + return; + } + const objc_opt::objc_opt_pointerlist_tt *optPointerList = (const objc_opt::objc_opt_pointerlist_tt *)cacheAccessor.contentForVMAddr(optPointerListSection->addr()); + + // Write nothing to optROHeader until everything else is written. + // If something fails below, libobjc will not use the section. + + + // + // Make copy of objcList and sort that list. + // + std::vector*> addressSortedDylibs = objcDylibs; + std::sort(addressSortedDylibs.begin(), addressSortedDylibs.end(), [](const macho_header

* lmh, const macho_header

* rmh) -> bool { + return lmh < rmh; + }); + + // + // Build HeaderInfo list in cache + // + // First the RO header info + // log("writing out %d RO dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optROSection->size() - optRORemaining)); + uint64_t hinfoROVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + HeaderInfoOptimizer> hinfoROOptimizer; + const char* err = hinfoROOptimizer.init((uint32_t)objcDylibs.size(), optROData, optRORemaining); + if (err) { + diag.warning("%s", err); + return; + } + else { + for (const macho_header

* mh : addressSortedDylibs) { + hinfoROOptimizer.update(&cacheAccessor, mh, pointersForASLR); + } + } + + // Then the RW header info + // log("writing out %d RW dylibs at offset %d", (uint32_t)objcDylibs.size(), (uint32_t)(optRWSection->size() - optRWRemaining)); + uint64_t hinfoRWVMAddr = (uint64_t)optRWSection->addr() + (uint64_t)optRWSection->size() - optRWRemaining; + HeaderInfoOptimizer> hinfoRWOptimizer; + err = hinfoRWOptimizer.init((uint32_t)objcDylibs.size(), optRWData, optRWRemaining); + if (err) { + diag.warning("%s", err); + return; + } + else { + for (const macho_header

* mh : addressSortedDylibs) { + hinfoRWOptimizer.update(&cacheAccessor, mh, pointersForASLR); + } + } + + // + // Update selector references and build selector list + // + // 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 selector cstring data first. + // This tries to localize selector cstring memory. + // + ObjCSelectorUniquer

uniq(&cacheAccessor); + std::vector*> sizeSortedDylibs = objcDylibs; + std::sort(sizeSortedDylibs.begin(), sizeSortedDylibs.end(), [](const macho_header

* lmh, const macho_header

* rmh) -> bool { + const macho_section

* lSection = lmh->getSection("__TEXT", "__objc_methname"); + const macho_section

* rSection = rmh->getSection("__TEXT", "__objc_methname"); + uint64_t lSelectorSize = (lSection ? lSection->size() : 0); + uint64_t rSelectorSize = (rSection ? rSection->size() : 0); + return lSelectorSize > rSelectorSize; + }); + + SelectorOptimizer > selOptimizer(uniq); + for (const macho_header

* mh : sizeSortedDylibs) { + LegacySelectorUpdater>::update(&cacheAccessor, mh, uniq); + selOptimizer.optimize(&cacheAccessor, mh); + } + + diag.verbose(" uniqued %6lu selectors\n", uniq.strings().size()); + diag.verbose(" updated %6lu selector references\n", uniq.count()); + + uint64_t seloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + objc_opt::objc_selopt_t *selopt = new(optROData) objc_opt::objc_selopt_t; + err = selopt->write(seloptVMAddr, optRORemaining, uniq.strings()); + if (err) { + diag.warning("%s", err); + return; + } + optROData += selopt->size(); + optRORemaining -= selopt->size(); + uint32_t seloptCapacity = selopt->capacity; + uint32_t seloptOccupied = selopt->occupied; + selopt->byteswap(E::little_endian), selopt = nullptr; + + diag.verbose(" selector table occupancy %u/%u (%u%%)\n", + seloptOccupied, seloptCapacity, + (unsigned)(seloptOccupied/(double)seloptCapacity*100)); + + + // + // Detect classes that have missing weak-import superclasses. + // + // Production only. Development cache does not do this: a replacement + // library could omit a class at runtime that was present during + // cache construction. + // + // This is SAFE: the binaries themselves are unmodified. + bool noMissingWeakSuperclasses = false; // dev cache can't promise otherwise + if (forProduction) { + WeakClassDetector

weakopt; + noMissingWeakSuperclasses = + weakopt.noMissingWeakSuperclasses(&cacheAccessor, sizeSortedDylibs); + + // Shared cache does not currently support unbound weak references. + // Here we assert that there are none. If support is added later then + // this assertion needs to be removed and this path needs to be tested. + if (!noMissingWeakSuperclasses) { + diag.error("Some Objective-C class has a superclass that is " + "weak-import and missing from the cache."); + } + } + + + // + // Build class table. + // + // This is SAFE: the binaries themselves are unmodified. + ClassListBuilder

classes(hinfoROOptimizer); + ClassWalker> classWalker(classes); + for (const macho_header

* mh : sizeSortedDylibs) { + classWalker.walk(&cacheAccessor, mh); + } + + diag.verbose(" recorded % 6ld classes\n", classes.classNames().size()); + + uint64_t clsoptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + objc_opt::objc_clsopt_t *clsopt = new(optROData) objc_opt::objc_clsopt_t; + err = clsopt->write(clsoptVMAddr, optRORemaining, + classes.classNames(), classes.classes(), false); + if (err) { + diag.warning("%s", err); + return; + } + optROData += clsopt->size(); + optRORemaining -= clsopt->size(); + size_t duplicateCount = clsopt->duplicateCount(); + uint32_t clsoptCapacity = clsopt->capacity; + uint32_t clsoptOccupied = clsopt->occupied; + clsopt->byteswap(E::little_endian); + clsopt = nullptr; + + diag.verbose(" found % 6ld duplicate classes\n", + duplicateCount); + diag.verbose(" class table occupancy %u/%u (%u%%)\n", + clsoptOccupied, clsoptCapacity, + (unsigned)(clsoptOccupied/(double)clsoptCapacity*100)); + + + // + // Sort method lists. + // + // This is SAFE: modified binaries are still usable as unsorted lists. + // This must be done AFTER uniquing selectors. + MethodListSorter

methodSorter; + for (const macho_header

* mh : sizeSortedDylibs) { + methodSorter.optimize(&cacheAccessor, mh); + } + + diag.verbose(" sorted % 6ld method lists\n", methodSorter.optimized()); + + + // Unique protocols and build protocol table. + + // This is SAFE: no protocol references are updated yet + // This must be done AFTER updating method lists. + + ProtocolOptimizer

protocolOptimizer(diag); + for (const macho_header

* mh : sizeSortedDylibs) { + protocolOptimizer.addProtocols(&cacheAccessor, mh); + } + + diag.verbose(" uniqued % 6ld protocols\n", + protocolOptimizer.protocolCount()); + + pint_t protocolClassVMAddr = (pint_t)P::getP(optPointerList->protocolClass); + err = protocolOptimizer.writeProtocols(&cacheAccessor, + optRWData, optRWRemaining, + optROData, optRORemaining, + pointersForASLR, protocolClassVMAddr); + if (err) { + diag.warning("%s", err); + return; + } + + uint64_t protocoloptVMAddr = optROSection->addr() + optROSection->size() - optRORemaining; + objc_opt::objc_protocolopt_t *protocolopt = new (optROData) objc_opt::objc_protocolopt_t; + err = protocolopt->write(protocoloptVMAddr, optRORemaining, + protocolOptimizer.protocolNames(), + protocolOptimizer.protocols(), true); + if (err) { + diag.warning("%s", err); + return; + } + optROData += protocolopt->size(); + optRORemaining -= protocolopt->size(); + uint32_t protocoloptCapacity = protocolopt->capacity; + uint32_t protocoloptOccupied = protocolopt->occupied; + protocolopt->byteswap(E::little_endian), protocolopt = NULL; + + diag.verbose(" protocol table occupancy %u/%u (%u%%)\n", + protocoloptOccupied, protocoloptCapacity, + (unsigned)(protocoloptOccupied/(double)protocoloptCapacity*100)); + + + // Redirect protocol references to the uniqued protocols. + + // This is SAFE: the new protocol objects are still usable as-is. + for (const macho_header

* mh : sizeSortedDylibs) { + protocolOptimizer.updateReferences(&cacheAccessor, mh); + } + + diag.verbose(" updated % 6ld protocol references\n", protocolOptimizer.protocolReferenceCount()); + + + // + // Repair ivar offsets. + // + // This is SAFE: the runtime always validates ivar offsets at runtime. + IvarOffsetOptimizer

ivarOffsetOptimizer; + for (const macho_header

* mh : sizeSortedDylibs) { + ivarOffsetOptimizer.optimize(&cacheAccessor, mh); + } + + diag.verbose(" updated % 6ld ivar offsets\n", ivarOffsetOptimizer.optimized()); + + + // Collect flags. + uint32_t headerFlags = 0; + if (forProduction) { + headerFlags |= objc_opt::IsProduction; + } + if (noMissingWeakSuperclasses) { + headerFlags |= objc_opt::NoMissingWeakSuperclasses; + } + + + // Success. Mark dylibs as optimized. + for (const macho_header

* mh : sizeSortedDylibs) { + const macho_section

* imageInfoSection = mh->getSection("__DATA", "__objc_imageinfo"); + if (!imageInfoSection) { + imageInfoSection = mh->getSection("__OBJC", "__image_info"); + } + if (imageInfoSection) { + objc_image_info

* info = (objc_image_info

*)cacheAccessor.contentForVMAddr(imageInfoSection->addr()); + info->setOptimizedByDyld(); + } + } + + + // Success. Update RO header last. + E::set32(optROHeader->flags, headerFlags); + E::set32(optROHeader->selopt_offset, (uint32_t)(seloptVMAddr - optROSection->addr())); + E::set32(optROHeader->clsopt_offset, (uint32_t)(clsoptVMAddr - optROSection->addr())); + E::set32(optROHeader->protocolopt_offset, (uint32_t)(protocoloptVMAddr - optROSection->addr())); + E::set32(optROHeader->headeropt_ro_offset, (uint32_t)(hinfoROVMAddr - optROSection->addr())); + E::set32(optROHeader->headeropt_rw_offset, (uint32_t)(hinfoRWVMAddr - optROSection->addr())); + + // Log statistics. + size_t roSize = optROSection->size() - optRORemaining; + size_t rwSize = optRWSection->size() - optRWRemaining; + diag.verbose(" %lu/%llu bytes (%d%%) used in libobjc read-only optimization section\n", + roSize, optROSection->size(), percent(roSize, optROSection->size())); + diag.verbose(" %lu/%llu bytes (%d%%) used in libobjc read/write optimization section\n", + rwSize, optRWSection->size(), percent(rwSize, optRWSection->size())); + diag.verbose(" wrote objc metadata optimization version %d\n", objc_opt::VERSION); +} + + +} // anon namespace + +void optimizeObjC(DyldSharedCache* cache, bool is64, bool customerCache, std::vector& pointersForASLR, Diagnostics& diag) +{ + if ( is64 ) + optimizeObjC>(cache, customerCache, pointersForASLR, diag); + else + optimizeObjC>(cache, customerCache, pointersForASLR, diag); +} + + diff --git a/dyld/dyld3/shared-cache/StringUtils.h b/dyld/dyld3/shared-cache/StringUtils.h new file mode 100644 index 0000000..701e5fd --- /dev/null +++ b/dyld/dyld3/shared-cache/StringUtils.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017 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 StringUtils_h +#define StringUtils_h + +#include + +inline bool startsWith(const std::string& str, const std::string& prefix) +{ + return std::mismatch(prefix.begin(), prefix.end(), str.begin()).first == prefix.end(); +} + +inline bool endsWith(const std::string& str, const std::string& suffix) +{ + std::size_t index = str.find(suffix, str.size() - suffix.size()); + return (index != std::string::npos); +} + +inline bool contains(const std::string& str, const std::string& search) +{ + std::size_t index = str.find(search); + return (index != std::string::npos); +} + +inline char hexDigit(uint8_t value) +{ + if ( value < 10 ) + return '0' + value; + else + return 'a' + value - 10; +} + +inline void bytesToHex(const uint8_t* bytes, size_t byteCount, char buffer[]) +{ + char* p = buffer; + for (int i=0; i < byteCount; ++i) { + *p++ = hexDigit(bytes[i] >> 4); + *p++ = hexDigit(bytes[i] & 0x0F); + } + *p++ = '\0'; +} + +inline uint8_t hexCharToUInt(const char hexByte, uint8_t& value) { + if (hexByte >= '0' && hexByte <= '9') { + value = hexByte - '0'; + return true; + } else if (hexByte >= 'A' && hexByte <= 'F') { + value = hexByte - 'A' + 10; + return true; + } else if (hexByte >= 'a' && hexByte <= 'f') { + value = hexByte - 'a' + 10; + return true; + } + + return false; +} + +inline uint64_t hexToUInt64(const char* startHexByte, const char** endHexByte) { + if (startHexByte == nullptr) + return 0; + uint64_t retval = 0; + if (startHexByte[0] == '0' && startHexByte[1] == 'x') { + startHexByte +=2; + } + *endHexByte = startHexByte + 16; + + //FIXME overrun? + for (uint32_t i = 0; i < 16; ++i) { + uint8_t value; + if (!hexCharToUInt(startHexByte[i], value)) { + *endHexByte = &startHexByte[i]; + break; + } + retval = (retval << 4) + value; + } + return retval; +} + +inline bool hexToBytes(const char* startHexByte, uint32_t length, uint8_t buffer[]) { + if (startHexByte == nullptr) + return false; + const char *currentHexByte = startHexByte; + for (uint32_t i = 0; i < length; ++i) { + uint8_t value; + if (!hexCharToUInt(currentHexByte[i], value)) { + return false; + } + if (i%2 == 0) { + buffer[i/2] = value << 4; + } else { + buffer[(i-1)/2] |= value; + } + } + return true; +} + +#endif // StringUtils_h + diff --git a/dyld/dyld3/shared-cache/Trie.hpp b/dyld/dyld3/shared-cache/Trie.hpp new file mode 100644 index 0000000..e021095 --- /dev/null +++ b/dyld/dyld3/shared-cache/Trie.hpp @@ -0,0 +1,498 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-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@ + */ + +/* + //This is the exposed iterface for the Trie template + //TODO: add erase methods + //TODO: re-enable iterators + + template + struct Trie { + struct Entry + { + std::string name; + V info; + + Entry(void) {} + Entry(const std::string& N, V I) : name(N), info(I) {} + }; + + struct const_iterator : std::iterator; + + const_iterator begin() const; + const_iterator end() const; + + Trie(void); + Trie(const uint8_t* start, const uint8_t* end); + Trie(const std::vector& entries); + + void emit(std::vector& output); + */ + + +#ifndef __TRIE__ +#define __TRIE__ +#define TRIE_DEBUG (0) + +#include +#include +#include +#include +#include +#include + +#include + +#if __cplusplus <= 201103L +namespace std { + template + std::unique_ptr make_unique(Args&&... args) + { + return std::unique_ptr(new T(std::forward(args)...)); + } +} +#endif + +namespace TrieUtils { + +static void append_uleb128(uint64_t value, std::vector& out) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + out.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); +} + +static void append_string(std::string str, std::vector& out) { + for(char& c : str) + out.push_back(c); + out.push_back('\0'); +} + +static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; +} + +static +inline bool parse_uleb128(const uint8_t*& p, const uint8_t* end, uint64_t& result) { + result = 0; + int bit = 0; + do { + if (p == end) + return false; // malformed uleb128 extends beyond input + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + return false; // malformed + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return true; +} +}; + +template +struct Trie { + uint32_t count; + uint32_t nodeCount; + + struct Entry + { + std::string name; + V info; + + Entry(void) {} + Entry(const std::string& N, V I) : name(N), info(I) {} + }; + + Trie(const std::vector& entries) : count(0), nodeCount(1) { + // make nodes for all exported symbols + for (auto& entry : entries) { + addEntry(entry); + } + } + + void emit(std::vector& output) { + // create vector of nodes + std::vector orderedNodes; + orderedNodes.reserve(nodeCount); + orderTrie(&root, orderedNodes); + + // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized + bool more; + do { + uint32_t offset = 0; + more = false; + for (auto& node : orderedNodes) { + if (node->updateOffset(offset)) { + more = true; + } + } + } while ( more ); + + // create trie stream + for (auto& node : orderedNodes) { + node->appendToStream(output); + } + } + + static + inline bool parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) + { + // empty trie has no entries + if ( start == end ) + return false; + char cummulativeString[32768]; + std::vector entries; + if ( !processExportNode(start, start, end, cummulativeString, 0, entries) ) + return false; + // to preserve tie layout order, sort by node offset + std::sort(entries.begin(), entries.end()); + // copy to output + output.reserve(entries.size()); + for (auto& entryWithOffset : entries) { + output.push_back(entryWithOffset.entry); + } + return true; + } + +private: + struct Node + { + //This needs to be a map to unsure deterministic ordering of tries. + std::map > fChildren; + bool fIsTerminal; + uint32_t fTrieOffset; + V fInfo; + + Node(void) : fIsTerminal(false), fTrieOffset(0) {} + Node(V v) :fInfo(v), fIsTerminal(true), fTrieOffset(0) {} + Node(Node&&) = default; + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // 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; // length of export info when no export info + if ( fIsTerminal ) { + nodeSize = fInfo.encodedSize(); + // do have export info, overall node size so far is uleb128 of export info + export info + nodeSize += TrieUtils::uleb128_size(nodeSize); + } + // add children + ++nodeSize; // byte for count of chidren + + for (auto &edge : fChildren) { + nodeSize += edge.first.length() + 1 + TrieUtils::uleb128_size(edge.second->fTrieOffset); + } + + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(std::vector& out) { + if ( fIsTerminal ) { + fInfo.appendToStream(out); + } + else { + // no export info uleb128 of zero is one byte of zero + out.push_back(0); + } + // write number of children + out.push_back(fChildren.size()); + // write each child + for (auto &edge : fChildren) { + TrieUtils::append_string(edge.first, out); + TrieUtils::append_uleb128(edge.second->fTrieOffset, out); + } + } + }; + + Node root; + + struct EntryWithOffset + { + uintptr_t nodeOffset; + Entry entry; + + bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } + }; + + void addEntry(const std::string& fullStr, std::string::const_iterator start, V v) { + Node *currentNode = &root; + bool done = false; + + while (!done && !currentNode->fChildren.empty() ) { + done = true; + + for (auto &entry : currentNode->fChildren) { + auto res = std::mismatch(entry.first.begin(), entry.first.end(), start); + + if (res.first == entry.first.end()) { + //Matched a full edge, go down it + done = false; + currentNode = entry.second.get(); + start = res.second; + break; + } else if (res.first != entry.first.begin()) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + + //Build the new strings + std::string abEdgeStr(entry.first.begin(), res.first); + std::string bcEdgeStr(res.first, entry.first.end()); + + //Copy out the exist node and delete it from the currentNode + std::unique_ptr nodeC; + std::swap(nodeC, entry.second); + currentNode->fChildren.erase(entry.first); + + //Build the new node and insert it + std::unique_ptr nodeB = std::make_unique(); + Node *newNode = nodeB.get(); + + nodeB->fChildren.insert(std::make_pair(bcEdgeStr, std::move(nodeC))); + currentNode->fChildren.insert(std::make_pair(abEdgeStr, std::move(nodeB))); + + currentNode = newNode; + start = res.second; + ++nodeCount; + break; + } + } + } + + // no commonality with any existing child, make a new edge that is this whole string + std::string edgeStr(start, fullStr.end()); + v.willInsertAs(fullStr); + + if (edgeStr.empty()) { + currentNode->fIsTerminal = true; + currentNode->fInfo = v; + } else { + currentNode->fChildren.emplace(edgeStr, std::make_unique(v)); + ++nodeCount; + } + ++count; + } + + void addEntry(Entry entry) { + addEntry(entry.name, entry.name.begin(), entry.info); + } + +#if TRIE_DEBUG + void printTrie(Node& node, std::string cummulativeString) { + if (node.fTerminal) { + printf("%s: \n", cummulativeString.c_str()); + } + for (auto &edge : node.fChildren) { + printTrie(*edge.second, cummulativeString+edge.first); + } + } + +public: + void printTrie(void) { + printTrie(root, ""); + } +private: +#endif + + static inline bool processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset, + std::vector& output) + { + if ( p >= end ) + return false; + uint64_t terminalSize; + if ( !TrieUtils::parse_uleb128(p, end, terminalSize) ) + return false; + const uint8_t* children = p + terminalSize; + if ( children >= end ) + return false; + if ( terminalSize != 0 ) { + EntryWithOffset e; + e.nodeOffset = p-start; + e.entry.name = cummulativeString; + e.entry.info.loadData(p,end); + output.push_back(e); + } + const uint8_t childrenCount = *children++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint64_t childNodeOffet; + if ( !TrieUtils::parse_uleb128(s, end, childNodeOffet) ) + return false; + if ( !processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output) ) + return false; + } + return true; + } + + void orderTrie(Node* node, std::vector& orderedNodes) { + orderedNodes.push_back(node); + for (auto &edge : node->fChildren) { + orderTrie(edge.second.get(), orderedNodes); + } + } +}; // struct Trie + +struct ExportInfo { + uint64_t address; + uint64_t flags; + uint64_t other; + std::string importName; + + ExportInfo() : address(0), flags(0), other(0) { } + + uint32_t encodedSize(void) { + uint32_t size = 0; + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + size = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other); // ordinal + if ( !importName.empty() ) + size += importName.length(); + ++size; // trailing zero in imported name + } + else { + size = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address); + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + size += TrieUtils::uleb128_size(other); + } + return size; + } + + void appendToStream(std::vector& out) { + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( !importName.empty() ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = (uint32_t)(TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other) + importName.length() + 1); + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(other, out); + TrieUtils::append_string(importName, out); + } + else { + // nodes with re-export info: size, flags, ordinal, empty-string + uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(other) + 1; + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(other, out); + out.push_back(0); + } + } + else if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { + // nodes with export info: size, flags, address, other + uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address) + TrieUtils::uleb128_size(other); + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(address, out); + TrieUtils::append_uleb128(other, out); + } + else { + // nodes with export info: size, flags, address + uint32_t nodeSize = TrieUtils::uleb128_size(flags) + TrieUtils::uleb128_size(address); + out.push_back(nodeSize); + TrieUtils::append_uleb128(flags, out); + TrieUtils::append_uleb128(address, out); + } + } + + void loadData(const uint8_t* p, const uint8_t* const end) { + TrieUtils::parse_uleb128(p, end, flags); + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + TrieUtils::parse_uleb128(p, end, other); // dylib ordinal + importName = (char*)p; + } + else { + TrieUtils::parse_uleb128(p, end, address); + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + TrieUtils::parse_uleb128(p, end, other); + } + } + + void willInsertAs(const std::string& name) { + // Symbols re-exported under the same name do not need an explict import name, delete it to save space + if ((name == importName) ) { + importName = ""; + } + } +}; + +typedef Trie ExportInfoTrie; + + +// Used by accelerator tables in dyld shared cache +struct DylibIndex { + uint32_t index; + + DylibIndex() : index(0) {} + DylibIndex(uint32_t i) : index(i) {} + + uint32_t encodedSize(void) { + return TrieUtils::uleb128_size(index); + } + + void appendToStream(std::vector& out) { + uint32_t nodeSize = TrieUtils::uleb128_size(index); + out.push_back(nodeSize); + TrieUtils::append_uleb128(index, out); + } + + void loadData(const uint8_t* p, const uint8_t* const end) { + uint64_t temp; + TrieUtils::parse_uleb128(p, end, temp); + index = (uint32_t)temp; + } + + void willInsertAs(const std::string& name) { + } +}; +typedef Trie DylibIndexTrie; + + +#endif // __TRIE__ + + diff --git a/dyld/dyld3/shared-cache/dyld_cache_format.h b/dyld/dyld3/shared-cache/dyld_cache_format.h new file mode 100644 index 0000000..de63bf9 --- /dev/null +++ b/dyld/dyld3/shared-cache/dyld_cache_format.h @@ -0,0 +1,279 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_FORMAT__ +#define __DYLD_CACHE_FORMAT__ + +#include +#include + + +struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 i386" + 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 + uint64_t localSymbolsOffset; // file offset of where local symbols are stored + uint64_t localSymbolsSize; // size of local symbols information + uint8_t uuid[16]; // unique value for each shared cache file + uint64_t cacheType; // 0 for development, 1 for production + uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses + uint32_t branchPoolsCount; // number of uint64_t entries + uint64_t accelerateInfoAddr; // (unslid) address of optimization info + uint64_t accelerateInfoSize; // size of optimization info + uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info + uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries + uint64_t dylibsImageGroupAddr; // (unslid) address of ImageGroup for dylibs in this cache + uint64_t dylibsImageGroupSize; // size of ImageGroup for dylibs in this cache + uint64_t otherImageGroupAddr; // (unslid) address of ImageGroup for other OS dylibs + uint64_t otherImageGroupSize; // size of oImageGroup for other OS dylibs + uint64_t progClosuresAddr; // (unslid) address of list of program launch closures + uint64_t progClosuresSize; // size of list of program launch closures + uint64_t progClosuresTrieAddr; // (unslid) address of trie of indexes into program launch closures + uint64_t progClosuresTrieSize; // size of trie of indexes into program launch closures + uint32_t platform; // platform number (macOS=1, etc) + uint32_t formatVersion : 8, // launch_cache::binary_format::kFormatVersion + dylibsExpectedOnDisk : 1, // dyld should expect the dylib exists on disk and to compare inode/mtime to see if cache is valid + simulator : 1; // for simulator of specified platform + uint64_t sharedRegionStart; // base load address of cache if not slid + uint64_t sharedRegionSize; // overall size of region cache can be mapped into + uint64_t maxSlide; // runtime slide of cache can be between zero and this value +}; + + +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 +{ + uint64_t address; + uint64_t modTime; + uint64_t inode; + uint32_t pathFileOffset; + uint32_t pad; +}; + +struct dyld_cache_image_info_extra +{ + uint64_t exportsTrieAddr; // address of trie in unslid cache + uint64_t weakBindingsAddr; + uint32_t exportsTrieSize; + uint32_t weakBindingsSize; + uint32_t dependentsStartArrayIndex; + uint32_t reExportsStartArrayIndex; +}; + + +struct dyld_cache_accelerator_info +{ + uint32_t version; // currently 1 + uint32_t imageExtrasCount; // does not include aliases + uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra + uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes + uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths + uint32_t dylibTrieSize; // size of trie containing all dylib paths + uint32_t initializersOffset; // offset into this chunk to start of initializers list + uint32_t initializersCount; // size of initializers list + uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list + uint32_t dofSectionsCount; // size of initializers list + uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports + uint32_t reExportCount; // size of re-exports + uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward) + uint32_t depListCount; // size of dependencies + uint32_t rangeTableOffset; // offset into this chunk to start of ss + uint32_t rangeTableCount; // size of dependencies + uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache +}; + +struct dyld_cache_accelerator_initializer +{ + uint32_t functionOffset; // address offset from start of cache mapping + uint32_t imageIndex; +}; + +struct dyld_cache_range_entry +{ + uint64_t startAddress; // unslid address of start of region + uint32_t size; + uint32_t imageIndex; +}; + +struct dyld_cache_accelerator_dof +{ + uint64_t sectionAddress; // unslid address of start of region + uint32_t sectionSize; + uint32_t imageIndex; +}; + +struct dyld_cache_image_text_info +{ + uuid_t uuid; + uint64_t loadAddress; // unslid address of start of __TEXT + uint32_t textSegmentSize; + uint32_t pathOffset; // offset from start of cache file +}; + + +// The rebasing info is to allow the kernel to lazily rebase DATA pages of the +// dyld shared cache. Rebasing is adding the slide to interior pointers. +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]; +}; + + +// The version 2 of the slide info uses a different compression scheme. Since +// only interior pointers (pointers that point within the cache) are rebased +// (slid), we know the possible range of the pointers and thus know there are +// unused bits in each pointer. We use those bits to form a linked list of +// locations needing rebasing in each page. +// +// Definitions: +// +// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size +// pageStarts[] = info + info->page_starts_offset +// pageExtras[] = info + info->page_extras_offset +// valueMask = ~(info->delta_mask) +// deltaShift = __builtin_ctzll(info->delta_mask) - 2 +// +// There are three cases: +// +// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE +// The page contains no values that need rebasing. +// +// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 +// All rebase locations are in one linked list. The offset of the first +// rebase location in the page is pageStarts[pageIndex] * 4. +// +// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA +// Multiple linked lists are needed for all rebase locations in a page. +// The pagesExtras array contains 2 or more entries each of which is the +// start of a new linked list in the page. The first is at: +// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF) +// The next is at extrasStartIndex+1. The last is denoted by +// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[] +// set. +// +// For 64-bit architectures, there is always enough free bits to encode all +// possible deltas. The info->delta_mask field shows where the delta is located +// in the pointer. That value must be masked off (valueMask) before the slide +// is added to the pointer. +// +// For 32-bit architectures, there are only three bits free (the three most +// significant bits). To extract the delta, you must first subtract value_add +// from the pointer value, then AND with delta_mask, then shift by deltaShift. +// That still leaves a maximum delta to the next rebase location of 28 bytes. +// To reduce the number or chains needed, an optimization was added. Turns +// out zero is common in the DATA region. A zero can be turned into a +// non-rebasing entry in the linked list. The can be done because nothing +// in the shared cache should point out of its dylib to the start of the shared +// cache. +// +// The code for processing a linked list (chain) is: +// +// uint32_t delta = 1; +// while ( delta != 0 ) { +// uint8_t* loc = pageStart + pageOffset; +// uintptr_t rawValue = *((uintptr_t*)loc); +// delta = ((rawValue & deltaMask) >> deltaShift); +// uintptr_t newValue = (rawValue & valueMask); +// if ( newValue != 0 ) { +// newValue += valueAdd; +// newValue += slideAmount; +// } +// *((uintptr_t*)loc) = newValue; +// pageOffset += delta; +// } +// +// +struct dyld_cache_slide_info2 +{ + uint32_t version; // currently 2 + uint32_t page_size; // currently 4096 (may also be 16384) + uint32_t page_starts_offset; + uint32_t page_starts_count; + uint32_t page_extras_offset; + uint32_t page_extras_count; + uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location + uint64_t value_add; + //uint16_t page_starts[page_starts_count]; + //uint16_t page_extras[page_extras_count]; +}; +#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags +#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array) +#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing +#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page + + +struct dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; // offset into this chunk of nlist entries + uint32_t nlistCount; // count of nlist entries + uint32_t stringsOffset; // offset into this chunk of string pool + uint32_t stringsSize; // byte count of string pool + uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry + uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array +}; + +struct dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; // offset in cache file of start of dylib + uint32_t nlistStartIndex; // start index of locals for this dylib + uint32_t nlistCount; // number of local symbols for this dylib +}; + + + +#define MACOSX_DYLD_SHARED_CACHE_DIR "/private/var/db/dyld/" +#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" +#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" +#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development" + +static const uint64_t kDyldSharedCacheTypeDevelopment = 0; +static const uint64_t kDyldSharedCacheTypeProduction = 1; + + + + +#endif // __DYLD_CACHE_FORMAT__ + + diff --git a/dyld/dyld3/shared-cache/dyld_closure_util.cpp b/dyld/dyld3/shared-cache/dyld_closure_util.cpp new file mode 100644 index 0000000..db01f5f --- /dev/null +++ b/dyld/dyld3/shared-cache/dyld_closure_util.cpp @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2017 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "LaunchCache.h" +#include "LaunchCacheWriter.h" +#include "DyldSharedCache.h" +#include "FileUtils.h" +#include "ImageProxy.h" +#include "StringUtils.h" +#include "ClosureBuffer.h" + +extern "C" { + #include "closuredProtocol.h" +} + +static const DyldSharedCache* mapCacheFile(const char* path) +{ + struct stat statbuf; + if (stat(path, &statbuf)) { + fprintf(stderr, "Error: stat failed for dyld shared cache at %s\n", path); + return nullptr; + } + + int cache_fd = open(path, O_RDONLY); + if (cache_fd < 0) { + fprintf(stderr, "Error: failed to open shared cache file at %s\n", path); + return nullptr; + } + + void* mapped_cache = mmap(NULL, (size_t)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", path, errno); + return nullptr; + } + close(cache_fd); + + return (DyldSharedCache*)mapped_cache; +} + +struct CachedSections +{ + uint32_t mappedOffsetStart; + uint32_t mappedOffsetEnd; + uint64_t vmAddress; + const mach_header* mh; + std::string segmentName; + std::string sectionName; + const char* dylibPath; +}; + +static const CachedSections& find(uint32_t mappedOffset, const std::vector& sections) +{ + for (const CachedSections& entry : sections) { + //printf("0x%08X -> 0x%08X\n", entry.mappedOffsetStart, entry.mappedOffsetEnd); + if ( (entry.mappedOffsetStart <= mappedOffset) && (mappedOffset < entry.mappedOffsetEnd) ) + return entry; + } + assert(0 && "invalid offset"); +} + +/* +static const dyld3::launch_cache::BinaryClosureData* +callClosureDaemon(const std::string& mainPath, const std::string& cachePath, const std::vector& envArgs) +{ + + mach_port_t serverPort = MACH_PORT_NULL; + mach_port_t bootstrapPort = MACH_PORT_NULL; + kern_return_t kr = task_get_bootstrap_port(mach_task_self(), &bootstrapPort); + kr = bootstrap_look_up(bootstrapPort, "com.apple.dyld.closured", &serverPort); + switch( kr ) { + case BOOTSTRAP_SUCCESS : + // service currently registered, "a good thing" (tm) + break; + case BOOTSTRAP_UNKNOWN_SERVICE : + // service not currently registered, try again later + fprintf(stderr, "bootstrap_look_up(): %s\n", mach_error_string(kr)); + return nullptr; + default: + // service not currently registered, try again later + fprintf(stderr, "bootstrap_look_up(): %s [%d]\n", mach_error_string(kr), kr); + return nullptr; + } + + //printf("serverPort=%d, replyPort=%d\n", serverPort, replyPort); + + + + bool success; + char envBuffer[2048]; + vm_offset_t reply = 0; + uint32_t replySize = 0; + envBuffer[0] = '\0'; + envBuffer[1] = '\0'; +// kr = closured_CreateLaunchClosure(serverPort, mainPath.c_str(), cachePath.c_str(), uuid, envBuffer, &success, &reply, &replySize); + + printf("success=%d, buf=%p, bufLen=%d\n", success, (void*)reply, replySize); + + if (!success) + return nullptr; + return (const dyld3::launch_cache::BinaryClosureData*)reply; +} +*/ + +static void usage() +{ + printf("dyld_closure_util program to create of view dyld3 closures\n"); + printf(" mode:\n"); + printf(" -create_closure # create a closure for the specified main executable\n"); + printf(" -create_image_group # create an ImageGroup for the specified dylib/bundle\n"); + printf(" -list_dyld_cache_closures # list all closures in the dyld shared cache with size\n"); + printf(" -list_dyld_cache_other_dylibs # list all group-1 (non-cached dylibs/bundles)\n"); + printf(" -print_image_group # print specified ImageGroup file as JSON\n"); + printf(" -print_closure_file # print specified closure file as JSON\n"); + printf(" -print_dyld_cache_closure # find closure for specified program in dyld cache and print as JSON\n"); + printf(" -print_dyld_cache_dylibs # print group-0 (cached dylibs) as JSON\n"); + printf(" -print_dyld_cache_other_dylibs # print group-1 (non-cached dylibs/bundles) as JSON\n"); + printf(" -print_dyld_cache_other # print just one group-1 (non-cached dylib/bundle) as JSON\n"); + printf(" -print_dyld_cache_patch_table # print locations in shared cache that may need patching\n"); + printf(" options:\n"); + printf(" -cache_file # path to cache file to use (default is current cache)\n"); + printf(" -build_root # when building a closure, the path prefix when runtime volume is not current boot volume\n"); + printf(" -o # when building a closure, the file to write the (binary) closure to\n"); + printf(" -include_all_dylibs_in_dir # when building a closure, add other mach-o files found in directory\n"); + printf(" -env # when building a closure, DYLD_* env vars to assume\n"); + printf(" -dlopen # for use with -create_closure to append ImageGroup if target had called dlopen\n"); + printf(" -verbose_fixups # for use with -print* options to force printing fixups\n"); +} + +int main(int argc, const char* argv[]) +{ + const char* cacheFilePath = nullptr; + const char* inputMainExecutablePath = nullptr; + const char* inputTopImagePath = nullptr; + const char* outPath = nullptr; + const char* printPath = nullptr; + const char* printGroupPath = nullptr; + const char* printCacheClosure = nullptr; + const char* printCachedDylib = nullptr; + const char* printOtherDylib = nullptr; + bool listCacheClosures = false; + bool listOtherDylibs = false; + bool includeAllDylibs = false; + bool printClosures = false; + bool printCachedDylibs = false; + bool printOtherDylibs = false; + bool printPatchTable = false; + bool useClosured = false; + bool verboseFixups = false; + std::vector buildtimePrefixes; + std::vector envArgs; + std::vector dlopens; + + if ( argc == 1 ) { + usage(); + return 0; + } + + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if ( strcmp(arg, "-cache_file") == 0 ) { + cacheFilePath = argv[++i]; + if ( cacheFilePath == nullptr ) { + fprintf(stderr, "-cache_file option requires path to cache file\n"); + return 1; + } + } + else if ( strcmp(arg, "-create_closure") == 0 ) { + inputMainExecutablePath = argv[++i]; + if ( inputMainExecutablePath == nullptr ) { + fprintf(stderr, "-create_closure option requires a path to an executable\n"); + return 1; + } + } + else if ( strcmp(arg, "-create_image_group") == 0 ) { + inputTopImagePath = argv[++i]; + if ( inputTopImagePath == nullptr ) { + fprintf(stderr, "-create_image_group option requires a path to a dylib or bundle\n"); + return 1; + } + } + else if ( strcmp(arg, "-dlopen") == 0 ) { + const char* path = argv[++i]; + if ( path == nullptr ) { + fprintf(stderr, "-dlopen option requires a path to a packed closure list\n"); + return 1; + } + dlopens.push_back(path); + } + else if ( strcmp(arg, "-verbose_fixups") == 0 ) { + verboseFixups = true; + } + else if ( strcmp(arg, "-build_root") == 0 ) { + const char* buildRootPath = argv[++i]; + if ( buildRootPath == nullptr ) { + fprintf(stderr, "-build_root option requires a path \n"); + return 1; + } + buildtimePrefixes.push_back(buildRootPath); + } + else if ( strcmp(arg, "-o") == 0 ) { + outPath = argv[++i]; + if ( outPath == nullptr ) { + fprintf(stderr, "-o option requires a path \n"); + return 1; + } + } + else if ( strcmp(arg, "-print_closure_file") == 0 ) { + printPath = argv[++i]; + if ( printPath == nullptr ) { + fprintf(stderr, "-print_closure_file option requires a path \n"); + return 1; + } + } + else if ( strcmp(arg, "-print_image_group") == 0 ) { + printGroupPath = argv[++i]; + if ( printGroupPath == nullptr ) { + fprintf(stderr, "-print_image_group option requires a path \n"); + return 1; + } + } + else if ( strcmp(arg, "-list_dyld_cache_closures") == 0 ) { + listCacheClosures = true; + } + else if ( strcmp(arg, "-list_dyld_cache_other_dylibs") == 0 ) { + listOtherDylibs = true; + } + else if ( strcmp(arg, "-print_dyld_cache_closure") == 0 ) { + printCacheClosure = argv[++i]; + if ( printCacheClosure == nullptr ) { + fprintf(stderr, "-print_dyld_cache_closure option requires a path \n"); + return 1; + } + } + else if ( strcmp(arg, "-print_dyld_cache_closures") == 0 ) { + printClosures = true; + } + else if ( strcmp(arg, "-print_dyld_cache_dylibs") == 0 ) { + printCachedDylibs = true; + } + else if ( strcmp(arg, "-print_dyld_cache_other_dylibs") == 0 ) { + printOtherDylibs = true; + } + else if ( strcmp(arg, "-print_dyld_cache_dylib") == 0 ) { + printCachedDylib = argv[++i]; + if ( printCachedDylib == nullptr ) { + fprintf(stderr, "-print_dyld_cache_dylib option requires a path \n"); + return 1; + } + } + else if ( strcmp(arg, "-print_dyld_cache_other") == 0 ) { + printOtherDylib = argv[++i]; + if ( printOtherDylib == nullptr ) { + fprintf(stderr, "-print_dyld_cache_other option requires a path \n"); + return 1; + } + } + else if ( strcmp(arg, "-print_dyld_cache_patch_table") == 0 ) { + printPatchTable = true; + } + else if ( strcmp(arg, "-include_all_dylibs_in_dir") == 0 ) { + includeAllDylibs = true; + } + else if ( strcmp(arg, "-env") == 0 ) { + const char* envArg = argv[++i]; + if ( (envArg == nullptr) || (strchr(envArg, '=') == nullptr) ) { + fprintf(stderr, "-env option requires KEY=VALUE\n"); + return 1; + } + envArgs.push_back(envArg); + } + else if ( strcmp(arg, "-use_closured") == 0 ) { + useClosured = true; + } + else { + fprintf(stderr, "unknown option %s\n", arg); + return 1; + } + } + + if ( (inputMainExecutablePath || inputTopImagePath) && printPath ) { + fprintf(stderr, "-create_closure and -print_closure_file are mutually exclusive"); + return 1; + } + + const DyldSharedCache* dyldCache = nullptr; + bool dyldCacheIsRaw = false; + if ( cacheFilePath != nullptr ) { + dyldCache = mapCacheFile(cacheFilePath); + dyldCacheIsRaw = true; + } + else { +#if !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) + size_t cacheLength; + dyldCache = (DyldSharedCache*)_dyld_get_shared_cache_range(&cacheLength); + dyldCacheIsRaw = false; +#endif + } + dyld3::ClosureBuffer::CacheIdent cacheIdent; + dyldCache->getUUID(cacheIdent.cacheUUID); + cacheIdent.cacheAddress = (unsigned long)dyldCache; + cacheIdent.cacheMappedSize = dyldCache->mappedSize(); + dyld3::DyldCacheParser cacheParser(dyldCache, dyldCacheIsRaw); + + if ( buildtimePrefixes.empty() ) + buildtimePrefixes.push_back(""); + + std::vector existingGroups; + const dyld3::launch_cache::BinaryClosureData* mainClosure = nullptr; + if ( inputMainExecutablePath != nullptr ) { + dyld3::PathOverrides pathStuff(envArgs); + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3+dlopens.size(), theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + dyld3::launch_cache::DynArray groupList(2, &theGroups[0]); + dyld3::ClosureBuffer clsBuffer(cacheIdent, inputMainExecutablePath, groupList, pathStuff); + + std::string mainPath = inputMainExecutablePath; + for (const std::string& prefix : buildtimePrefixes) { + if ( startsWith(mainPath, prefix) ) { + mainPath = mainPath.substr(prefix.size()); + if ( mainPath[0] != '/' ) + mainPath = "/" + mainPath; + break; + } + } + + Diagnostics closureDiag; + //if ( useClosured ) + // mainClosure = closured_makeClosure(closureDiag, clsBuffer); + // else + mainClosure = dyld3::ImageProxyGroup::makeClosure(closureDiag, clsBuffer, mach_task_self(), buildtimePrefixes); + if ( closureDiag.hasError() ) { + fprintf(stderr, "dyld_closure_util: %s\n", closureDiag.errorMessage().c_str()); + return 1; + } + for (const std::string& warn : closureDiag.warnings() ) + fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str()); + + dyld3::launch_cache::Closure closure(mainClosure); + if ( outPath != nullptr ) { + safeSave(mainClosure, closure.size(), outPath); + } + else { + dyld3::launch_cache::Closure theClosure(mainClosure); + theGroups[2] = theClosure.group().binaryData(); + if ( !dlopens.empty() ) + printf("[\n"); + closure.printAsJSON(dyld3::launch_cache::ImageGroupList(3, &theGroups[0]), true); + + int groupIndex = 3; + for (const char* path : dlopens) { + printf(",\n"); + dyld3::launch_cache::DynArray groupList2(groupIndex-2, &theGroups[2]); + dyld3::ClosureBuffer dlopenBuffer(cacheIdent, path, groupList2, pathStuff); + Diagnostics dlopenDiag; + //if ( useClosured ) + // theGroups[groupIndex] = closured_makeDlopenGroup(closureDiag, clsBuffer); + //else + theGroups[groupIndex] = dyld3::ImageProxyGroup::makeDlopenGroup(dlopenDiag, dlopenBuffer, mach_task_self(), buildtimePrefixes); + if ( dlopenDiag.hasError() ) { + fprintf(stderr, "dyld_closure_util: %s\n", dlopenDiag.errorMessage().c_str()); + return 1; + } + for (const std::string& warn : dlopenDiag.warnings() ) + fprintf(stderr, "dyld_closure_util: warning: %s\n", warn.c_str()); + dyld3::launch_cache::ImageGroup dlopenGroup(theGroups[groupIndex]); + dlopenGroup.printAsJSON(dyld3::launch_cache::ImageGroupList(groupIndex+1, &theGroups[0]), true); + ++groupIndex; + } + if ( !dlopens.empty() ) + printf("]\n"); + } + + } +#if 0 + else if ( inputTopImagePath != nullptr ) { + std::string imagePath = inputTopImagePath; + for (const std::string& prefix : buildtimePrefixes) { + if ( startsWith(imagePath, prefix) ) { + imagePath = imagePath.substr(prefix.size()); + if ( imagePath[0] != '/' ) + imagePath = "/" + imagePath; + break; + } + } + + Diagnostics igDiag; + existingGroups.push_back(dyldCache->cachedDylibsGroup()); + existingGroups.push_back(dyldCache->otherDylibsGroup()); + if ( existingClosuresPath != nullptr ) { + size_t mappedSize; + const void* imageGroups = mapFileReadOnly(existingClosuresPath, mappedSize); + if ( imageGroups == nullptr ) { + fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath); + return 1; + } + uint32_t sentGroups = *(uint32_t*)imageGroups; + uint16_t lastGroupNum = 2; + existingGroups.resize(sentGroups+2); + const uint8_t* p = (uint8_t*)(imageGroups)+4; + //const uint8_t* end = (uint8_t*)(imageGroups) + mappedSize; + for (uint32_t i=0; i < sentGroups; ++i) { + const dyld3::launch_cache::binary_format::ImageGroup* aGroup = (const dyld3::launch_cache::binary_format::ImageGroup*)p; + existingGroups[2+i] = aGroup; + dyld3::launch_cache::ImageGroup imgrp(aGroup); + lastGroupNum = imgrp.groupNum(); + p += imgrp.size(); + } + } + const dyld3::launch_cache::binary_format::ImageGroup* ig = dyld3::ImageProxyGroup::makeDlopenGroup(igDiag, dyldCache, existingGroups.size(), existingGroups, imagePath, envArgs); + if ( igDiag.hasError() ) { + fprintf(stderr, "dyld_closure_util: %s\n", igDiag.errorMessage().c_str()); + return 1; + } + + dyld3::launch_cache::ImageGroup group(ig); + group.printAsJSON(dyldCache, true); + } +#endif + else if ( printPath != nullptr ) { + size_t mappedSize; + const void* buff = mapFileReadOnly(printPath, mappedSize); + if ( buff == nullptr ) { + fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath); + return 1; + } + dyld3::launch_cache::Closure theClosure((dyld3::launch_cache::binary_format::Closure*)buff); + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + theGroups[2] = theClosure.group().binaryData(); + theClosure.printAsJSON(theGroups, verboseFixups); + //closure.printStatistics(); + munmap((void*)buff, mappedSize); + } + else if ( printGroupPath != nullptr ) { + size_t mappedSize; + const void* buff = mapFileReadOnly(printGroupPath, mappedSize); + if ( buff == nullptr ) { + fprintf(stderr, "dyld_closure_util: could not read file %s\n", printPath); + return 1; + } + dyld3::launch_cache::ImageGroup group((dyld3::launch_cache::binary_format::ImageGroup*)buff); +// group.printAsJSON(dyldCache, verboseFixups); + munmap((void*)buff, mappedSize); + } + else if ( listCacheClosures ) { + cacheParser.forEachClosure(^(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* closureBinary) { + dyld3::launch_cache::Closure closure(closureBinary); + printf("%6lu %s\n", closure.size(), runtimePath); + }); + } + else if ( listOtherDylibs ) { + dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.otherDylibsGroup()); + for (uint32_t i=0; i < dylibGroup.imageCount(); ++i) { + dyld3::launch_cache::Image image = dylibGroup.image(i); + printf("%s\n", image.path()); + } + } + else if ( printCacheClosure ) { + const dyld3::launch_cache::BinaryClosureData* cls = cacheParser.findClosure(printCacheClosure); + if ( cls != nullptr ) { + dyld3::launch_cache::Closure theClosure(cls); + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + theGroups[2] = theClosure.group().binaryData(); + theClosure.printAsJSON(theGroups, verboseFixups); + } + else { + fprintf(stderr, "no closure in cache for %s\n", printCacheClosure); + } + } + else if ( printClosures ) { + cacheParser.forEachClosure(^(const char* runtimePath, const dyld3::launch_cache::binary_format::Closure* closureBinary) { + dyld3::launch_cache::Closure theClosure(closureBinary); + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + theGroups[2] = theClosure.group().binaryData(); + theClosure.printAsJSON(theGroups, verboseFixups); + }); + } + else if ( printCachedDylibs ) { + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + dyld3::launch_cache::ImageGroup dylibGroup(theGroups[0]); + dylibGroup.printAsJSON(theGroups, verboseFixups); + } + else if ( printCachedDylib != nullptr ) { + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.cachedDylibsGroup()); + uint32_t imageIndex; + const dyld3::launch_cache::binary_format::Image* binImage = dylibGroup.findImageByPath(printCachedDylib, imageIndex); + if ( binImage != nullptr ) { + dyld3::launch_cache::Image image(binImage); + image.printAsJSON(theGroups, true); + } + else { + fprintf(stderr, "no such other image found\n"); + } + } + else if ( printOtherDylibs ) { + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + dyld3::launch_cache::ImageGroup dylibGroup(theGroups[1]); + dylibGroup.printAsJSON(theGroups, verboseFixups); + } + else if ( printOtherDylib != nullptr ) { + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 2, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.otherDylibsGroup()); + uint32_t imageIndex; + const dyld3::launch_cache::binary_format::Image* binImage = dylibGroup.findImageByPath(printOtherDylib, imageIndex); + if ( binImage != nullptr ) { + dyld3::launch_cache::Image image(binImage); + image.printAsJSON(theGroups, true); + } + else { + fprintf(stderr, "no such other image found\n"); + } + } + else if ( printPatchTable ) { + __block uint64_t cacheBaseAddress = 0; + dyldCache->forEachRegion(^(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions) { + if ( cacheBaseAddress == 0 ) + cacheBaseAddress = vmAddr; + }); + __block std::vector sections; + __block bool hasError = false; + dyldCache->forEachImage(^(const mach_header* mh, const char* installName) { + dyld3::MachOParser parser(mh, dyldCacheIsRaw); + parser.forEachSection(^(const char* segName, const char* sectionName, uint32_t flags, uint64_t addr, const void* content, + uint64_t size, uint32_t alignP2, uint32_t reserved1, uint32_t reserved2, bool illegalSectionSize, bool& stop) { + if ( illegalSectionSize ) { + fprintf(stderr, "dyld_closure_util: section size extends beyond the end of the segment %s/%s\n", segName, sectionName); + stop = true; + return; + } + uint32_t offsetStart = (uint32_t)(addr - cacheBaseAddress); + uint32_t offsetEnd = (uint32_t)(offsetStart + size); + sections.push_back({offsetStart, offsetEnd, addr, mh, segName, sectionName, installName}); + }); + }); + if (hasError) + return 1; + dyld3::launch_cache::ImageGroup dylibGroup(cacheParser.cachedDylibsGroup()); + dylibGroup.forEachDyldCachePatchLocation(cacheParser, ^(uint32_t targetCacheVmOffset, const std::vector& usesPointersCacheVmOffsets, bool& stop) { + const CachedSections& targetSection = find(targetCacheVmOffset, sections); + dyld3::MachOParser targetParser(targetSection.mh, dyldCacheIsRaw); + const char* symbolName; + uint64_t symbolAddress; + if ( targetParser.findClosestSymbol(targetSection.vmAddress + targetCacheVmOffset - targetSection.mappedOffsetStart, &symbolName, &symbolAddress) ) { + printf("%s: [cache offset = 0x%08X]\n", symbolName, targetCacheVmOffset); + } + else { + printf("0x%08X from %40s %10s %16s + 0x%06X\n", targetCacheVmOffset, strrchr(targetSection.dylibPath, '/')+1, targetSection.segmentName.c_str(), targetSection.sectionName.c_str(), targetCacheVmOffset - targetSection.mappedOffsetStart); + } + for (uint32_t offset : usesPointersCacheVmOffsets) { + const CachedSections& usedInSection = find(offset, sections); + printf("%40s %10s %16s + 0x%06X\n", strrchr(usedInSection.dylibPath, '/')+1, usedInSection.segmentName.c_str(), usedInSection.sectionName.c_str(), offset - usedInSection.mappedOffsetStart); + } + }); + } + + + return 0; +} diff --git a/dyld/dyld3/shared-cache/dyld_shared_cache_builder.mm b/dyld/dyld3/shared-cache/dyld_shared_cache_builder.mm new file mode 100644 index 0000000..cbedce2 --- /dev/null +++ b/dyld/dyld3/shared-cache/dyld_shared_cache_builder.mm @@ -0,0 +1,381 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "Manifest.h" +#include "Diagnostics.h" +#include "DyldSharedCache.h" +#include "BuilderUtils.h" +#include "FileUtils.h" +#include "StringUtils.h" +#include "MachOParser.h" + +#if !__has_feature(objc_arc) +#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks +#endif + +extern char** environ; + +static dispatch_queue_t build_queue; + +static const char* tempRootDirTemplate = "/tmp/dyld_shared_cache_builder.XXXXXX"; +static char* tempRootDir = nullptr; + +int runCommandAndWait(Diagnostics& diags, const char* args[]) +{ + pid_t pid; + int status; + int res = posix_spawn(&pid, args[0], nullptr, nullptr, (char**)args, environ); + if (res != 0) + diags.error("Failed to spawn %s: %s (%d)", args[0], strerror(res), res); + + do { + res = waitpid(pid, &status, 0); + } while (res == -1 && errno == EINTR); + if (res != -1) { + if (WIFEXITED(status)) { + res = WEXITSTATUS(status); + } else { + res = -1; + } + } + + return res; +} + +void processRoots(Diagnostics& diags, std::set& roots) +{ + std::set processedRoots; + struct stat sb; + int res = 0; + const char* args[8]; + + for (const auto& root : roots) { + res = stat(root.c_str(), &sb); + + if (res == 0 && S_ISDIR(sb.st_mode)) { + roots.insert(root); + return; + } else if (endsWith(root, ".cpio") || endsWith(root, ".cpio.gz") || endsWith(root, ".cpgz") || endsWith(root, ".cpio.bz2") || endsWith(root, ".cpbz2") || endsWith(root, ".pax") || endsWith(root, ".pax.gz") || endsWith(root, ".pgz") || endsWith(root, ".pax.bz2") || endsWith(root, ".pbz2")) { + args[0] = (char*)"/usr/bin/ditto"; + args[1] = (char*)"-x"; + args[2] = (char*)root.c_str(); + args[3] = tempRootDir; + args[4] = nullptr; + } else if (endsWith(root, ".tar")) { + args[0] = (char*)"/usr/bin/tar"; + args[1] = (char*)"xf"; + args[2] = (char*)root.c_str(); + args[3] = (char*)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (endsWith(root, ".tar.gz") || endsWith(root, ".tgz")) { + args[0] = (char*)"/usr/bin/tar"; + args[1] = (char*)"xzf"; + args[2] = (char*)root.c_str(); + args[3] = (char*)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (endsWith(root, ".tar.bz2") + || endsWith(root, ".tbz2") + || endsWith(root, ".tbz")) { + args[0] = (char*)"/usr/bin/tar"; + args[1] = (char*)"xjf"; + args[2] = (char*)root.c_str(); + args[3] = (char*)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (endsWith(root, ".xar")) { + args[0] = (char*)"/usr/bin/xar"; + args[1] = (char*)"-xf"; + args[2] = (char*)root.c_str(); + args[3] = (char*)"-C"; + args[4] = tempRootDir; + args[5] = nullptr; + } else if (endsWith(root, ".zip")) { + args[0] = (char*)"/usr/bin/ditto"; + args[1] = (char*)"-xk"; + args[2] = (char*)root.c_str(); + args[3] = tempRootDir; + args[4] = nullptr; + } else { + diags.error("unknown archive type: %s", root.c_str()); + continue; + } + + if (res != runCommandAndWait(diags, args)) { + fprintf(stderr, "Could not expand archive %s: %s (%d)", root.c_str(), strerror(res), res); + exit(-1); + } + for (auto& existingRoot : processedRoots) { + if (existingRoot == tempRootDir) + return; + } + + processedRoots.insert(tempRootDir); + } + + roots = processedRoots; +} + +bool writeRootList(const std::string& dstRoot, const std::set& roots) +{ + if (roots.size() == 0) + return false; + + std::string rootFile = dstRoot + "/roots.txt"; + FILE* froots = ::fopen(rootFile.c_str(), "w"); + if (froots == NULL) + return false; + + for (auto& root : roots) { + fprintf(froots, "%s\n", root.c_str()); + } + + ::fclose(froots); + return true; +} + +std::set cachePaths; + +BOMCopierCopyOperation filteredCopy(BOMCopier copier, const char* path, BOMFSObjType type, off_t size) +{ + std::string absolutePath = &path[1]; + if (cachePaths.count(absolutePath)) { + return BOMCopierSkipFile; + } + return BOMCopierContinue; +} + +int main(int argc, const char* argv[]) +{ + @autoreleasepool { + __block Diagnostics diags; + std::set roots; + std::string dylibCacheDir; + std::string release; + bool emitDevCaches = true; + bool emitElidedDylibs = true; + bool listConfigs = false; + bool copyRoots = false; + bool debug = false; + std::string dstRoot; + std::string configuration; + std::string resultPath; + + tempRootDir = strdup(tempRootDirTemplate); + mkdtemp(tempRootDir); + + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (arg[0] == '-') { + if (strcmp(arg, "-debug") == 0) { + diags = Diagnostics(true); + debug = true; + } else if (strcmp(arg, "-list_configs") == 0) { + listConfigs = true; + } else if (strcmp(arg, "-root") == 0) { + roots.insert(realPath(argv[++i])); + } else if (strcmp(arg, "-copy_roots") == 0) { + copyRoots = true; + } else if (strcmp(arg, "-dylib_cache") == 0) { + dylibCacheDir = realPath(argv[++i]); + } else if (strcmp(arg, "-no_development_cache") == 0) { + emitDevCaches = false; + } else if (strcmp(arg, "-no_overflow_dylibs") == 0) { + emitElidedDylibs = false; + } else if (strcmp(arg, "-development_cache") == 0) { + emitDevCaches = true; + } else if (strcmp(arg, "-overflow_dylibs") == 0) { + emitElidedDylibs = true; + } else if (strcmp(arg, "-dst_root") == 0) { + dstRoot = realPath(argv[++i]); + } else if (strcmp(arg, "-release") == 0) { + release = argv[++i]; + } else if (strcmp(arg, "-results") == 0) { + resultPath = realPath(argv[++i]); + } else { + //usage(); + diags.error("unknown option: %s\n", arg); + } + } else { + if (!configuration.empty()) { + diags.error("You may only specify one configuration"); + } + configuration = argv[i]; + } + } + + time_t mytime = time(0); + fprintf(stderr, "Started: %s", asctime(localtime(&mytime))); + processRoots(diags, roots); + + struct rlimit rl = { OPEN_MAX, OPEN_MAX }; + (void)setrlimit(RLIMIT_NOFILE, &rl); + + if (dylibCacheDir.empty() && release.empty()) { + fprintf(stderr, "you must specify either -dylib_cache or -release"); + exit(-1); + } else if (!dylibCacheDir.empty() && !release.empty()) { + fprintf(stderr, "you may not use -dylib_cache and -release at the same time"); + exit(-1); + } + + if ((configuration.empty() || dstRoot.empty()) && !listConfigs) { + fprintf(stderr, "Must specify a configuration and a valid -dst_root OR -list_configs\n"); + exit(-1); + } + + if (dylibCacheDir.empty()) { + dylibCacheDir = std::string("/AppleInternal/Developer/DylibCaches/") + release + ".dlc"; + } + + //Move into the dir so we can use relative path manifests + chdir(dylibCacheDir.c_str()); + + dispatch_async(dispatch_get_main_queue(), ^{ + auto manifest = dyld3::Manifest(diags, dylibCacheDir + "/Manifest.plist", roots); + + if (manifest.build().empty()) { + fprintf(stderr, "No manifest found at '%s/Manifest.plist'\n", dylibCacheDir.c_str()); + exit(-1); + } + fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str()); + + if (listConfigs) { + manifest.forEachConfiguration([](const std::string& configName) { + printf("%s\n", configName.c_str()); + }); + } + + if (!manifest.filterForConfig(configuration)) { + fprintf(stderr, "No config %s. Please run with -list_configs to see configurations available for this %s.\n", + configuration.c_str(), manifest.build().c_str()); + exit(-1); + } + manifest.calculateClosure(); + + std::vector buildQueue; + + bool cacheBuildSuccess = build(diags, manifest, dstRoot, false, debug, false, false); + + if (!cacheBuildSuccess) { + exit(-1); + } + + writeRootList(dstRoot, roots); + + if (copyRoots) { + manifest.forEachConfiguration([&manifest](const std::string& configName) { + for (auto& arch : manifest.configuration(configName).architectures) { + for (auto& dylib : arch.second.results.dylibs) { + if (dylib.second.included) { + dyld3::MachOParser parser = manifest.parserForUUID(dylib.first); + cachePaths.insert(parser.installName()); + } + } + } + }); + + BOMCopier copier = BOMCopierNewWithSys(BomSys_default()); + BOMCopierSetCopyFileStartedHandler(copier, filteredCopy); + for (auto& root : roots) { + BOMCopierCopy(copier, root.c_str(), dstRoot.c_str()); + } + BOMCopierFree(copier); + } + + + + + int err = sync_volume_np(dstRoot.c_str(), SYNC_VOLUME_FULLSYNC | SYNC_VOLUME_WAIT); + if (err) { + fprintf(stderr, "Volume sync failed errnor=%d (%s)\n", err, strerror(err)); + } + + // Create an empty FIPS data in the root + (void)mkpath_np((dstRoot + "/private/var/db/FIPS/").c_str(), 0755); + int fd = open((dstRoot + "/private/var/db/FIPS/fips_data").c_str(), O_CREAT | O_TRUNC, 0644); + close(fd); + + // Now that all the build commands have been issued lets put a barrier in after then which can tear down the app after + // everything is written. + + if (!resultPath.empty()) { + manifest.write(resultPath); + } + + const char* args[8]; + args[0] = (char*)"/bin/rm"; + args[1] = (char*)"-rf"; + args[2] = (char*)tempRootDir; + args[3] = nullptr; + (void)runCommandAndWait(diags, args); + + for (const std::string& warn : diags.warnings()) { + fprintf(stderr, "dyld_shared_cache_builder: warning: %s\n", warn.c_str()); + } + exit(0); + }); + } + + dispatch_main(); + + return 0; +} diff --git a/dyld/dyld3/shared-cache/make_ios_dyld_cache.cpp b/dyld/dyld3/shared-cache/make_ios_dyld_cache.cpp new file mode 100644 index 0000000..588d7d8 --- /dev/null +++ b/dyld/dyld3/shared-cache/make_ios_dyld_cache.cpp @@ -0,0 +1,356 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "MachOParser.h" +#include "FileUtils.h" +#include "StringUtils.h" +#include "DyldSharedCache.h" + + + +struct MappedMachOsByCategory +{ + std::string archName; + std::vector dylibsForCache; + std::vector otherDylibsAndBundles; + std::vector mainExecutables; +}; + +static bool verbose = false; + + +static bool addIfMachO(const std::string& buildRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector& files) +{ + // read start of file to determine if it is mach-o or a fat file + std::string fullPath = buildRootPath + runtimePath; + int fd = ::open(fullPath.c_str(), O_RDONLY); + if ( fd < 0 ) + return false; + bool result = false; + const void* wholeFile = ::mmap(NULL, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if ( wholeFile != MAP_FAILED ) { + Diagnostics diag; + bool usedWholeFile = false; + for (MappedMachOsByCategory& file : files) { + size_t sliceOffset; + size_t sliceLength; + bool fatButMissingSlice; + const void* slice = MAP_FAILED; + if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) { + slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset); + if ( slice != MAP_FAILED ) { + //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str()); + if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, platform, slice, sliceLength, fullPath.c_str(), false) ) { + ::munmap((void*)slice, sliceLength); + slice = MAP_FAILED; + } + } + } + else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, platform, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) { + slice = wholeFile; + sliceLength = statBuf.st_size; + sliceOffset = 0; + usedWholeFile = true; + //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str()); + } + if ( slice != MAP_FAILED ) { + const mach_header* mh = (mach_header*)slice; + dyld3::MachOParser parser(mh); + if ( parser.platform() != platform ) { + fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str()); + result = false; + } + else { + bool sip = true; // assume anything found in the simulator runtime is a platform binary + if ( parser.isDynamicExecutable() ) { + bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID)); + file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + else { + if ( parser.canBePlacedInDyldCache(runtimePath) ) { + file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + else { + file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + } + result = true; + } + } + } + if ( !usedWholeFile ) + ::munmap((void*)wholeFile, statBuf.st_size); + } + ::close(fd); + return result; +} + + +static bool parsePathsFile(const std::string& filePath, std::vector& paths) { + std::ifstream myfile( filePath ); + if ( myfile.is_open() ) { + std::string line; + while ( std::getline(myfile, line) ) { + size_t pos = line.find('#'); + if ( pos != std::string::npos ) + line.resize(pos); + while ( line.size() != 0 && isspace(line.back()) ) { + line.pop_back(); + } + if ( !line.empty() ) + paths.push_back(line); + } + myfile.close(); + return true; + } + return false; +} + + +static void mapAllFiles(const std::string& dylibsRootDir, const std::vector& paths, dyld3::Platform platform, std::vector& files) +{ + for (const std::string& runtimePath : paths) { + std::string fullPath = dylibsRootDir + runtimePath; + struct stat statBuf; + if ( (stat(fullPath.c_str(), &statBuf) != 0) || !addIfMachO(dylibsRootDir, runtimePath, statBuf, platform, files) ) + fprintf(stderr, "could not load: %s\n", fullPath.c_str()); + } +} + + + +inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) +{ + return (uint32_t)(abstime/1000/1000); +} + + +#define TERMINATE_IF_LAST_ARG( s ) \ + do { \ + if ( i == argc - 1 ) { \ + fprintf(stderr, s ); \ + return 1; \ + } \ + } while ( 0 ) + +int main(int argc, const char* argv[]) +{ + std::string rootPath; + std::string dylibListFile; + bool force = false; + std::string cacheDir; + std::string dylibsList; + std::unordered_set archStrs; + + dyld3::Platform platform = dyld3::Platform::iOS; + + // parse command line options + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (strcmp(arg, "-debug") == 0) { + verbose = true; + } + else if (strcmp(arg, "-verbose") == 0) { + verbose = true; + } + else if (strcmp(arg, "-tvOS") == 0) { + platform = dyld3::Platform::tvOS; + } + else if (strcmp(arg, "-iOS") == 0) { + platform = dyld3::Platform::iOS; + } + else if (strcmp(arg, "-watchOS") == 0) { + platform = dyld3::Platform::watchOS; + } + else if ( strcmp(arg, "-root") == 0 ) { + TERMINATE_IF_LAST_ARG("-root missing path argument\n"); + rootPath = argv[++i]; + } + else if ( strcmp(arg, "-dylibs_list") == 0 ) { + TERMINATE_IF_LAST_ARG("-dylibs_list missing path argument\n"); + dylibsList = argv[++i]; + } + else if (strcmp(arg, "-cache_dir") == 0) { + TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n"); + cacheDir = argv[++i]; + } + else if (strcmp(arg, "-arch") == 0) { + TERMINATE_IF_LAST_ARG("-arch missing argument\n"); + archStrs.insert(argv[++i]); + } + else if (strcmp(arg, "-force") == 0) { + force = true; + } + else { + //usage(); + fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg); + return 1; + } + } + + if ( cacheDir.empty() ) { + fprintf(stderr, "missing -cache_dir option to specify directory in which to write cache file(s)\n"); + return 1; + } + + if ( rootPath.empty() ) { + fprintf(stderr, "missing -runtime_dir option to specify directory which is root of simulator runtime)\n"); + return 1; + } + else { + // canonicalize rootPath + char resolvedPath[PATH_MAX]; + if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) { + rootPath = resolvedPath; + } + if ( rootPath.back() != '/' ) + rootPath = rootPath + "/"; + } + + int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + if ( (err != 0) && (err != EEXIST) ) { + fprintf(stderr, "mkpath_np fail: %d", err); + return 1; + } + + if ( archStrs.empty() ) { + switch ( platform ) { + case dyld3::Platform::iOS: + case dyld3::Platform::tvOS: + archStrs.insert("arm64"); + break; + case dyld3::Platform::watchOS: + archStrs.insert("armv7k"); + break; + case dyld3::Platform::unknown: + case dyld3::Platform::macOS: + assert(0 && "macOS not support with this tool"); + break; + } + } + + uint64_t t1 = mach_absolute_time(); + + // find all mach-o files for requested architectures + std::vector allFileSets; + if ( archStrs.count("arm64") ) + allFileSets.push_back({"arm64"}); + if ( archStrs.count("armv7k") ) + allFileSets.push_back({"armv7k"}); + std::vector paths; + parsePathsFile(dylibsList, paths); + mapAllFiles(rootPath, paths, platform, allFileSets); + + uint64_t t2 = mach_absolute_time(); + + fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1)); + + // build all caches in parallel + __block bool cacheBuildFailure = false; + dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) { + const MappedMachOsByCategory& fileSet = allFileSets[index]; + const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName; + + fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size()); + + // build cache new cache file + DyldSharedCache::CreateOptions options; + options.archName = fileSet.archName; + options.platform = platform; + options.excludeLocalSymbols = true; + options.optimizeStubs = false; + options.optimizeObjC = true; + options.codeSigningDigestMode = (platform() == dyld3::Platform::watchOS) ? + DyldSharedCache::Agile : DyldSharedCache::SHA256only; + options.dylibsRemovedDuringMastering = true; + options.inodesAreSameAsRuntime = false; + options.cacheSupportsASLR = true; + options.forSimulator = false; + options.verbose = verbose; + options.evictLeafDylibsOnOverflow = false; + options.pathPrefixes = { rootPath }; + DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables); + + // print any warnings + for (const std::string& warn : results.warnings) { + fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str()); + } + if ( !results.errorMessage.empty() ) { + // print error (if one) + fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str()); + cacheBuildFailure = true; + } + else { + // save new cache file to disk and write new .map file + assert(results.cacheContent != nullptr); + if ( !safeSave(results.cacheContent, results.cacheLength, outFile) ) + cacheBuildFailure = true; + if ( !cacheBuildFailure ) { + std::string mapStr = results.cacheContent->mapFile(); + std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map"; + safeSave(mapStr.c_str(), mapStr.size(), outFileMap); + } + // free created cache buffer + vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength); + } + }); + + // we could unmap all input files, but tool is about to quit + + return (cacheBuildFailure ? 1 : 0); +} + diff --git a/dyld/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm b/dyld/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm new file mode 100644 index 0000000..8d106d1 --- /dev/null +++ b/dyld/dyld3/shared-cache/multi_dyld_shared_cache_builder.mm @@ -0,0 +1,282 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Manifest.h" +#include "FileUtils.h" +#include "BuilderUtils.h" + +#define CACHE_BUILDER_COPY_FILE_MODE COPYFILE_ALL + +#if !__has_feature(objc_arc) +#error The use of libdispatch in this files requires it to be compiled with ARC in order to avoid leaks +#endif + +static dispatch_queue_t build_queue = dispatch_queue_create("com.apple.dyld.cache-builder.build", DISPATCH_QUEUE_CONCURRENT); + +#define kDylibCachePrefix "/AppleInternal/Developer/DylibCaches/" + +void createArtifact(Diagnostics& diags, dyld3::Manifest& manifest, const std::string& dylibCachePath, bool includeExecutables) +{ + auto copy_state = copyfile_state_alloc(); + mkpath_np((dylibCachePath + "/Metadata").c_str(), 0755); + (void)copyfile(manifest.metabomFile().c_str(), (dylibCachePath + "/Metadata/metabom.bom").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); + + if (!manifest.dylibOrderFile().empty()) { + (void)copyfile(realPath(manifest.dylibOrderFile()).c_str(), (dylibCachePath + "/Metadata/dylibOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); + } + + if (!manifest.dirtyDataOrderFile().empty()) { + (void)copyfile(realPath(manifest.dirtyDataOrderFile()).c_str(), (dylibCachePath + "/Metadata/dirtyDataOrderFile.txt").c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); + } + + std::set uuids; + std::map copy_pairs; + + manifest.forEachConfiguration([&manifest, &uuids](const std::string& config) { + manifest.configuration(config).forEachArchitecture([&manifest, &config, &uuids](const std::string& arch) { + auto results = manifest.configuration(config).architecture(arch).results; + for (const auto& image : results.dylibs) { + uuids.insert(image.first); + } + for (const auto& image : results.bundles) { + uuids.insert(image.first); + } + for (const auto& image : results.executables) { + uuids.insert(image.first); + } + }); + }); + + for (auto& uuid : uuids) { + auto buildPath = manifest.buildPathForUUID(uuid); + auto installPath = manifest.runtimePathForUUID(uuid); + assert(!buildPath.empty() && !installPath.empty()); + copy_pairs.insert(std::make_pair(installPath, buildPath)); + } + + for (const auto& copy_pair : copy_pairs) { + std::string from = realPath(copy_pair.second); + std::string to = dylibCachePath + "/Root/" + copy_pair.first; + mkpath_np(dirPath(to).c_str(), 0755); + int err = copyfile(from.c_str(), to.c_str(), copy_state, CACHE_BUILDER_COPY_FILE_MODE); + diags.verbose("COPYING (%d) %s -> %s\n", err, from.c_str(), to.c_str()); + + } + copyfile_state_free(copy_state); + + fprintf(stderr, "[Artifact] dylibs copied\n"); +} + +void addArtifactPaths(Diagnostics& diags, dyld3::Manifest& manifest) +{ + manifest.setDylibOrderFile("./Metadata/dylibOrderFile.txt"); + manifest.setDirtyDataOrderFile("./Metadata/dirtyDataOrderFile.txt"); + manifest.setMetabomFile("./Metadata/metabom.bom"); + + for (auto& projects : manifest.projects()) { + manifest.addProjectSource(projects.first, "./Root", true); + } +} + +int main(int argc, const char* argv[]) +{ + @autoreleasepool { + __block Diagnostics diags; + bool verbose = false; + std::string masterDstRoot; + std::string dylibCacheDir; + std::string resultPath; + std::string manifestPath; + bool preflight = false; + __block bool allBuildsSucceeded = true; + bool skipWrites = false; + bool skipBuilds = false; + bool agileChooseSHA256CdHash = false; + time_t mytime = time(0); + fprintf(stderr, "Started: %s", asctime(localtime(&mytime))); + + // parse command line options + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (arg[0] == '-') { + if (strcmp(arg, "-debug") == 0) { + verbose = true; + diags = Diagnostics(true); + } else if (strcmp(arg, "-skip_writes") == 0) { + skipWrites = true; + } else if (strcmp(arg, "-skip_builds") == 0) { + skipBuilds = true; + } else if (strcmp(arg, "-delete_writes") == 0) { + skipWrites = true; + } else if (strcmp(arg, "-dylib_cache") == 0) { + dylibCacheDir = argv[++i]; + } else if (strcmp(arg, "-preflight") == 0) { + preflight = true; + skipWrites = true; + } else if (strcmp(arg, "-master_dst_root") == 0) { + masterDstRoot = argv[++i]; + if (masterDstRoot.empty()) { + diags.error("-master_dst_root missing path argument"); + } + } else if (strcmp(arg, "-results") == 0) { + resultPath = argv[++i]; + } else if (strcmp(arg, "-plist") == 0) { + manifestPath = argv[++i]; + } else if (strcmp(arg, "-agile_choose_sha256_cdhash") == 0) { + agileChooseSHA256CdHash = true; + } else { + // usage(); + diags.error("unknown option: %s", arg); + } + } else { + manifestPath = argv[i]; + } + } + + if (getenv("LGG_SKIP_CACHE_FUN") != nullptr) { + skipBuilds = true; + } + + if (diags.hasError()) { + printf("%s\n", diags.errorMessage().c_str()); + exit(-1); + } + + dispatch_async(dispatch_get_main_queue(), ^{ + if (manifestPath.empty()) { + fprintf(stderr, "mainfest path argument is required\n"); + exit(-1); + } + + (void)chdir(dirname(strdup(manifestPath.c_str()))); + __block auto manifest = dyld3::Manifest(diags, manifestPath); + + if (manifest.build().empty()) { + fprintf(stderr, "No version found in manifest\n"); + exit(-1); + } + + fprintf(stderr, "Building Caches for %s\n", manifest.build().c_str()); + + if (masterDstRoot.empty()) { + fprintf(stderr, "-master_dst_root required path argument\n"); + exit(-1); + } + + if (manifest.version() < 4) { + fprintf(stderr, "must specify valid manifest file\n"); + exit(-1); + } + + struct rlimit rl = { OPEN_MAX, OPEN_MAX }; + (void)setrlimit(RLIMIT_NOFILE, &rl); + + manifest.calculateClosure(); + + if (!skipWrites && !skipBuilds) { + (void)mkpath_np((masterDstRoot + "/Boms/").c_str(), 0755); + dispatch_group_async(buildGroup(), build_queue, ^{ + createArtifact(diags, manifest, masterDstRoot + "/Artifact.dlc/", true); + }); + } + + if (!dylibCacheDir.empty()) { + dispatch_group_async(buildGroup(), build_queue, ^{ + createArtifact(diags, manifest, dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/", false); + }); + } + + if (!skipBuilds) { + dispatch_group_async(buildGroup(), build_queue, ^{ + makeBoms(manifest, masterDstRoot); + }); + allBuildsSucceeded = build(diags, manifest, masterDstRoot, true, verbose, skipWrites, + agileChooseSHA256CdHash); + } + + manifest.write(resultPath); + + addArtifactPaths(diags, manifest); + if (!dylibCacheDir.empty()) { + manifest.write(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.plist"); + manifest.writeJSON(dylibCacheDir + "/AppleInternal/Developer/DylibCaches/" + manifest.build() + ".dlc/Manifest.json"); + } + + if (!skipWrites) { + mkpath_np((masterDstRoot + "/Artifact.dlc").c_str(), 0755); + auto copy_state = copyfile_state_alloc(); + (void)copyfile(realPath(manifestPath).c_str(), (masterDstRoot + "/Artifact.dlc/BNIManifest.plist").c_str(), copy_state, COPYFILE_ALL); + copyfile_state_free(copy_state); + manifest.write(masterDstRoot + "/Artifact.dlc/Manifest.plist"); + manifest.writeJSON(masterDstRoot + "/Artifact.dlc/Manifest.json"); + } + + dispatch_group_wait(buildGroup(), DISPATCH_TIME_FOREVER); + time_t mytime = time(0); + fprintf(stderr, "Finished: %s", asctime(localtime(&mytime))); + + if (preflight && !allBuildsSucceeded) { + exit(-1); + } + + exit(0); + }); + } + dispatch_main(); + return 0; +} diff --git a/dyld/dyld3/shared-cache/update_dyld_shared_cache.cpp b/dyld/dyld3/shared-cache/update_dyld_shared_cache.cpp new file mode 100644 index 0000000..1e82704 --- /dev/null +++ b/dyld/dyld3/shared-cache/update_dyld_shared_cache.cpp @@ -0,0 +1,839 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "MachOParser.h" +#include "FileUtils.h" +#include "StringUtils.h" +#include "DyldSharedCache.h" + +struct MappedMachOsByCategory +{ + std::string archName; + std::vector dylibsForCache; + std::vector otherDylibsAndBundles; + std::vector mainExecutables; +}; + +static const char* sAllowedPrefixes[] = { + "/bin/", + "/sbin/", + "/usr/", + "/System", + "/Applications/App Store.app/", + "/Applications/Automator.app/", + "/Applications/Calculator.app/", + "/Applications/Calendar.app/", + "/Applications/Chess.app/", + "/Applications/Contacts.app/", +// "/Applications/DVD Player.app/", + "/Applications/Dashboard.app/", + "/Applications/Dictionary.app/", + "/Applications/FaceTime.app/", + "/Applications/Font Book.app/", + "/Applications/Image Capture.app/", + "/Applications/Launchpad.app/", + "/Applications/Mail.app/", + "/Applications/Maps.app/", + "/Applications/Messages.app/", + "/Applications/Mission Control.app/", + "/Applications/Notes.app/", + "/Applications/Photo Booth.app/", +// "/Applications/Photos.app/", + "/Applications/Preview.app/", + "/Applications/QuickTime Player.app/", + "/Applications/Reminders.app/", + "/Applications/Safari.app/", + "/Applications/Siri.app/", + "/Applications/Stickies.app/", + "/Applications/System Preferences.app/", + "/Applications/TextEdit.app/", + "/Applications/Time Machine.app/", + "/Applications/iBooks.app/", + "/Applications/iTunes.app/", + "/Applications/Utilities/Activity Monitor.app", + "/Applications/Utilities/AirPort Utility.app", + "/Applications/Utilities/Audio MIDI Setup.app", + "/Applications/Utilities/Bluetooth File Exchange.app", + "/Applications/Utilities/Boot Camp Assistant.app", + "/Applications/Utilities/ColorSync Utility.app", + "/Applications/Utilities/Console.app", + "/Applications/Utilities/Digital Color Meter.app", + "/Applications/Utilities/Disk Utility.app", + "/Applications/Utilities/Grab.app", + "/Applications/Utilities/Grapher.app", + "/Applications/Utilities/Keychain Access.app", + "/Applications/Utilities/Migration Assistant.app", + "/Applications/Utilities/Script Editor.app", + "/Applications/Utilities/System Information.app", + "/Applications/Utilities/Terminal.app", + "/Applications/Utilities/VoiceOver Utility.app", + "/Library/CoreMediaIO/Plug-Ins/DAL/" // temp until plugins moved or closured working +}; + +static const char* sDontUsePrefixes[] = { + "/usr/share", + "/usr/local/", + "/System/Library/Assets", + "/System/Library/StagedFrameworks", + "/System/Library/Kernels/", + "/bin/zsh", // until is fixed + "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/Metadata.framework/Versions/A/Support/mdworker", // these load third party plugins + "/usr/bin/mdimport", // these load third party plugins +}; + + +static bool verbose = false; + + + +static bool addIfMachO(const std::string& pathPrefix, const std::string& runtimePath, const struct stat& statBuf, bool requireSIP, std::vector& files) +{ + // don't precompute closure info for any debug or profile dylibs + if ( endsWith(runtimePath, "_profile.dylib") || endsWith(runtimePath, "_debug.dylib") || endsWith(runtimePath, "_profile") || endsWith(runtimePath, "_debug") ) + return false; + + // read start of file to determine if it is mach-o or a fat file + std::string fullPath = pathPrefix + runtimePath; + int fd = ::open(fullPath.c_str(), O_RDONLY); + if ( fd < 0 ) + return false; + bool result = false; + const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if ( wholeFile != MAP_FAILED ) { + Diagnostics diag; + bool usedWholeFile = false; + for (MappedMachOsByCategory& file : files) { + size_t sliceOffset; + size_t sliceLength; + bool fatButMissingSlice; + const void* slice = MAP_FAILED; + if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) { + slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE | MAP_RESILIENT_CODESIGN, fd, sliceOffset); + if ( slice != MAP_FAILED ) { + //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str()); + if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, dyld3::Platform::macOS, slice, sliceLength, fullPath.c_str(), false) ) { + ::munmap((void*)slice, sliceLength); + slice = MAP_FAILED; + } + } + } + else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, dyld3::Platform::macOS, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) { + slice = wholeFile; + sliceLength = statBuf.st_size; + sliceOffset = 0; + usedWholeFile = true; + //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str()); + } + std::vector nonArchWarnings; + for (const std::string& warning : diag.warnings()) { + if ( !contains(warning, "required architecture") && !contains(warning, "not a dylib") ) + nonArchWarnings.push_back(warning); + } + diag.clearWarnings(); + if ( !nonArchWarnings.empty() ) { + fprintf(stderr, "update_dyld_shared_cache: warning: %s for %s: ", file.archName.c_str(), runtimePath.c_str()); + for (const std::string& warning : nonArchWarnings) { + fprintf(stderr, "%s ", warning.c_str()); + } + fprintf(stderr, "\n"); + } + if ( slice != MAP_FAILED ) { + const mach_header* mh = (mach_header*)slice; + dyld3::MachOParser parser((mach_header*)slice); + bool sipProtected = isProtectedBySIP(fd); + bool issetuid = false; + if ( parser.isDynamicExecutable() ) { + // When SIP enabled, only build closures for SIP protected programs + if ( !requireSIP || sipProtected ) { + //fprintf(stderr, "requireSIP=%d, sipProtected=%d, path=%s\n", requireSIP, sipProtected, fullPath.c_str()); + issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID)); + file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + } + else if ( parser.canBePlacedInDyldCache(runtimePath) ) { + // when SIP is enabled, only dylib protected by SIP can go in cache + if ( !requireSIP || sipProtected ) + file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + else + file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + else { + if ( parser.fileType() == MH_DYLIB ) { + std::string installName = parser.installName(); + if ( startsWith(installName, "@") && !contains(runtimePath, ".app/") ) { + if ( startsWith(runtimePath, "/usr/lib/") || startsWith(runtimePath, "/System/Library/") ) + fprintf(stderr, "update_dyld_shared_cache: warning @rpath install name for system framework: %s\n", runtimePath.c_str()); + } + } + file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, issetuid, sipProtected, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + result = true; + } + } + if ( !usedWholeFile ) + ::munmap((void*)wholeFile, statBuf.st_size); + } + ::close(fd); + return result; +} + +static void findAllFiles(const std::vector& pathPrefixes, bool requireSIP, std::vector& files) +{ + std::unordered_set skipDirs; + for (const char* s : sDontUsePrefixes) + skipDirs.insert(s); + + __block std::unordered_set alreadyUsed; + bool multiplePrefixes = (pathPrefixes.size() > 1); + for (const std::string& prefix : pathPrefixes) { + // get all files from overlay for this search dir + for (const char* searchDir : sAllowedPrefixes ) { + iterateDirectoryTree(prefix, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) { + // ignore files that don't have 'x' bit set (all runnable mach-o files do) + const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH); + if ( !hasXBit && !endsWith(path, ".dylib") ) + return; + + // ignore files too small + if ( statBuf.st_size < 0x3000 ) + return; + + // don't add paths already found using previous prefix + if ( multiplePrefixes && (alreadyUsed.count(path) != 0) ) + return; + + // if the file is mach-o, add to list + if ( addIfMachO(prefix, path, statBuf, requireSIP, files) ) { + if ( multiplePrefixes ) + alreadyUsed.insert(path); + } + }); + } + } +} + + +static void findOSFilesViaBOMS(const std::vector& pathPrefixes, bool requireSIP, std::vector& files) +{ + __block std::unordered_set runtimePathsFound; + for (const std::string& prefix : pathPrefixes) { + iterateDirectoryTree(prefix, "/System/Library/Receipts", ^(const std::string&) { return false; }, ^(const std::string& path, const struct stat& statBuf) { + if ( !contains(path, "com.apple.pkg.") ) + return; + if ( !endsWith(path, ".bom") ) + return; + std::string fullPath = prefix + path; + BOMBom bom = BOMBomOpenWithSys(fullPath.c_str(), false, NULL); + if ( bom == nullptr ) + return; + BOMFSObject rootFso = BOMBomGetRootFSObject(bom); + if ( rootFso == nullptr ) { + BOMBomFree(bom); + return; + } + BOMBomEnumerator e = BOMBomEnumeratorNew(bom, rootFso); + if ( e == nullptr ) { + fprintf(stderr, "Can't get enumerator for BOM root FSObject\n"); + return; + } + BOMFSObjectFree(rootFso); + //fprintf(stderr, "using BOM %s\n", path.c_str()); + while (BOMFSObject fso = BOMBomEnumeratorNext(e)) { + if ( BOMFSObjectIsBinaryObject(fso) ) { + const char* runPath = BOMFSObjectPathName(fso); + if ( (runPath[0] == '.') && (runPath[1] == '/') ) + ++runPath; + if ( runtimePathsFound.count(runPath) == 0 ) { + // only add files from sAllowedPrefixes and not in sDontUsePrefixes + bool inSearchDir = false; + for (const char* searchDir : sAllowedPrefixes ) { + if ( strncmp(searchDir, runPath, strlen(searchDir)) == 0 ) { + inSearchDir = true; + break; + } + } + if ( inSearchDir ) { + bool inSkipDir = false; + for (const char* skipDir : sDontUsePrefixes) { + if ( strncmp(skipDir, runPath, strlen(skipDir)) == 0 ) { + inSkipDir = true; + break; + } + } + if ( !inSkipDir ) { + for (const std::string& prefix2 : pathPrefixes) { + struct stat statBuf2; + std::string fullPath2 = prefix2 + runPath; + if ( stat(fullPath2.c_str(), &statBuf2) == 0 ) { + addIfMachO(prefix2, runPath, statBuf2, requireSIP, files); + runtimePathsFound.insert(runPath); + break; + } + } + } + } + } + } + BOMFSObjectFree(fso); + } + + BOMBomEnumeratorFree(e); + BOMBomFree(bom); + }); + } +} + + +static bool dontCache(const std::string& volumePrefix, const std::string& archName, + const std::unordered_set& pathsWithDuplicateInstallName, + const DyldSharedCache::MappedMachO& aFile, bool warn, + const std::unordered_set& skipDylibs) +{ + if ( skipDylibs.count(aFile.runtimePath) ) + return true; + if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") ) + return true; + if ( startsWith(aFile.runtimePath, "/System/Library/QuickTime/") ) + return true; + if ( startsWith(aFile.runtimePath, "/System/Library/Tcl/") ) + return true; + if ( startsWith(aFile.runtimePath, "/System/Library/Perl/") ) + return true; + if ( startsWith(aFile.runtimePath, "/System/Library/MonitorPanels/") ) + return true; + if ( startsWith(aFile.runtimePath, "/System/Library/Accessibility/") ) + return true; + if ( startsWith(aFile.runtimePath, "/usr/local/") ) + return true; + + // anything inside a .app bundle is specific to app, so should not be in shared cache + if ( aFile.runtimePath.find(".app/") != std::string::npos ) + return true; + + if ( archName == "i386" ) { + if ( startsWith(aFile.runtimePath, "/System/Library/CoreServices/") ) + return true; + if ( startsWith(aFile.runtimePath, "/System/Library/Extensions/") ) + return true; + } + + if ( aFile.runtimePath.find("//") != std::string::npos ) { + if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); + return true; + } + + dyld3::MachOParser parser(aFile.mh); + const char* installName = parser.installName(); + if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) { + if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); + return true; + } + + if ( aFile.runtimePath != installName ) { + // see if install name is a symlink to actual path + std::string fullInstall = volumePrefix + installName; + char resolvedPath[PATH_MAX]; + if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) { + std::string resolvedSymlink = resolvedPath; + if ( !volumePrefix.empty() ) { + resolvedSymlink = resolvedSymlink.substr(volumePrefix.size()); + } + if ( aFile.runtimePath == resolvedSymlink ) { + return false; + } + } + if (warn) fprintf(stderr, "update_dyld_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); + return true; + } + return false; +} + +static void pruneCachedDylibs(const std::string& volumePrefix, const std::unordered_set& skipDylibs, MappedMachOsByCategory& fileSet) +{ + std::unordered_set pathsWithDuplicateInstallName; + + std::unordered_map installNameToFirstPath; + for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { + dyld3::MachOParser parser(aFile.mh); + const char* installName = parser.installName(); + auto pos = installNameToFirstPath.find(installName); + if ( pos == installNameToFirstPath.end() ) { + installNameToFirstPath[installName] = aFile.runtimePath; + } + else { + pathsWithDuplicateInstallName.insert(aFile.runtimePath); + pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]); + } + } + + for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { + if ( dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, true, skipDylibs) ) + fileSet.otherDylibsAndBundles.push_back(aFile); + } + fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(), + [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(volumePrefix, fileSet.archName, pathsWithDuplicateInstallName, aFile, false, skipDylibs); }), + fileSet.dylibsForCache.end()); +} + +static void pruneOtherDylibs(const std::string& volumePrefix, MappedMachOsByCategory& fileSet) +{ + // other OS dylibs should not contain dylibs that are embedded in some .app bundle + fileSet.otherDylibsAndBundles.erase(std::remove_if(fileSet.otherDylibsAndBundles.begin(), fileSet.otherDylibsAndBundles.end(), + [&](const DyldSharedCache::MappedMachO& aFile) { return (aFile.runtimePath.find(".app/") != std::string::npos); }), + fileSet.otherDylibsAndBundles.end()); +} + + +static void pruneExecutables(const std::string& volumePrefix, MappedMachOsByCategory& fileSet) +{ + // don't build closures for xcode shims in /usr/bin (e.g. /usr/bin/clang) which re-exec themselves to a tool inside Xcode.app + fileSet.mainExecutables.erase(std::remove_if(fileSet.mainExecutables.begin(), fileSet.mainExecutables.end(), + [&](const DyldSharedCache::MappedMachO& aFile) { + if ( !startsWith(aFile.runtimePath, "/usr/bin/") ) + return false; + dyld3::MachOParser parser(aFile.mh); + __block bool isXcodeShim = false; + parser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool &stop) { + if ( strcmp(loadPath, "/usr/lib/libxcselect.dylib") == 0 ) + isXcodeShim = true; + }); + return isXcodeShim; + }), fileSet.mainExecutables.end()); +} + +static bool existingCacheUpToDate(const std::string& existingCache, const std::vector& currentDylibs) +{ + // if no existing cache, it is not up-to-date + int fd = ::open(existingCache.c_str(), O_RDONLY); + if ( fd < 0 ) + return false; + + // build map of found dylibs + std::unordered_map currentDylibMap; + for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) { + //fprintf(stderr, "0x%0llX 0x%0llX %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str()); + currentDylibMap[aFile.runtimePath] = &aFile; + } + + // make sure all dylibs in existing cache have same mtime and inode as found dylib + __block bool foundMismatch = false; + const uint64_t cacheMapLen = 0x40000000; + void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0); + if ( p != MAP_FAILED ) { + const DyldSharedCache* cache = (DyldSharedCache*)p; + cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) { + bool foundMatch = false; + auto pos = currentDylibMap.find(installName); + if ( pos != currentDylibMap.end() ) { + const DyldSharedCache::MappedMachO* foundDylib = pos->second; + if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) { + foundMatch = true; + } + } + if ( !foundMatch ) { + // use slow path and look for any dylib with a matching inode and mtime + bool foundSlow = false; + for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) { + if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) { + foundSlow = true; + break; + } + } + if ( !foundSlow ) { + foundMismatch = true; + if ( verbose ) + fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName); + } + } + }); + ::munmap(p, cacheMapLen); + } + + ::close(fd); + + return !foundMismatch; +} + + +inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) +{ + return (uint32_t)(abstime/1000/1000); +} + +static bool runningOnHaswell() +{ + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + + return ( (result == KERN_SUCCESS) && (info.cpu_subtype == CPU_SUBTYPE_X86_64_H) ); +} + + + +#define TERMINATE_IF_LAST_ARG( s ) \ + do { \ + if ( i == argc - 1 ) { \ + fprintf(stderr, s ); \ + return 1; \ + } \ + } while ( 0 ) + +int main(int argc, const char* argv[]) +{ + std::string rootPath; + std::string overlayPath; + std::string dylibListFile; + bool universal = false; + bool force = false; + bool searchDisk = false; + bool dylibsRemoved = false; + std::string cacheDir; + std::unordered_set archStrs; + std::unordered_set skipDylibs; + + // parse command line options + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (strcmp(arg, "-debug") == 0) { + verbose = true; + } + else if (strcmp(arg, "-verbose") == 0) { + verbose = true; + } + else if (strcmp(arg, "-dont_map_local_symbols") == 0) { + //We are going to ignore this + } + else if (strcmp(arg, "-dylib_list") == 0) { + TERMINATE_IF_LAST_ARG("-dylib_list missing argument"); + dylibListFile = argv[++i]; + } + else if ((strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0)) { + TERMINATE_IF_LAST_ARG("-root missing path argument\n"); + rootPath = argv[++i]; + } + else if (strcmp(arg, "-overlay") == 0) { + TERMINATE_IF_LAST_ARG("-overlay missing path argument\n"); + overlayPath = argv[++i]; + } + else if (strcmp(arg, "-cache_dir") == 0) { + TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n"); + cacheDir = argv[++i]; + } + else if (strcmp(arg, "-arch") == 0) { + TERMINATE_IF_LAST_ARG("-arch missing argument\n"); + archStrs.insert(argv[++i]); + } + else if (strcmp(arg, "-search_disk") == 0) { + searchDisk = true; + } + else if (strcmp(arg, "-dylibs_removed_in_mastering") == 0) { + dylibsRemoved = true; + } + else if (strcmp(arg, "-force") == 0) { + force = true; + } + else if (strcmp(arg, "-sort_by_name") == 0) { + //No-op, we always do this now + } + else if (strcmp(arg, "-universal_boot") == 0) { + universal = true; + } + else if (strcmp(arg, "-skip") == 0) { + TERMINATE_IF_LAST_ARG("-skip missing argument\n"); + skipDylibs.insert(argv[++i]); + } + else { + //usage(); + fprintf(stderr, "update_dyld_shared_cache: unknown option: %s\n", arg); + return 1; + } + } + + if ( !rootPath.empty() & !overlayPath.empty() ) { + fprintf(stderr, "-root and -overlay cannot be used together\n"); + return 1; + } + // canonicalize rootPath + if ( !rootPath.empty() ) { + char resolvedPath[PATH_MAX]; + if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) { + rootPath = resolvedPath; + } + // when building closures for boot volume, pathPrefixes should be empty + if ( rootPath == "/" ) { + rootPath = ""; + } + } + // canonicalize overlayPath + if ( !overlayPath.empty() ) { + char resolvedPath[PATH_MAX]; + if ( realpath(overlayPath.c_str(), resolvedPath) != NULL ) { + overlayPath = resolvedPath; + } + } + // + // pathPrefixes for three modes: + // 1) no options: { "" } // search only boot volume + // 2) -overlay: { overlay, "" } // search overlay, then boot volume + // 3) -root: { root } // search only -root volume + // + std::vector pathPrefixes; + if ( !overlayPath.empty() ) + pathPrefixes.push_back(overlayPath); + pathPrefixes.push_back(rootPath); + + + if ( cacheDir.empty() ) { + // write cache file into -root or -overlay directory, if used + if ( rootPath != "/" ) + cacheDir = rootPath + MACOSX_DYLD_SHARED_CACHE_DIR; + else if ( !overlayPath.empty() ) + cacheDir = overlayPath + MACOSX_DYLD_SHARED_CACHE_DIR; + else + cacheDir = MACOSX_DYLD_SHARED_CACHE_DIR; + } + + int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + if ( (err != 0) && (err != EEXIST) ) { + fprintf(stderr, "mkpath_np fail: %d", err); + return 1; + } + + if ( archStrs.empty() ) { + if ( universal ) { + // -universal_boot should make all possible dyld caches + archStrs.insert("i386"); + archStrs.insert("x86_64"); + archStrs.insert("x86_64h"); + } + else { + // just make caches for this machine + archStrs.insert("i386"); + archStrs.insert(runningOnHaswell() ? "x86_64h" : "x86_64"); + } + } + + uint64_t t1 = mach_absolute_time(); + + // find all mach-o files for requested architectures + bool requireDylibsBeRootlessProtected = isProtectedBySIP(cacheDir); + __block std::vector allFileSets; + if ( archStrs.count("x86_64") ) + allFileSets.push_back({"x86_64"}); + if ( archStrs.count("x86_64h") ) + allFileSets.push_back({"x86_64h"}); + if ( archStrs.count("i386") ) + allFileSets.push_back({"i386"}); + if ( searchDisk ) + findAllFiles(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets); + else { + std::unordered_set runtimePathsFound; + findOSFilesViaBOMS(pathPrefixes, requireDylibsBeRootlessProtected, allFileSets); + } + + // nothing in OS uses i386 dylibs, so only dylibs used by third party apps need to be in cache + for (MappedMachOsByCategory& fileSet : allFileSets) { + pruneCachedDylibs(rootPath, skipDylibs, fileSet); + pruneOtherDylibs(rootPath, fileSet); + pruneExecutables(rootPath, fileSet); + } + + uint64_t t2 = mach_absolute_time(); + if ( verbose ) { + if ( searchDisk ) + fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1)); + else + fprintf(stderr, "time to read BOM and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1)); + } + + // build caches in parallel on machines with at leat 4GB of RAM + uint64_t memSize = 0; + size_t sz = sizeof(memSize);; + bool buildInParallel = false; + if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) { + if ( memSize >= 0x100000000ULL ) + buildInParallel = true; + } + dispatch_queue_t dqueue = buildInParallel ? dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) + : dispatch_queue_create("serial-queue", DISPATCH_QUEUE_SERIAL); + + // build all caches + __block bool cacheBuildFailure = false; + __block bool wroteSomeCacheFile = false; + dispatch_apply(allFileSets.size(), dqueue, ^(size_t index) { + MappedMachOsByCategory& fileSet = allFileSets[index]; + const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName; + + DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) { + if ( skipDylibs.count(runtimePath) ) + return DyldSharedCache::MappedMachO(); + for (const std::string& prefix : pathPrefixes) { + std::string fullPath = prefix + runtimePath; + struct stat statBuf; + if ( stat(fullPath.c_str(), &statBuf) == 0 ) { + std::vector mappedFiles; + mappedFiles.push_back({fileSet.archName}); + if ( addIfMachO(prefix, runtimePath, statBuf, requireDylibsBeRootlessProtected, mappedFiles) ) { + if ( !mappedFiles.back().dylibsForCache.empty() ) + return mappedFiles.back().dylibsForCache.back(); + } + } + } + return DyldSharedCache::MappedMachO(); + }; + size_t startCount = fileSet.dylibsForCache.size(); + std::vector>> excludes; + DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, loader, excludes); + for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) { + fprintf(stderr, "update_dyld_shared_cache: warning: %s not in .bom, but adding required dylib %s\n", fileSet.archName.c_str(), fileSet.dylibsForCache[i].runtimePath.c_str()); + } + for (auto& exclude : excludes) { + std::string reasons = "(\""; + for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) { + reasons += *i; + if (i != --exclude.second.end()) { + reasons += "\", \""; + } + } + reasons += "\")"; + fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archName.c_str(), exclude.first.runtimePath.c_str(), reasons.c_str()); + fileSet.otherDylibsAndBundles.push_back(exclude.first); + } + + // check if cache is already up to date + if ( !force ) { + if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) ) + return; + } + + // add any extra dylibs needed which were not in .bom + fprintf(stderr, "update_dyld_shared_cache: %s incorporating %lu OS dylibs, tracking %lu others, building closures for %lu executables\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size()); + //for (const DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { + // fprintf(stderr, " %s\n", aFile.runtimePath.c_str()); + //} + + + // build cache new cache file + DyldSharedCache::CreateOptions options; + options.archName = fileSet.archName; + options.platform = dyld3::Platform::macOS; + options.excludeLocalSymbols = false; + options.optimizeStubs = false; + options.optimizeObjC = true; + options.codeSigningDigestMode = DyldSharedCache::SHA256only; + options.dylibsRemovedDuringMastering = dylibsRemoved; + options.inodesAreSameAsRuntime = true; + options.cacheSupportsASLR = (fileSet.archName != "i386"); + options.forSimulator = false; + options.verbose = verbose; + options.evictLeafDylibsOnOverflow = true; + options.pathPrefixes = pathPrefixes; + DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables); + + // print any warnings + for (const std::string& warn : results.warnings) { + fprintf(stderr, "update_dyld_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str()); + } + if ( !results.errorMessage.empty() ) { + // print error (if one) + fprintf(stderr, "update_dyld_shared_cache: %s\n", results.errorMessage.c_str()); + cacheBuildFailure = true; + } + else { + // save new cache file to disk and write new .map file + assert(results.cacheContent != nullptr); + if ( !safeSave(results.cacheContent, results.cacheLength, outFile) ) + cacheBuildFailure = true; + if ( !cacheBuildFailure ) { + std::string mapStr = results.cacheContent->mapFile(); + std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map"; + safeSave(mapStr.c_str(), mapStr.size(), outFileMap); + wroteSomeCacheFile = true; + } + // free created cache buffer + vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength); + } + }); + + + // Save off spintrace data + if ( wroteSomeCacheFile ) { + void* h = dlopen("/usr/lib/libdscsym.dylib", 0); + if ( h != nullptr ) { + typedef int (*dscym_func)(const char*); + dscym_func func = (dscym_func)dlsym(h, "dscsym_save_dscsyms_for_current_caches"); + std::string nuggetRoot = rootPath; + if ( nuggetRoot.empty() ) + nuggetRoot = overlayPath; + if ( nuggetRoot.empty() ) + nuggetRoot = "/"; + (*func)(nuggetRoot.c_str()); + } + } + + + // we could unmap all input files, but tool is about to quit + + return (cacheBuildFailure ? 1 : 0); +} + diff --git a/dyld/dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist b/dyld/dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist new file mode 100644 index 0000000..5ed9d1c --- /dev/null +++ b/dyld/dyld3/shared-cache/update_dyld_shared_cache_entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.rootless.storage.dyld + + + diff --git a/dyld/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp b/dyld/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp new file mode 100644 index 0000000..404bdc4 --- /dev/null +++ b/dyld/dyld3/shared-cache/update_dyld_sim_shared_cache.cpp @@ -0,0 +1,541 @@ +/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- + * + * Copyright (c) 2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "MachOParser.h" +#include "FileUtils.h" +#include "StringUtils.h" +#include "DyldSharedCache.h" + + + +struct MappedMachOsByCategory +{ + std::string archName; + std::vector dylibsForCache; + std::vector otherDylibsAndBundles; + std::vector mainExecutables; +}; + +static const char* sSearchDirs[] = { + "/bin", + "/sbin", + "/usr", + "/System", +}; + +static const char* sSkipDirs[] = { + "/usr/share", + "/usr/local/include", +}; + + +static const char* sMacOsAdditions[] = { + "/usr/lib/system/libsystem_kernel.dylib", + "/usr/lib/system/libsystem_platform.dylib", + "/usr/lib/system/libsystem_pthread.dylib", +}; + + +static bool verbose = false; + +static bool addIfMachO(const std::string& simRuntimeRootPath, const std::string& runtimePath, const struct stat& statBuf, dyld3::Platform platform, std::vector& files) +{ + // read start of file to determine if it is mach-o or a fat file + std::string fullPath = simRuntimeRootPath + runtimePath; + int fd = ::open(fullPath.c_str(), O_RDONLY); + if ( fd < 0 ) + return false; + bool result = false; + const void* wholeFile = ::mmap(NULL, statBuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if ( wholeFile != MAP_FAILED ) { + Diagnostics diag; + bool usedWholeFile = false; + for (MappedMachOsByCategory& file : files) { + size_t sliceOffset; + size_t sliceLength; + bool fatButMissingSlice; + const void* slice = MAP_FAILED; + if ( dyld3::FatUtil::isFatFileWithSlice(diag, wholeFile, statBuf.st_size, file.archName, sliceOffset, sliceLength, fatButMissingSlice) ) { + slice = ::mmap(NULL, sliceLength, PROT_READ, MAP_PRIVATE, fd, sliceOffset); + if ( slice != MAP_FAILED ) { + //fprintf(stderr, "mapped slice at %p size=0x%0lX, offset=0x%0lX for %s\n", p, len, offset, fullPath.c_str()); + if ( !dyld3::MachOParser::isValidMachO(diag, file.archName, platform, slice, sliceLength, fullPath.c_str(), false) ) { + ::munmap((void*)slice, sliceLength); + slice = MAP_FAILED; + } + } + } + else if ( !fatButMissingSlice && dyld3::MachOParser::isValidMachO(diag, file.archName, platform, wholeFile, statBuf.st_size, fullPath.c_str(), false) ) { + slice = wholeFile; + sliceLength = statBuf.st_size; + sliceOffset = 0; + usedWholeFile = true; + //fprintf(stderr, "mapped whole file at %p size=0x%0lX for %s\n", p, len, inputPath.c_str()); + } + if ( slice != MAP_FAILED ) { + const mach_header* mh = (mach_header*)slice; + dyld3::MachOParser parser(mh); + if ( parser.platform() != platform ) { + fprintf(stderr, "skipped wrong platform binary: %s\n", fullPath.c_str()); + result = false; + } + else { + bool sip = true; // assume anything found in the simulator runtime is a platform binary + if ( parser.isDynamicExecutable() ) { + bool issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID)); + file.mainExecutables.emplace_back(runtimePath, mh, sliceLength, issetuid, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + else { + if ( parser.canBePlacedInDyldCache(runtimePath) ) { + file.dylibsForCache.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + else { + file.otherDylibsAndBundles.emplace_back(runtimePath, mh, sliceLength, false, sip, sliceOffset, statBuf.st_mtime, statBuf.st_ino); + } + } + result = true; + } + } + } + if ( !usedWholeFile ) + ::munmap((void*)wholeFile, statBuf.st_size); + } + ::close(fd); + return result; +} + +static void findAllFiles(const std::string& simRuntimeRootPath, dyld3::Platform platform, std::vector& files) +{ + std::unordered_set skipDirs; + for (const char* s : sSkipDirs) + skipDirs.insert(s); + + for (const char* searchDir : sSearchDirs ) { + iterateDirectoryTree(simRuntimeRootPath, searchDir, ^(const std::string& dirPath) { return (skipDirs.count(dirPath) != 0); }, ^(const std::string& path, const struct stat& statBuf) { + // ignore files that don't have 'x' bit set (all runnable mach-o files do) + const bool hasXBit = ((statBuf.st_mode & S_IXOTH) == S_IXOTH); + if ( !hasXBit && !endsWith(path, ".dylib") ) + return; + + // ignore files too small + if ( statBuf.st_size < 0x3000 ) + return; + + // if the file is mach-o add to list + addIfMachO(simRuntimeRootPath, path, statBuf, platform, files); + }); + } +} + +static void addMacOSAdditions(std::vector& allFileSets) +{ + for (const char* addPath : sMacOsAdditions) { + struct stat statBuf; + if ( stat(addPath, &statBuf) == 0 ) + addIfMachO("", addPath, statBuf, dyld3::Platform::macOS, allFileSets); + } +} + + +static bool dontCache(const std::string& simRuntimeRootPath, const std::string& archName, + const std::unordered_set& pathsWithDuplicateInstallName, + const DyldSharedCache::MappedMachO& aFile, bool warn) +{ + if ( startsWith(aFile.runtimePath, "/usr/lib/system/introspection/") ) + return true; + if ( startsWith(aFile.runtimePath, "/usr/local/") ) + return true; + + // anything inside a .app bundle is specific to app, so should be in shared cache + if ( aFile.runtimePath.find(".app/") != std::string::npos ) + return true; + + if ( aFile.runtimePath.find("//") != std::string::npos ) { + if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s double-slash in install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); + } + + dyld3::MachOParser parser(aFile.mh); + const char* installName = parser.installName(); + if ( (pathsWithDuplicateInstallName.count(aFile.runtimePath) != 0) && (aFile.runtimePath != installName) ) { + if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of duplicate install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); + return true; + } + + if ( aFile.runtimePath != installName ) { + // see if install name is a symlink to actual path + std::string fullInstall = simRuntimeRootPath + installName; + char resolvedPath[PATH_MAX]; + if ( realpath(fullInstall.c_str(), resolvedPath) != NULL ) { + std::string resolvedSymlink = resolvedPath; + if ( !simRuntimeRootPath.empty() ) { + resolvedSymlink = resolvedSymlink.substr(simRuntimeRootPath.size()); + } + if ( aFile.runtimePath == resolvedSymlink ) { + return false; + } + } + if (warn) fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s skipping because of bad install name %s\n", archName.c_str(), aFile.runtimePath.c_str()); + return true; + } + + return false; +} + +static void pruneCachedDylibs(const std::string& simRuntimeRootPath, MappedMachOsByCategory& fileSet) +{ + std::unordered_set pathsWithDuplicateInstallName; + + std::unordered_map installNameToFirstPath; + for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { + //fprintf(stderr, "dylib: %s\n", aFile.runtimePath.c_str()); + dyld3::MachOParser parser(aFile.mh); + const char* installName = parser.installName(); + auto pos = installNameToFirstPath.find(installName); + if ( pos == installNameToFirstPath.end() ) { + installNameToFirstPath[installName] = aFile.runtimePath; + } + else { + pathsWithDuplicateInstallName.insert(aFile.runtimePath); + pathsWithDuplicateInstallName.insert(installNameToFirstPath[installName]); + } + } + + for (DyldSharedCache::MappedMachO& aFile : fileSet.dylibsForCache) { + if ( dontCache(simRuntimeRootPath, fileSet.archName, pathsWithDuplicateInstallName, aFile, true) ) + fileSet.otherDylibsAndBundles.push_back(aFile); + } + fileSet.dylibsForCache.erase(std::remove_if(fileSet.dylibsForCache.begin(), fileSet.dylibsForCache.end(), + [&](const DyldSharedCache::MappedMachO& aFile) { return dontCache(simRuntimeRootPath, fileSet.archName, pathsWithDuplicateInstallName, aFile, false); }), + fileSet.dylibsForCache.end()); +} + +static bool existingCacheUpToDate(const std::string& existingCache, const std::vector& currentDylibs) +{ + // if no existing cache, it is not up-to-date + int fd = ::open(existingCache.c_str(), O_RDONLY); + if ( fd < 0 ) + return false; + + // build map of found dylibs + std::unordered_map currentDylibMap; + for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) { + //fprintf(stderr, "0x%0llX 0x%0llX %s\n", aFile.inode, aFile.modTime, aFile.runtimePath.c_str()); + currentDylibMap[aFile.runtimePath] = &aFile; + } + + // make sure all dylibs in existing cache have same mtime and inode as found dylib + __block bool foundMismatch = false; + const uint64_t cacheMapLen = 0x40000000; + void *p = ::mmap(NULL, cacheMapLen, PROT_READ, MAP_PRIVATE, fd, 0); + if ( p != MAP_FAILED ) { + const DyldSharedCache* cache = (DyldSharedCache*)p; + cache->forEachImageEntry(^(const char* installName, uint64_t mTime, uint64_t inode) { + bool foundMatch = false; + auto pos = currentDylibMap.find(installName); + if ( pos != currentDylibMap.end() ) { + const DyldSharedCache::MappedMachO* foundDylib = pos->second; + if ( (foundDylib->inode == inode) && (foundDylib->modTime == mTime) ) { + foundMatch = true; + } + } + if ( !foundMatch ) { + // use slow path and look for any dylib with a matching inode and mtime + bool foundSlow = false; + for (const DyldSharedCache::MappedMachO& aFile : currentDylibs) { + if ( (aFile.inode == inode) && (aFile.modTime == mTime) ) { + foundSlow = true; + break; + } + } + if ( !foundSlow ) { + foundMismatch = true; + if ( verbose ) + fprintf(stderr, "rebuilding dyld cache because dylib changed: %s\n", installName); + } + } + }); + ::munmap(p, cacheMapLen); + } + + ::close(fd); + + return !foundMismatch; +} + + +inline uint32_t absolutetime_to_milliseconds(uint64_t abstime) +{ + return (uint32_t)(abstime/1000/1000); +} + + +#define TERMINATE_IF_LAST_ARG( s ) \ + do { \ + if ( i == argc - 1 ) { \ + fprintf(stderr, s ); \ + return 1; \ + } \ + } while ( 0 ) + +int main(int argc, const char* argv[]) +{ + std::string rootPath; + std::string dylibListFile; + bool force = false; + std::string cacheDir; + std::unordered_set archStrs; + + dyld3::Platform platform = dyld3::Platform::iOS; + + // parse command line options + for (int i = 1; i < argc; ++i) { + const char* arg = argv[i]; + if (strcmp(arg, "-debug") == 0) { + verbose = true; + } + else if (strcmp(arg, "-verbose") == 0) { + verbose = true; + } + else if (strcmp(arg, "-tvOS") == 0) { + platform = dyld3::Platform::tvOS; + } + else if (strcmp(arg, "-iOS") == 0) { + platform = dyld3::Platform::iOS; + } + else if (strcmp(arg, "-watchOS") == 0) { + platform = dyld3::Platform::watchOS; + } + else if ( strcmp(arg, "-runtime_dir") == 0 ) { + TERMINATE_IF_LAST_ARG("-runtime_dir missing path argument\n"); + rootPath = argv[++i]; + } + else if (strcmp(arg, "-cache_dir") == 0) { + TERMINATE_IF_LAST_ARG("-cache_dir missing path argument\n"); + cacheDir = argv[++i]; + } + else if (strcmp(arg, "-arch") == 0) { + TERMINATE_IF_LAST_ARG("-arch missing argument\n"); + archStrs.insert(argv[++i]); + } + else if (strcmp(arg, "-force") == 0) { + force = true; + } + else { + //usage(); + fprintf(stderr, "update_dyld_sim_shared_cache: unknown option: %s\n", arg); + return 1; + } + } + + if ( cacheDir.empty() ) { + fprintf(stderr, "missing -cache_dir option to specify directory in which to write cache file(s)\n"); + return 1; + } + + if ( rootPath.empty() ) { + fprintf(stderr, "missing -runtime_dir option to specify directory which is root of simulator runtime)\n"); + return 1; + } + else { + // canonicalize rootPath + char resolvedPath[PATH_MAX]; + if ( realpath(rootPath.c_str(), resolvedPath) != NULL ) { + rootPath = resolvedPath; + } + } + + int err = mkpath_np(cacheDir.c_str(), S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + if ( (err != 0) && (err != EEXIST) ) { + fprintf(stderr, "mkpath_np fail: %d", err); + return 1; + } + + if ( archStrs.empty() ) { + switch ( platform ) { + case dyld3::Platform::iOS: + archStrs.insert("x86_64"); + break; + case dyld3::Platform::tvOS: + archStrs.insert("x86_64"); + break; + case dyld3::Platform::watchOS: + archStrs.insert("i386"); + break; + case dyld3::Platform::unknown: + case dyld3::Platform::macOS: + assert(0 && "macOS does not have a simulator"); + break; + case dyld3::Platform::bridgeOS: + assert(0 && "bridgeOS does not have a simulator"); + break; + } + } + + uint64_t t1 = mach_absolute_time(); + + // find all mach-o files for requested architectures + __block std::vector allFileSets; + if ( archStrs.count("x86_64") ) + allFileSets.push_back({"x86_64"}); + if ( archStrs.count("i386") ) + allFileSets.push_back({"i386"}); + findAllFiles(rootPath, platform, allFileSets); + addMacOSAdditions(allFileSets); + for (MappedMachOsByCategory& fileSet : allFileSets) { + pruneCachedDylibs(rootPath, fileSet); + } + + uint64_t t2 = mach_absolute_time(); + + fprintf(stderr, "time to scan file system and construct lists of mach-o files: %ums\n", absolutetime_to_milliseconds(t2-t1)); + + // build all caches in parallel + __block bool cacheBuildFailure = false; + dispatch_apply(allFileSets.size(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) { + MappedMachOsByCategory& fileSet = allFileSets[index]; + const std::string outFile = cacheDir + "/dyld_shared_cache_" + fileSet.archName; + __block std::unordered_set knownMissingDylib; + + DyldSharedCache::MappedMachO (^loader)(const std::string&) = ^DyldSharedCache::MappedMachO(const std::string& runtimePath) { + std::string fullPath = rootPath + runtimePath; + struct stat statBuf; + if ( stat(fullPath.c_str(), &statBuf) == 0 ) { + std::vector mappedFiles; + mappedFiles.push_back({fileSet.archName}); + if ( addIfMachO(rootPath, runtimePath, statBuf, platform, mappedFiles) ) { + if ( !mappedFiles.back().dylibsForCache.empty() ) + return mappedFiles.back().dylibsForCache.back(); + } + } + if ( knownMissingDylib.count(runtimePath) == 0 ) { + fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s could not use in dylid cache: %s\n", fileSet.archName.c_str(), runtimePath.c_str()); + knownMissingDylib.insert(runtimePath); + } + return DyldSharedCache::MappedMachO(); + }; + size_t startCount = fileSet.dylibsForCache.size(); + std::vector>> excludes; + DyldSharedCache::verifySelfContained(fileSet.dylibsForCache, loader, excludes); + for (size_t i=startCount; i < fileSet.dylibsForCache.size(); ++i) { + fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s not found in initial scan, but adding required dylib %s\n", fileSet.archName.c_str(), fileSet.dylibsForCache[i].runtimePath.c_str()); + } + for (auto& exclude : excludes) { + std::string reasons = "(\""; + for (auto i = exclude.second.begin(); i != exclude.second.end(); ++i) { + reasons += *i; + if (i != --exclude.second.end()) { + reasons += "\", \""; + } + } + reasons += "\")"; + fprintf(stderr, "update_dyld_shared_cache: warning: %s rejected from cached dylibs: %s (%s)\n", fileSet.archName.c_str(), exclude.first.runtimePath.c_str(), reasons.c_str()); + fileSet.otherDylibsAndBundles.push_back(exclude.first); + } + + // check if cache is already up to date + if ( !force ) { + if ( existingCacheUpToDate(outFile, fileSet.dylibsForCache) ) + return; + } + fprintf(stderr, "make %s cache with %lu dylibs, %lu other dylibs, %lu programs\n", fileSet.archName.c_str(), fileSet.dylibsForCache.size(), fileSet.otherDylibsAndBundles.size(), fileSet.mainExecutables.size()); + + // build cache new cache file + DyldSharedCache::CreateOptions options; + options.archName = fileSet.archName; + options.platform = platform; + options.excludeLocalSymbols = false; + options.optimizeStubs = false; + options.optimizeObjC = true; + options.codeSigningDigestMode = DyldSharedCache::SHA256only; + options.dylibsRemovedDuringMastering = false; + options.inodesAreSameAsRuntime = true; + options.cacheSupportsASLR = false; + options.forSimulator = true; + options.verbose = verbose; + options.evictLeafDylibsOnOverflow = true; + options.pathPrefixes = { rootPath }; + DyldSharedCache::CreateResults results = DyldSharedCache::create(options, fileSet.dylibsForCache, fileSet.otherDylibsAndBundles, fileSet.mainExecutables); + + // print any warnings + for (const std::string& warn : results.warnings) { + fprintf(stderr, "update_dyld_sim_shared_cache: warning: %s %s\n", fileSet.archName.c_str(), warn.c_str()); + } + if ( !results.errorMessage.empty() ) { + // print error (if one) + fprintf(stderr, "update_dyld_sim_shared_cache: %s\n", results.errorMessage.c_str()); + cacheBuildFailure = true; + } + else { + // save new cache file to disk and write new .map file + assert(results.cacheContent != nullptr); + if ( !safeSave(results.cacheContent, results.cacheLength, outFile) ) + cacheBuildFailure = true; + if ( !cacheBuildFailure ) { + std::string mapStr = results.cacheContent->mapFile(); + std::string outFileMap = cacheDir + "/dyld_shared_cache_" + fileSet.archName + ".map"; + safeSave(mapStr.c_str(), mapStr.size(), outFileMap); + } + // free created cache buffer + vm_deallocate(mach_task_self(), (vm_address_t)results.cacheContent, results.cacheLength); + } + }); + + // we could unmap all input files, but tool is about to quit + + return (cacheBuildFailure ? 1 : 0); +} + diff --git a/dyld/dyld_sim-entitlements.plist b/dyld/dyld_sim-entitlements.plist new file mode 100644 index 0000000..0509fb2 --- /dev/null +++ b/dyld/dyld_sim-entitlements.plist @@ -0,0 +1,8 @@ + + + + + com.apple.private.dyld_sim + + + diff --git a/dyld/include/dlfcn.h b/dyld/include/dlfcn.h new file mode 100644 index 0000000..bcb0c09 --- /dev/null +++ b/dyld/include/dlfcn.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + Based on the dlcompat work done by: + Jorge Acereda & + Peter O'Gorman +*/ + +#ifndef _DLFCN_H_ +#define _DLFCN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +#include +#include +/* + * Structure filled in by dladdr(). + */ +typedef struct dl_info { + const char *dli_fname; /* Pathname of shared object */ + void *dli_fbase; /* Base address of shared object */ + const char *dli_sname; /* Name of nearest symbol */ + void *dli_saddr; /* Address of nearest symbol */ +} Dl_info; + +extern int dladdr(const void *, Dl_info *); +#endif /* not POSIX */ + +extern int dlclose(void * __handle); +extern char * dlerror(void); +extern void * dlopen(const char * __path, int __mode); +extern void * dlsym(void * __handle, const char * __symbol); + +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +extern bool dlopen_preflight(const char* __path) __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); +#endif /* not POSIX */ + + +#define RTLD_LAZY 0x1 +#define RTLD_NOW 0x2 +#define RTLD_LOCAL 0x4 +#define RTLD_GLOBAL 0x8 + +#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE) +#define RTLD_NOLOAD 0x10 +#define RTLD_NODELETE 0x80 +#define RTLD_FIRST 0x100 /* Mac OS X 10.5 and later */ + +/* + * Special handle arguments for dlsym(). + */ +#define RTLD_NEXT ((void *) -1) /* Search subsequent objects. */ +#define RTLD_DEFAULT ((void *) -2) /* Use default search algorithm. */ +#define RTLD_SELF ((void *) -3) /* Search this and subsequent objects (Mac OS X 10.5 and later) */ +#define RTLD_MAIN_ONLY ((void *) -5) /* Search main executable only (Mac OS X 10.5 and later) */ +#endif /* not POSIX */ + +#ifdef __cplusplus +} +#endif + +#endif /* _DLFCN_H_ */ diff --git a/dyld/include/mach-o/dyld-interposing.h b/dyld/include/mach-o/dyld-interposing.h new file mode 100644 index 0000000..1621a52 --- /dev/null +++ b/dyld/include/mach-o/dyld-interposing.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#if !defined(_DYLD_INTERPOSING_H_) +#define _DYLD_INTERPOSING_H_ + +/* + * Example: + * + * static + * int + * my_open(const char* path, int flags, mode_t mode) + * { + * int value; + * // do stuff before open (including changing the arguments) + * value = open(path, flags, mode); + * // do stuff after open (including changing the return value(s)) + * return value; + * } + * DYLD_INTERPOSE(my_open, open) + */ + +#define DYLD_INTERPOSE(_replacement,_replacee) \ + __attribute__((used)) static struct{ const void* replacement; const void* replacee; } _interpose_##_replacee \ + __attribute__ ((section ("__DATA,__interpose"))) = { (const void*)(unsigned long)&_replacement, (const void*)(unsigned long)&_replacee }; + +#endif diff --git a/dyld/include/mach-o/dyld.h b/dyld/include/mach-o/dyld.h new file mode 100644 index 0000000..770abd4 --- /dev/null +++ b/dyld/include/mach-o/dyld.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 1999-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _MACH_O_DYLD_H_ +#define _MACH_O_DYLD_H_ + + +#include +#include +#include + +#include +#include + +#if __cplusplus +extern "C" { +#endif + +/* + * The following functions allow you to iterate through all loaded images. + * This is not a thread safe operation. Another thread can add or remove + * an image during the iteration. + * + * Many uses of these routines can be replace by a call to dladdr() which + * will return the mach_header and name of an image, given an address in + * the image. dladdr() is thread safe. + */ +extern uint32_t _dyld_image_count(void) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern const struct mach_header* _dyld_get_image_header(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern const char* _dyld_get_image_name(uint32_t image_index) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * The following functions allow you to install callbacks which will be called + * by dyld whenever an image is loaded or unloaded. During a call to _dyld_register_func_for_add_image() + * the callback func is called for every existing image. Later, it is called as each new image + * is loaded and bound (but initializers not yet run). The callback registered with + * _dyld_register_func_for_remove_image() is called after any terminators in an image are run + * and before the image is un-memory-mapped. + */ +extern void _dyld_register_func_for_add_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); +extern void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header* mh, intptr_t vmaddr_slide)) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * NSVersionOfRunTimeLibrary() returns the current_version number of the currently dylib + * specifed by the libraryName. The libraryName parameter would be "bar" for /path/libbar.3.dylib and + * "Foo" for /path/Foo.framework/Versions/A/Foo. It returns -1 if no such library is loaded. + */ +extern int32_t NSVersionOfRunTimeLibrary(const char* libraryName) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * NSVersionOfLinkTimeLibrary() returns the current_version number that the main executable was linked + * against at build time. The libraryName parameter would be "bar" for /path/libbar.3.dylib and + * "Foo" for /path/Foo.framework/Versions/A/Foo. It returns -1 if the main executable did not link + * against the specified library. + */ +extern int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) __OSX_AVAILABLE_STARTING(__MAC_10_1, __IPHONE_2_0); + + +/* + * _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, + * 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 + * needed could be more than MAXPATHLEN. + */ +extern int _NSGetExecutablePath(char* buf, uint32_t* bufsize) __OSX_AVAILABLE_STARTING(__MAC_10_2, __IPHONE_2_0); + + + +/* + * Registers a function to be called when the current thread terminates. + * Called by c++ compiler to implement destructors on thread_local object variables. + */ +extern void _tlv_atexit(void (*termFunc)(void* objAddr), void* objAddr) __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0); + + +/* + * Never called. On-disk thread local variables contain a pointer to this. Once + * the thread local is prepared, the pointer changes to a real handler such as tlv_get_addr. + */ +extern void _tlv_bootstrap() __OSX_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0); + + +// FIXME: remove when Availability.h updated +#ifndef __BRIDGEOS_UNAVAILABLE + #ifdef __ENVIRONMENT_BRIDGE_OS_VERSION_MIN_REQUIRED__ + #define __BRIDGEOS_UNAVAILABLE __OS_AVAILABILITY(bridgeos,unavailable) + #else + #define __BRIDGEOS_UNAVAILABLE + #endif +#endif + +/* + * The following dyld API's are deprecated as of Mac OS X 10.5. They are either + * no longer necessary or are superceeded by dlopen and friends in . + * dlopen/dlsym/dlclose have been available since Mac OS X 10.3 and work with + * dylibs and bundles. + * + * NSAddImage -> dlopen + * NSLookupSymbolInImage -> dlsym + * NSCreateObjectFileImageFromFile -> dlopen + * NSDestroyObjectFileImage -> dlclose + * NSLinkModule -> not needed when dlopen used + * NSUnLinkModule -> not needed when dlclose used + * NSLookupSymbolInModule -> dlsym + * _dyld_image_containing_address -> dladdr + * NSLinkEditError -> dlerror + * + */ + +#ifndef ENUM_DYLD_BOOL +#define ENUM_DYLD_BOOL + #undef FALSE + #undef TRUE + enum DYLD_BOOL { FALSE, TRUE }; +#endif /* ENUM_DYLD_BOOL */ + + +/* Object file image API */ +typedef enum { + NSObjectFileImageFailure, /* for this a message is printed on stderr */ + NSObjectFileImageSuccess, + NSObjectFileImageInappropriateFile, + NSObjectFileImageArch, + NSObjectFileImageFormat, /* for this a message is printed on stderr */ + NSObjectFileImageAccess +} NSObjectFileImageReturnCode; + +typedef struct __NSObjectFileImage* NSObjectFileImage; + + + +/* NSObjectFileImage can only be used with MH_BUNDLE files */ +extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()"); +extern NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void *address, size_t size, NSObjectFileImage *objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlclose()"); + +extern uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern const char* NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, bool *tentative_definition) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, const char* segmentName, const char* sectionName, size_t *size) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "getsectiondata()"); + +typedef struct __NSModule* NSModule; +extern const char* NSNameOfModule(NSModule m) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern const char* NSLibraryNameForModule(NSModule m) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); + +extern NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()"); +#define NSLINKMODULE_OPTION_NONE 0x0 +#define NSLINKMODULE_OPTION_BINDNOW 0x1 +#define NSLINKMODULE_OPTION_PRIVATE 0x2 +#define NSLINKMODULE_OPTION_RETURN_ON_ERROR 0x4 +#define NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES 0x8 +#define NSLINKMODULE_OPTION_TRAILING_PHYS_NAME 0x10 + +extern bool NSUnLinkModule(NSModule module, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +#define NSUNLINKMODULE_OPTION_NONE 0x0 +#define NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED 0x1 +#define NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES 0x2 + +/* symbol API */ +typedef struct __NSSymbol* NSSymbol; +extern bool NSIsSymbolNameDefined(const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern bool NSIsSymbolNameDefinedInImage(const struct mach_header* image, const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern NSSymbol NSLookupAndBindSymbol(const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); +extern NSSymbol NSLookupSymbolInImage(const struct mach_header* image, const char* symbolName, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); +#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND 0x0 +#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW 0x1 +#define NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY 0x2 +#define NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR 0x4 +extern const char* NSNameOfSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); +extern void * NSAddressOfSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); +extern NSModule NSModuleForSymbol(NSSymbol symbol) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dladdr()"); + +/* error handling API */ +typedef enum { + NSLinkEditFileAccessError, + NSLinkEditFileFormatError, + NSLinkEditMachResourceError, + NSLinkEditUnixResourceError, + NSLinkEditOtherError, + NSLinkEditWarningError, + NSLinkEditMultiplyDefinedError, + NSLinkEditUndefinedError +} NSLinkEditErrors; + +/* + * For the NSLinkEditErrors value NSLinkEditOtherError these are the values + * passed to the link edit error handler as the errorNumber (what would be an + * errno value for NSLinkEditUnixResourceError or a kern_return_t value for + * NSLinkEditMachResourceError). + */ +typedef enum { + NSOtherErrorRelocation, + NSOtherErrorLazyBind, + NSOtherErrorIndrLoop, + NSOtherErrorLazyInit, + NSOtherErrorInvalidArgs +} NSOtherErrorNumbers; + +extern void NSLinkEditError(NSLinkEditErrors *c, int *errorNumber, const char** fileName, const char** errorString) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlerror()"); + +typedef struct { + void (*undefined)(const char* symbolName); + NSModule (*multiple)(NSSymbol s, NSModule oldModule, NSModule newModule); + void (*linkEdit)(NSLinkEditErrors errorClass, int errorNumber, + const char* fileName, const char* errorString); +} NSLinkEditErrorHandlers; + +extern void NSInstallLinkEditErrorHandlers(const NSLinkEditErrorHandlers *handlers) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, ""); + +extern bool NSAddLibrary(const char* pathName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()"); +extern bool NSAddLibraryWithSearching(const char* pathName) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlopen()"); +extern const struct mach_header* NSAddImage(const char* image_name, uint32_t options) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen()"); +#define NSADDIMAGE_OPTION_NONE 0x0 +#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 +#define NSADDIMAGE_OPTION_WITH_SEARCHING 0x2 +#define NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED 0x4 +#define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 + +extern bool _dyld_present(void) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "always true"); +extern bool _dyld_launched_prebound(void) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "moot"); +extern bool _dyld_all_twolevel_modules_prebound(void) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "moot"); +extern bool _dyld_bind_fully_image_containing_address(const void* address) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlopen(RTLD_NOW)"); +extern bool _dyld_image_containing_address(const void* address) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "dladdr()"); +extern void _dyld_lookup_and_bind(const char* symbol_name, void **address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern void _dyld_lookup_and_bind_with_hint(const char* symbol_name, const char* library_name_hint, void** address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.4, "dlsym()"); +extern void _dyld_lookup_and_bind_fully(const char* symbol_name, void** address, NSModule* module) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.1, 10.5, "dlsym()"); + +extern const struct mach_header* _dyld_get_image_header_containing_address(const void* address) __IOS_UNAVAILABLE __BRIDGEOS_UNAVAILABLE __OSX_DEPRECATED(10.3, 10.5, "dladdr()"); + + +#if __cplusplus +} +#endif + +#endif /* _MACH_O_DYLD_H_ */ diff --git a/dyld/include/mach-o/dyld_gdb.h b/dyld/include/mach-o/dyld_gdb.h new file mode 100644 index 0000000..f249515 --- /dev/null +++ b/dyld/include/mach-o/dyld_gdb.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _DYLD_GDB_ +#define _DYLD_GDB_ + + +#endif /* _DYLD_GDB_ */ diff --git a/dyld/include/mach-o/dyld_images.h b/dyld/include/mach-o/dyld_images.h new file mode 100644 index 0000000..4b2da8f --- /dev/null +++ b/dyld/include/mach-o/dyld_images.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2006-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_IMAGES_ +#define _DYLD_IMAGES_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* + * Beginning in Mac OS X 10.4, this is how gdb discovers which mach-o images are loaded in a process. + * + * gdb looks for the symbol "_dyld_all_image_infos" in dyld. It contains the fields below. + * + * For a snashot of what images are currently loaded, the infoArray fields contain a pointer + * to an array of all images. If infoArray is NULL, it means it is being modified, come back later. + * + * To be notified of changes, gdb sets a break point on the address pointed to by the notificationn + * field. The function it points to is called by dyld with an array of information about what images + * have been added (dyld_image_adding) or are about to be removed (dyld_image_removing). + * + * The notification is called after infoArray is updated. This means that if gdb attaches to a process + * and infoArray is NULL, gdb can set a break point on notification and let the proccess continue to + * run until the break point. Then gdb can inspect the full infoArray. + * + * The dyldVersion field always points to a C string that contains the dyld version. For instance, + * in dyld-127.3, dyldVersion would contain a pointer to "127.3". + * + * The errorMessage and terminationFlags fields are normally zero. If dyld terminates a process + * (for instance because a required dylib or symbol is missing), then the errorMessage field will + * be set to point to a C string message buffer containing the reason dyld terminate the process. + * The low bit of the terminationFlags will be set if dyld terminated the process before any user + * code ran, in which case there is no need for the crash log to contain the backtrace. + * + * 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, dyld_image_info_change=2 }; + +struct dyld_image_info { + const struct mach_header* imageLoadAddress; /* base address image is mapped into */ + const char* imageFilePath; /* path dyld used to load the image */ + uintptr_t imageFileModDate; /* time_t of image file */ + /* if stat().st_mtime of imageFilePath does not match imageFileModDate, */ + /* then file has been modified since dyld loaded it */ +}; + +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 + }; + +/* internal limit */ +#define DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT 8 + +struct dyld_all_image_infos { + uint32_t version; /* 1 in Mac OS X 10.4 and 10.5 */ + uint32_t infoArrayCount; + const struct dyld_image_info* infoArray; + dyld_image_notifier notification; + bool processDetachedFromSharedRegion; + /* the following fields are only in version 2 (Mac OS X 10.6, iPhoneOS 2.0) and later */ + bool libSystemInitialized; + const struct mach_header* dyldImageLoadAddress; + /* the following field is only in version 3 (Mac OS X 10.6, iPhoneOS 3.0) and later */ + void* jitInfo; + /* 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, iPhoneOS 3.1) and later */ + void* coreSymbolicationShmPage; + /* 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; + /* the following field is only in version 13 (Mac OS X 10.9, iOS 7.0) and later */ + uint8_t sharedCacheUUID[16]; + /* the following field is only in version 15 (macOS 10.12, iOS 10.0) and later */ + uintptr_t sharedCacheBaseAddress; + uint64_t infoArrayChangeTimestamp; + const char* dyldPath; + mach_port_t notifyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; +#if __LP64__ + uintptr_t reserved[13-(DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT/2)]; +#else + uintptr_t reserved[13-DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; +#endif + /* the following field is only in version 16 (macOS 10.13, iOS 11.0) and later */ + uintptr_t compact_dyld_image_info_addr; + size_t compact_dyld_image_info_size; +}; + +/* + * Beginning in Mac OS X 10.5, this is how gdb discovers where the shared cache is in a process. + * Images that are in the shared cache have their segments rearranged, so when using imageFilePath + * to load the file from disk, you have to know to adjust addresses based on how their segment + * was rearranged. + * + * gdb looks for the symbol "_dyld_shared_region_ranges" in dyld. + * + * It contains information the count of shared regions used by the process. The count is + * the number of start/length pairs. + */ +struct dyld_shared_cache_ranges { + uintptr_t sharedRegionsCount; /* how many ranges follow */ + struct { + uintptr_t start; + uintptr_t length; + } ranges[4]; /* max regions */ +}; +extern struct dyld_shared_cache_ranges dyld_shared_cache_ranges __attribute__((visibility("hidden"))); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _DYLD_IMAGES_ */ diff --git a/dyld/include/mach-o/dyld_priv.h b/dyld/include/mach-o/dyld_priv.h new file mode 100644 index 0000000..93f1b90 --- /dev/null +++ b/dyld/include/mach-o/dyld_priv.h @@ -0,0 +1,433 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2003-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 _MACH_O_DYLD_PRIV_H_ +#define _MACH_O_DYLD_PRIV_H_ + +#include +#include +#include +#include +#include + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +// +// private interface between libSystem.dylib and dyld +// +extern void _dyld_fork_child(); + + +// DEPRECATED +enum dyld_image_states +{ + dyld_image_state_mapped = 10, // No batch notification for this + dyld_image_state_dependents_mapped = 20, // Only batch notification for this + dyld_image_state_rebased = 30, + dyld_image_state_bound = 40, + dyld_image_state_dependents_initialized = 45, // Only single notification for this + dyld_image_state_initialized = 50, + dyld_image_state_terminated = 60 // Only single notification for this +}; + +// DEPRECATED +typedef const char* (*dyld_image_state_change_handler)(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]); + + + +typedef void (*_dyld_objc_notify_mapped)(unsigned count, const char* const paths[], const struct mach_header* const mh[]); +typedef void (*_dyld_objc_notify_init)(const char* path, const struct mach_header* mh); +typedef void (*_dyld_objc_notify_unmapped)(const char* path, const struct mach_header* mh); + + +// +// Note: only for use by objc runtime +// Register handlers to be called when objc images are mapped, unmapped, and initialized. +// Dyld will call back the "mapped" function with an array of images that contain an objc-image-info section. +// Those images that are dylibs will have the ref-counts automatically bumped, so objc will no longer need to +// call dlopen() on them to keep them from being unloaded. During the call to _dyld_objc_notify_register(), +// dyld will call the "mapped" function with already loaded objc images. During any later dlopen() call, +// dyld will also call the "mapped" function. Dyld will call the "init" function when dyld would be called +// initializers in that image. This is when objc calls any +load methods in that image. +// +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped); + + + +// +// 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 +// +extern intptr_t _dyld_get_image_slide(const struct mach_header* mh); + + + +struct dyld_unwind_sections +{ + const struct mach_header* mh; + const void* dwarf_section; + uintptr_t dwarf_section_length; + const void* compact_unwind_section; + uintptr_t compact_unwind_section_length; +}; + + +// +// Returns true iff some loaded mach-o image contains "addr". +// info->mh mach header of image containing addr +// info->dwarf_section pointer to start of __TEXT/__eh_frame section +// info->dwarf_section_length length of __TEXT/__eh_frame section +// info->compact_unwind_section pointer to start of __TEXT/__unwind_info section +// info->compact_unwind_section_length length of __TEXT/__unwind_info section +// +// Exists in Mac OS X 10.6 and later +#if !__USING_SJLJ_EXCEPTIONS__ +extern bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info); +#endif + + +// +// This is an optimized form of dladdr() that only returns the dli_fname field. +// +// Exists in Mac OS X 10.6 and later +extern const char* dyld_image_path_containing_address(const void* addr); + + +// +// This is an optimized form of dladdr() that only returns the dli_fbase field. +// Return NULL, if address is not in any image tracked by dyld. +// +// Exists in Mac OS X 10.11 and later +extern const struct mach_header* dyld_image_header_containing_address(const void* addr); + + + +// Convienence constants for return values from dyld_get_sdk_version() and friends. + +//@MAC_VERSION_DEFS@ + +//@IOS_VERSION_DEFS@ + +//@WATCHOS_VERSION_DEFS@ + + +// +// This finds the SDK version a binary was built against. +// Returns zero on error, or if SDK version could not be determined. +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_sdk_version(const struct mach_header* mh); + + +// +// This finds the SDK version that the main executable was built against. +// Returns zero on error, or if SDK version could not be determined. +// +// Note on watchOS, this returns the equivalent iOS SDK version number +// (i.e an app built against watchOS 2.0 SDK returne 9.0). To see the +// platform specific sdk version use dyld_get_program_sdk_watch_os_version(). +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_program_sdk_version(); + + +#if __WATCH_OS_VERSION_MIN_REQUIRED +// watchOS only. +// This finds the Watch OS SDK version that the main executable was built against. +// Exists in Watch OS 2.0 and later +extern uint32_t dyld_get_program_sdk_watch_os_version() __IOS_UNAVAILABLE __OSX_UNAVAILABLE __WATCHOS_AVAILABLE(2.0); + + +// watchOS only. +// This finds the Watch min OS version that the main executable was built to run on. +// Note: dyld_get_program_min_os_version() returns the iOS equivalent (e.g. 9.0) +// whereas this returns the raw watchOS version (e.g. 2.0). +// Exists in Watch OS 3.0 and later +extern uint32_t dyld_get_program_min_watch_os_version(); // __WATCHOS_AVAILABLE(3.0); +#endif + + +#if TARGET_OS_BRIDGE +// bridgeOS only. +// This finds the bridgeOS SDK version that the main executable was built against. +// Exists in bridgeOSOS 2.0 and later +extern uint32_t dyld_get_program_sdk_bridge_os_version(); + +// bridgeOS only. +// This finds the Watch min OS version that the main executable was built to run on. +// Note: dyld_get_program_min_os_version() returns the iOS equivalent (e.g. 9.0) +// whereas this returns the raw bridgeOS version (e.g. 2.0). +// Exists in bridgeOS 2.0 and later +extern uint32_t dyld_get_program_min_bridge_os_version(); +#endif + +// +// This finds the min OS version a binary was built to run on. +// Returns zero on error, or if no min OS recorded in binary. +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_min_os_version(const struct mach_header* mh); + + +// +// This finds the min OS version the main executable was built to run on. +// Returns zero on error, or if no min OS recorded in binary. +// +// Exists in Mac OS X 10.8 and later +// Exists in iOS 6.0 and later +extern uint32_t dyld_get_program_min_os_version(); + + + + +// +// Returns if any OS dylib has overridden its copy in the shared cache +// +// Exists in iPhoneOS 3.1 and later +// Exists in Mac OS X 10.10 and later +extern bool dyld_shared_cache_some_image_overridden(); + + + +// +// Returns if the process is setuid or is code signed with entitlements. +// +// Exists in Mac OS X 10.9 and later +extern bool dyld_process_is_restricted(); + + + +// +// Returns path used by dyld for standard dyld shared cache file for the current arch. +// +// Exists in Mac OS X 10.11 and later +extern const char* dyld_shared_cache_file_path(); + + + +// +// for OpenGL to tell dyld it is ok to deallocate a memory based image when done. +// +// Exists in Mac OS X 10.9 and later +#define NSLINKMODULE_OPTION_CAN_UNLOAD 0x20 + + +// +// Update all bindings on specified image. +// Looks for uses of 'replacement' and changes it to 'replacee'. +// NOTE: this is less safe than using static interposing via DYLD_INSERT_LIBRARIES +// because the running program may have already copy the pointer values to other +// locations that dyld does not know about. +// +struct dyld_interpose_tuple { + const void* replacement; + const void* replacee; +}; +extern void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count); + + +struct dyld_shared_cache_dylib_text_info { + uint64_t version; // current version 1 + // following fields all exist in version 1 + uint64_t loadAddressUnslid; + uint64_t textSegmentSize; + uuid_t dylibUuid; + const char* path; // pointer invalid at end of iterations + // following fields all exist in version 2 + uint64_t textSegmentOffset; // offset from start of cache +}; +typedef struct dyld_shared_cache_dylib_text_info dyld_shared_cache_dylib_text_info; + + +#ifdef __BLOCKS__ +// +// Given the UUID of a dyld shared cache file, this function will attempt to locate the cache +// file and if found iterate all images, returning info about each one. Returns 0 on success. +// +// Exists in Mac OS X 10.11 and later +// iOS 9.0 and later +extern int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)); + + +// +// Given the UUID of a dyld shared cache file, and a NULL terminated array of extra directory paths to search, +// this function will scan the standard and extra directories looking for a cache file that matches the UUID +// and if found iterate all images, returning info about each one. Returns 0 on success. +// +// Exists in Mac OS X 10.12 and later +// iOS 10.0 and later +extern int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)); +#endif /* __BLOCKS */ + + +// +// Returns if the specified address range is in a dyld owned memory +// that is mapped read-only and will never be unloaded. +// +// Exists in Mac OS X 10.12 and later +// iOS 10.0 and later +extern bool _dyld_is_memory_immutable(const void* addr, size_t length); + + +// +// Finds the UUID (from LC_UUID load command) of given image. +// Returns false if LC_UUID is missing or mach_header is malformed. +// +// Exists in Mac OS X 10.12 and later +// Exists in iOS 10.0 and later +extern bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid); + + +// +// Gets the UUID of the dyld shared cache in the current process. +// Returns false if there is no dyld shared cache in use by the processes. +// +// Exists in Mac OS X 10.12 and later +// Exists in iOS 10.0 and later +extern bool _dyld_get_shared_cache_uuid(uuid_t uuid); + + +// +// Returns the start address of the dyld cache in the process and sets length to the size of the cache. +// Returns NULL if the process is not using a dyld shared cache +// +// Exists in Mac OS X 10.13 and later +// Exists in iOS 11.0 and later +extern const void* _dyld_get_shared_cache_range(size_t* length); + + + +// +// When dyld must terminate a process because of a required dependent dylib +// could not be loaded or a symbol is missing, dyld calls abort_with_reason() +// using one of the following error codes. +// +#define DYLD_EXIT_REASON_DYLIB_MISSING 1 +#define DYLD_EXIT_REASON_DYLIB_WRONG_ARCH 2 +#define DYLD_EXIT_REASON_DYLIB_WRONG_VERSION 3 +#define DYLD_EXIT_REASON_SYMBOL_MISSING 4 +#define DYLD_EXIT_REASON_CODE_SIGNATURE 5 +#define DYLD_EXIT_REASON_FILE_SYSTEM_SANDBOX 6 +#define DYLD_EXIT_REASON_MALFORMED_MACHO 7 +#define DYLD_EXIT_REASON_OTHER 9 + +// +// When it has more information about the termination, dyld will use abort_with_payload(). +// The payload is a dyld_abort_payload structure. The fixed fields are offsets into the +// payload for the corresponding string. If the offset is zero, that string is not available. +// +struct dyld_abort_payload { + uint32_t version; // first version is 1 + uint32_t flags; // 0x00000001 means dyld terminated at launch, backtrace not useful + uint32_t targetDylibPathOffset; // offset in payload of path string to dylib that could not be loaded + uint32_t clientPathOffset; // offset in payload of path string to image requesting dylib + uint32_t symbolOffset; // offset in payload of symbol string that could not be found + // string data +}; +typedef struct dyld_abort_payload dyld_abort_payload; + + +// These global variables are implemented in libdyld.dylib +// Old programs that used crt1.o also defined these globals. +// The ones in dyld are not used when an old program is run. +extern int NXArgc; +extern const char** NXArgv; +extern char** environ; // POSIX says this not const, because it pre-dates const +extern const char* __progname; + + +// called by libSystem_initializer only +extern void _dyld_initializer(); + +// never called from source code. Used by static linker to implement lazy binding +extern void dyld_stub_binder() __asm__("dyld_stub_binder"); + + +// called by exit() before it calls cxa_finalize() so that thread_local +// objects are destroyed before global objects. +extern void _tlv_exit(); + + +// temp exports to keep tapi happy, until ASan stops using dyldVersionNumber +extern double dyldVersionNumber; +extern const char* dyldVersionString; + +#if __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _MACH_O_DYLD_PRIV_H_ */ diff --git a/dyld/include/mach-o/dyld_process_info.h b/dyld/include/mach-o/dyld_process_info.h new file mode 100644 index 0000000..7db413f --- /dev/null +++ b/dyld/include/mach-o/dyld_process_info.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2016 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_PROCESS_INFO_ +#define _DYLD_PROCESS_INFO_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Beginning in iOS 10.0 and Mac OS X 10.12, this is how lldb figures out mach-o binaries are in a process: +// +// When attaching to an existing process, lldb uses _dyld_process_info_create() to get the current list of images +// in a process, then falls into the start up case. +// +// When starting a process, lldb starts the process suspended, finds the "_dyld_debugger_notification" symbol in +// dyld, sets a break point on it, then resumes the process. Dyld will call _dyld_debugger_notification() with +// a list of images that were just added or removed from the process. Dyld calls this function before running +// any initializers in the image, so the debugger will have a chance to set break points in the image. +// +// + +enum dyld_notify_mode { dyld_notify_adding=0, dyld_notify_removing=1, dyld_notify_remove_all=2 }; +// void _dyld_debugger_notification(enum dyld_notify_mode, unsigned long count, uint64_t machHeaders[]); + + + +struct dyld_process_cache_info { + uuid_t cacheUUID; // UUID of cache used by process + uint64_t cacheBaseAddress; // load address of dyld shared cache + bool noCache; // process is running without a dyld cache + bool privateCache; // process is using a private copy of its dyld cache +}; +typedef struct dyld_process_cache_info dyld_process_cache_info; + +enum { + dyld_process_state_not_started = 0x00, // process is suspended, dyld has not started running yet + dyld_process_state_dyld_initialized = 0x10, // dyld has initialzed itself + dyld_process_state_terminated_before_inits = 0x20, // process was terminated due missing library or symbol before it got to main() + dyld_process_state_libSystem_initialized = 0x30, // dyld has run libSystem's initializer + dyld_process_state_running_initializers = 0x40, // dyld is running other initializers + dyld_process_state_program_running = 0x50, // dyld has finished and jumped into main() + dyld_process_state_dyld_terminated = 0x60 // process was terminated by dyld post-main (e.g. bad lazying binding info) +}; + +struct dyld_process_state_info { + uint64_t timestamp; // mach_absolute_time of last time dyld change to image list + uint32_t imageCount; // number of images currently loaded into process + uint32_t initialImageCount; // number of images statically loaded into process (before any dlopen() calls) + uint8_t dyldState; // one of dyld_process_state_* values +}; +typedef struct dyld_process_state_info dyld_process_state_info; + + +typedef const struct dyld_process_info_base* dyld_process_info; + +// +// Generate a dyld_process_info object for specified task. +// +// The timestamp parameter is an optimization to not spend the time to gather all the image information +// if the process image list has not changed since the last call. If timestamp is zero, this function +// always gathers the full process info. If timestamp is non-zero, this function will check if the target +// task's image list has changed since that time. If is has not changed, the function returns NULL and +// kern_return_t is KERN_SUCCESS. If it has changed, the function gathers the full image info. +// The kernelError parameter can be NULL for clients that don't care why it failed. +// +extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError); + +// retain/release dyld_process_info for specified task +extern void _dyld_process_info_release(dyld_process_info info); +extern void _dyld_process_info_retain(dyld_process_info info); + +// fill in struct with basic info about dyld in the process +extern void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo); + +// fill in struct with info about dyld cache in use by process +extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); + +// iterate all images in process +extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); + +// iterate all segments in an image +extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)); + + + + +typedef const struct dyld_process_info_notify_base* dyld_process_info_notify; + +// +// Request notifications if image list changes in target process. Each time a load or unload happens in the target taks, +// the notify block will be called in this process. If the process exits, the notifyExit block will be called. +// If the notifications cannot be set up, this function will return NULL, and the reason in the kernError parameter. +// The kernelError parameter can be NULL for clients that don't care why it failed. +// If you want to stop receiving notifications, call _dyld_process_info_notify_release(). +// +extern dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue, + void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path), + void (^notifyExit)(), + kern_return_t* kernelError); +// add block to call right before main() is entered. +// does nothing if process is already in main(). +extern void _dyld_process_info_notify_main(dyld_process_info_notify objc, void (^notifyMain)()); + + +// stop notifications and invalid dyld_process_info_notify object +extern void _dyld_process_info_notify_release(dyld_process_info_notify object); +extern void _dyld_process_info_notify_retain(dyld_process_info_notify object); + + +#ifdef __cplusplus +} +#endif + +#endif /* _DYLD_PROCESS_INFO_ */ diff --git a/dyld/include/objc-shared-cache.h b/dyld/include/objc-shared-cache.h new file mode 100644 index 0000000..6f29230 --- /dev/null +++ b/dyld/include/objc-shared-cache.h @@ -0,0 +1,1483 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* +Portions derived from: + +-------------------------------------------------------------------- +lookup8.c, by Bob Jenkins, January 4 1997, Public Domain. +hash(), hash2(), hash3, and mix() are externally useful functions. +Routines to test the hash are included if SELF_TEST is defined. +You can use this free for any purpose. It has no warranty. +-------------------------------------------------------------------- + +------------------------------------------------------------------------------ +perfect.c: code to generate code for a hash for perfect hashing. +(c) Bob Jenkins, September 1996, December 1999 +You may use this code in any way you wish, and it is free. No warranty. +I hereby place this in the public domain. +Source is http://burtleburtle.net/bob/c/perfect.c +------------------------------------------------------------------------------ +*/ + +/* + * objc-selopt.h + * Interface between libobjc and dyld + * for selector uniquing in the dyld shared cache. + * + * When building the shared cache, dyld locates all selectors and selector + * references in the cached images. It builds a perfect hash table out of + * them and writes the table into the shared cache copy of libobjc. + * libobjc then uses that table as the builtin selector list. + * + * Versioning + * The table has a version number. dyld and objc can both ignore the table + * if the other used the wrong version number. + * + * Completeness + * Not all libraries are in the shared cache. Libraries that are in the + * shared cache and were optimized are specially marked. Libraries on + * disk never include those marks. + * + * Coherency + * Libraries optimized in the shared cache can be replaced by unoptimized + * copies from disk when loaded. The copy from disk is not marked and will + * be fixed up by libobjc. The shared cache copy is still mapped into the + * process, so the table can point to cstring data in that library's part + * of the shared cache without trouble. + * + * Atomicity + * dyld writes the table itself last. If dyld marks some metadata as + * updated but then fails to write a table for some reason, libobjc + * fixes up all metadata as if it were not marked. + */ + +#ifndef _OBJC_SELOPT_H +#define _OBJC_SELOPT_H + +/* + DO NOT INCLUDE ANY objc HEADERS HERE + dyld USES THIS FILE AND CANNOT SEE THEM +*/ +#include +#include +#ifdef SELOPT_WRITE +#include +#endif +/* + DO NOT INCLUDE ANY objc HEADERS HERE + dyld USES THIS FILE AND CANNOT SEE THEM +*/ + +#ifndef STATIC_ASSERT +# define STATIC_ASSERT(x) _STATIC_ASSERT2(x, __LINE__) +# define _STATIC_ASSERT2(x, line) _STATIC_ASSERT3(x, line) +# define _STATIC_ASSERT3(x, line) \ + typedef struct { \ + int _static_assert[(x) ? 0 : -1]; \ + } _static_assert_ ## line __attribute__((unavailable)) +#endif + +#define SELOPT_DEBUG 0 + +#define S32(x) x = little_endian ? OSSwapHostToLittleInt32(x) : OSSwapHostToBigInt32(x) +#define S64(x) x = little_endian ? OSSwapHostToLittleInt64(x) : OSSwapHostToBigInt64(x) + +namespace objc_opt { + +typedef int32_t objc_stringhash_offset_t; +typedef uint8_t objc_stringhash_check_t; + +static uint64_t lookup8( uint8_t *k, size_t length, uint64_t level); + +#ifdef SELOPT_WRITE + +// Perfect hash code is at the end of this file. + +struct __attribute__((packed)) perfect_hash { + uint32_t capacity; + uint32_t occupied; + uint32_t shift; + uint32_t mask; + uint64_t salt; + + uint32_t scramble[256]; + uint8_t *tab; // count == mask+1; free with delete[] + + perfect_hash() : tab(0) { } + + ~perfect_hash() { if (tab) delete[] tab; } +}; + +struct eqstr { + bool operator()(const char* s1, const char* s2) const { + return strcmp(s1, s2) == 0; + } +}; + +struct hashstr { + size_t operator()(const char *s) const { + return (size_t)lookup8((uint8_t *)s, strlen(s), 0); + } +}; + +// cstring => cstring's vmaddress +// (used for selector names and class names) +typedef std::unordered_map string_map; + +// protocol name => protocol vmaddress +typedef std::unordered_map protocol_map; + +// class name => (class vmaddress, header_info vmaddress) +typedef std::unordered_multimap, hashstr, eqstr> class_map; + +static perfect_hash make_perfect(const string_map& strings); + +#endif + + +// Precomputed perfect hash table of strings. +// Base class for precomputed selector table and class table. +// Edit objc-sel-table.s if you change this structure. +struct __attribute__((packed)) objc_stringhash_t { + uint32_t capacity; + uint32_t occupied; + uint32_t shift; + uint32_t mask; + uint32_t unused1; // was zero + uint32_t unused2; // alignment pad + uint64_t salt; + + uint32_t scramble[256]; + uint8_t tab[0]; /* tab[mask+1] (always power-of-2) */ + // uint8_t checkbytes[capacity]; /* check byte for each string */ + // int32_t offsets[capacity]; /* offsets from &capacity to cstrings */ + + objc_stringhash_check_t *checkbytes() { return (objc_stringhash_check_t *)&tab[mask+1]; } + const objc_stringhash_check_t *checkbytes() const { return (const objc_stringhash_check_t *)&tab[mask+1]; } + + objc_stringhash_offset_t *offsets() { return (objc_stringhash_offset_t *)&checkbytes()[capacity]; } + const objc_stringhash_offset_t *offsets() const { return (const objc_stringhash_offset_t *)&checkbytes()[capacity]; } + + uint32_t hash(const char *key, size_t keylen) const + { + uint64_t val = lookup8((uint8_t*)key, keylen, salt); + uint32_t index = (uint32_t)(val>>shift) ^ scramble[tab[val&mask]]; + return index; + } + + uint32_t hash(const char *key) const + { + return hash(key, strlen(key)); + } + + // The check bytes areused to reject strings that aren't in the table + // without paging in the table's cstring data. This checkbyte calculation + // catches 4785/4815 rejects when launching Safari; a perfect checkbyte + // would catch 4796/4815. + objc_stringhash_check_t checkbyte(const char *key, size_t keylen) const + { + return + ((key[0] & 0x7) << 5) + | + ((uint8_t)keylen & 0x1f); + } + + objc_stringhash_check_t checkbyte(const char *key) const + { + return checkbyte(key, strlen(key)); + } + + +#define INDEX_NOT_FOUND (~(uint32_t)0) + + uint32_t getIndex(const char *key) const + { + size_t keylen = strlen(key); + uint32_t h = hash(key, keylen); + + // Use check byte to reject without paging in the table's cstrings + objc_stringhash_check_t h_check = checkbytes()[h]; + objc_stringhash_check_t key_check = checkbyte(key, keylen); + bool check_fail = (h_check != key_check); +#if ! SELOPT_DEBUG + if (check_fail) return INDEX_NOT_FOUND; +#endif + + objc_stringhash_offset_t offset = offsets()[h]; + if (offset == 0) return INDEX_NOT_FOUND; + const char *result = (const char *)this + offset; + if (0 != strcmp(key, result)) return INDEX_NOT_FOUND; + +#if SELOPT_DEBUG + if (check_fail) abort(); +#endif + + return h; + } + +#ifdef SELOPT_WRITE + + size_t size() + { + return sizeof(objc_stringhash_t) + + mask+1 + + capacity * sizeof(objc_stringhash_check_t) + + capacity * sizeof(objc_stringhash_offset_t); + } + + void byteswap(bool little_endian) + { + // tab and checkbytes are arrays of bytes, no swap needed + for (uint32_t i = 0; i < 256; i++) { + S32(scramble[i]); + } + objc_stringhash_offset_t *o = offsets(); + for (uint32_t i = 0; i < capacity; i++) { + S32(o[i]); + } + + S32(capacity); + S32(occupied); + S32(shift); + S32(mask); + S64(salt); + } + + const char *write(uint64_t base, size_t remaining, string_map& strings) + { + if (sizeof(objc_stringhash_t) > remaining) { + return "selector section too small (metadata not optimized)"; + } + + if (strings.size() == 0) { + bzero(this, sizeof(objc_stringhash_t)); + return NULL; + } + + perfect_hash phash = make_perfect(strings); + if (phash.capacity == 0) { + return "perfect hash failed (metadata not optimized)"; + } + + // Set header + capacity = phash.capacity; + occupied = phash.occupied; + shift = phash.shift; + mask = phash.mask; + unused1 = 0; + unused2 = 0; + salt = phash.salt; + + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // Set hash data + for (uint32_t i = 0; i < 256; i++) { + scramble[i] = phash.scramble[i]; + } + for (uint32_t i = 0; i < phash.mask+1; i++) { + tab[i] = phash.tab[i]; + } + + // Set offsets to 0 + for (uint32_t i = 0; i < phash.capacity; i++) { + offsets()[i] = 0; + } + // Set checkbytes to 0 + for (uint32_t i = 0; i < phash.capacity; i++) { + checkbytes()[i] = 0; + } + + // Set real string offsets and checkbytes +# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t)) + string_map::const_iterator s; + for (s = strings.begin(); s != strings.end(); ++s) { + int64_t offset = s->second - base; + if ((offset<>SHIFT != offset) { + return "selector offset too big (metadata not optimized)"; + } + + uint32_t h = hash(s->first); + offsets()[h] = (objc_stringhash_offset_t)offset; + checkbytes()[h] = checkbyte(s->first); + } +# undef SHIFT + + return NULL; + } + +// SELOPT_WRITE +#endif +}; + + +// Precomputed selector table. +// Edit objc-sel-table.s if you change this structure. +struct objc_selopt_t : objc_stringhash_t { + const char *get(const char *key) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) return NULL; + + return (const char *)this + offsets()[h]; + } +}; + +// Precomputed class list. +// Edit objc-sel-table.s if you change these structures. + +struct objc_classheader_t { + objc_stringhash_offset_t clsOffset; + objc_stringhash_offset_t hiOffset; + + // For duplicate class names: + // clsOffset = count<<1 | 1 + // duplicated classes are duplicateOffsets[hiOffset..hiOffset+count-1] + bool isDuplicate() const { return clsOffset & 1; } + uint32_t duplicateCount() const { return clsOffset >> 1; } + uint32_t duplicateIndex() const { return hiOffset; } +}; + + +struct objc_clsopt_t : objc_stringhash_t { + // ...objc_stringhash_t fields... + // objc_classheader_t classOffsets[capacity]; /* offsets from &capacity to class_t and header_info */ + // uint32_t duplicateCount; + // objc_classheader_t duplicateOffsets[duplicatedClasses]; + + objc_classheader_t *classOffsets() { return (objc_classheader_t *)&offsets()[capacity]; } + const objc_classheader_t *classOffsets() const { return (const objc_classheader_t *)&offsets()[capacity]; } + + uint32_t& duplicateCount() { return *(uint32_t *)&classOffsets()[capacity]; } + const uint32_t& duplicateCount() const { return *(const uint32_t *)&classOffsets()[capacity]; } + + objc_classheader_t *duplicateOffsets() { return (objc_classheader_t *)(&duplicateCount()+1); } + const objc_classheader_t *duplicateOffsets() const { return (const objc_classheader_t *)(&duplicateCount()+1); } + + // 0/NULL/NULL: not found + // 1/ptr/ptr: found exactly one + // n/NULL/NULL: found N - use getClassesAndHeaders() instead + uint32_t getClassAndHeader(const char *key, void*& cls, void*& hi) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) { + cls = NULL; + hi = NULL; + return 0; + } + + const objc_classheader_t& clshi = classOffsets()[h]; + if (! clshi.isDuplicate()) { + // class appears in exactly one header + cls = (void *)((const char *)this + clshi.clsOffset); + hi = (void *)((const char *)this + clshi.hiOffset); + return 1; + } + else { + // class appears in more than one header - use getClassesAndHeaders + cls = NULL; + hi = NULL; + return clshi.duplicateCount(); + } + } + + void getClassesAndHeaders(const char *key, void **cls, void **hi) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) return; + + const objc_classheader_t& clshi = classOffsets()[h]; + if (! clshi.isDuplicate()) { + // class appears in exactly one header + cls[0] = (void *)((const char *)this + clshi.clsOffset); + hi[0] = (void *)((const char *)this + clshi.hiOffset); + } + else { + // class appears in more than one header + uint32_t count = clshi.duplicateCount(); + const objc_classheader_t *list = + &duplicateOffsets()[clshi.duplicateIndex()]; + for (uint32_t i = 0; i < count; i++) { + cls[i] = (void *)((const char *)this + list[i].clsOffset); + hi[i] = (void *)((const char *)this + list[i].hiOffset); + } + } + } + +#ifdef SELOPT_WRITE + + size_t size() + { + return + objc_stringhash_t::size() + + capacity * sizeof(objc_classheader_t) + + sizeof(duplicateCount()) + + duplicateCount() * sizeof(objc_classheader_t); + } + + void byteswap(bool little_endian) + { + objc_classheader_t *o; + + o = classOffsets(); + for (uint32_t i = 0; i < capacity; i++) { + S32(o[i].clsOffset); + S32(o[i].hiOffset); + } + + o = duplicateOffsets(); + for (uint32_t i = 0; i < duplicateCount(); i++) { + S32(o[i].clsOffset); + S32(o[i].hiOffset); + } + + S32(duplicateCount()); + + objc_stringhash_t::byteswap(little_endian); + } + + const char *write(uint64_t base, size_t remaining, + string_map& strings, class_map& classes, bool verbose) + { + const char *err; + err = objc_stringhash_t::write(base, remaining, strings); + if (err) return err; + + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // Set class offsets to 0 + for (uint32_t i = 0; i < capacity; i++) { + classOffsets()[i].clsOffset = 0; + classOffsets()[i].hiOffset = 0; + } + + // Set real class offsets +# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t)) + class_map::const_iterator c; + for (c = classes.begin(); c != classes.end(); ++c) { + uint32_t h = getIndex(c->first); + if (h == INDEX_NOT_FOUND) { + return "class list busted (metadata not optimized)"; + } + + if (classOffsets()[h].clsOffset != 0) { + // already did this class + continue; + } + + uint32_t count = (uint32_t)classes.count(c->first); + if (count == 1) { + // only one class with this name + + int64_t coff = c->second.first - base; + int64_t hoff = c->second.second - base; + if ((coff<>SHIFT != coff) { + return "class offset too big (metadata not optimized)"; + } + if ((hoff<>SHIFT != hoff) { + return "header offset too big (metadata not optimized)"; + } + + classOffsets()[h].clsOffset = (objc_stringhash_offset_t)coff; + classOffsets()[h].hiOffset = (objc_stringhash_offset_t)hoff; + } + else { + // class name has duplicates - write them all now + if (verbose) { + fprintf(stderr, "update_dyld_shared_cache: %u duplicates of Objective-C class %s\n", count, c->first); + } + + uint32_t dest = duplicateCount(); + duplicateCount() += count; + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // classOffsets() instead contains count and array index + classOffsets()[h].clsOffset = count*2 + 1; + classOffsets()[h].hiOffset = dest; + + std::pair + duplicates = classes.equal_range(c->first); + class_map::const_iterator dup; + for (dup = duplicates.first; dup != duplicates.second; ++dup) { + int64_t coff = dup->second.first - base; + int64_t hoff = dup->second.second - base; + if ((coff<>SHIFT != coff) { + return "class offset too big (metadata not optimized)"; + } + if ((hoff<>SHIFT != hoff) { + return "header offset too big (metadata not optimized)"; + } + + duplicateOffsets()[dest].clsOffset = (objc_stringhash_offset_t)coff; + duplicateOffsets()[dest].hiOffset = (objc_stringhash_offset_t)hoff; + dest++; + } + } + } +# undef SHIFT + + return NULL; + } + +// SELOPT_WRITE +#endif +}; + + + +struct objc_protocolopt_t : objc_stringhash_t { + // ...objc_stringhash_t fields... + // uint32_t protocolOffsets[capacity]; /* offsets from &capacity to protocol_t */ + + objc_stringhash_offset_t *protocolOffsets() { return (objc_stringhash_offset_t *)&offsets()[capacity]; } + const objc_stringhash_offset_t *protocolOffsets() const { return (const objc_stringhash_offset_t *)&offsets()[capacity]; } + + void* getProtocol(const char *key) const + { + uint32_t h = getIndex(key); + if (h == INDEX_NOT_FOUND) { + return NULL; + } + + return (void *)((const char *)this + protocolOffsets()[h]); + } + +#ifdef SELOPT_WRITE + + size_t size() + { + return + objc_stringhash_t::size() + capacity * sizeof(objc_stringhash_offset_t); + } + + void byteswap(bool little_endian) + { + objc_stringhash_offset_t *o; + + o = protocolOffsets(); + for (objc_stringhash_offset_t i = 0; i < capacity; i++) { + S32(o[i]); + } + + objc_stringhash_t::byteswap(little_endian); + } + + const char *write(uint64_t base, size_t remaining, + string_map& strings, protocol_map& protocols, + bool verbose) + { + const char *err; + err = objc_stringhash_t::write(base, remaining, strings); + if (err) return err; + + if (size() > remaining) { + return "selector section too small (metadata not optimized)"; + } + + // Set protocol offsets to 0 + for (uint32_t i = 0; i < capacity; i++) { + protocolOffsets()[i] = 0; + } + + // Set real protocol offsets +# define SHIFT (64 - 8*sizeof(objc_stringhash_offset_t)) + protocol_map::const_iterator c; + for (c = protocols.begin(); c != protocols.end(); ++c) { + uint32_t h = getIndex(c->first); + if (h == INDEX_NOT_FOUND) { + return "protocol list busted (metadata not optimized)"; + } + + int64_t offset = c->second - base; + if ((offset<>SHIFT != offset) { + return "protocol offset too big (metadata not optimized)"; + } + + protocolOffsets()[h] = (objc_stringhash_offset_t)offset; + } +# undef SHIFT + + return NULL; + } + +// SELOPT_WRITE +#endif +}; + + +// Precomputed image list. +struct objc_headeropt_ro_t; + +// Precomputed image list. +struct objc_headeropt_rw_t; + +// Precomputed class list. +struct objc_clsopt_t; + +// Edit objc-sel-table.s if you change this value. +// lldb and Symbolication read these structures. Inform them of any changes. +enum { VERSION = 15 }; + +// Values for objc_opt_t::flags +enum : uint32_t { + IsProduction = (1 << 0), // never set in development cache + NoMissingWeakSuperclasses = (1 << 1), // never set in development cache +}; + +// Top-level optimization structure. +// Edit objc-sel-table.s if you change this structure. +struct alignas(alignof(void*)) objc_opt_t { + uint32_t version; + uint32_t flags; + int32_t selopt_offset; + int32_t headeropt_ro_offset; + int32_t clsopt_offset; + int32_t protocolopt_offset; + int32_t headeropt_rw_offset; + + const objc_selopt_t* selopt() const { + if (selopt_offset == 0) return NULL; + return (objc_selopt_t *)((uint8_t *)this + selopt_offset); + } + objc_selopt_t* selopt() { + if (selopt_offset == 0) return NULL; + return (objc_selopt_t *)((uint8_t *)this + selopt_offset); + } + + struct objc_headeropt_ro_t* headeropt_ro() const { + if (headeropt_ro_offset == 0) return NULL; + return (struct objc_headeropt_ro_t *)((uint8_t *)this + headeropt_ro_offset); + } + + struct objc_clsopt_t* clsopt() const { + if (clsopt_offset == 0) return NULL; + return (objc_clsopt_t *)((uint8_t *)this + clsopt_offset); + } + + struct objc_protocolopt_t* protocolopt() const { + if (protocolopt_offset == 0) return NULL; + return (objc_protocolopt_t *)((uint8_t *)this + protocolopt_offset); + } + + struct objc_headeropt_rw_t* headeropt_rw() const { + if (headeropt_rw_offset == 0) return NULL; + return (struct objc_headeropt_rw_t *)((uint8_t *)this + headeropt_rw_offset); + } +}; + +// sizeof(objc_opt_t) must be pointer-aligned +STATIC_ASSERT(sizeof(objc_opt_t) % sizeof(void*) == 0); + + +// List of offsets in libobjc that the shared cache optimization needs to use. +template +struct objc_opt_pointerlist_tt { + T protocolClass; +}; +typedef struct objc_opt_pointerlist_tt objc_opt_pointerlist_t; + + +/* +-------------------------------------------------------------------- +mix -- mix 3 64-bit values reversibly. +mix() takes 48 machine instructions, but only 24 cycles on a superscalar + machine (like Intel's new MMX architecture). It requires 4 64-bit + registers for 4::2 parallelism. +All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of + (a,b,c), and all deltas of bottom bits were tested. All deltas were + tested both on random keys and on keys that were nearly all zero. + These deltas all cause every bit of c to change between 1/3 and 2/3 + of the time (well, only 113/400 to 287/400 of the time for some + 2-bit delta). These deltas all cause at least 80 bits to change + among (a,b,c) when the mix is run either forward or backward (yes it + is reversible). +This implies that a hash using mix64 has no funnels. There may be + characteristics with 3-bit deltas or bigger, I didn't test for + those. +-------------------------------------------------------------------- +*/ +#define mix64(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>43); \ + b -= c; b -= a; b ^= (a<<9); \ + c -= a; c -= b; c ^= (b>>8); \ + a -= b; a -= c; a ^= (c>>38); \ + b -= c; b -= a; b ^= (a<<23); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>35); \ + b -= c; b -= a; b ^= (a<<49); \ + c -= a; c -= b; c ^= (b>>11); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<18); \ + c -= a; c -= b; c ^= (b>>22); \ +} + +/* +-------------------------------------------------------------------- +hash() -- hash a variable-length key into a 64-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 8-byte value +Returns a 64-bit value. Every bit of the key affects every bit of +the return value. No funnels. Every 1-bit and 2-bit delta achieves +avalanche. About 41+5len instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 64 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i= 24) + { + a += (k[0] +((uint64_t)k[ 1]<< 8)+((uint64_t)k[ 2]<<16)+((uint64_t)k[ 3]<<24) + +((uint64_t)k[4 ]<<32)+((uint64_t)k[ 5]<<40)+((uint64_t)k[ 6]<<48)+((uint64_t)k[ 7]<<56)); + b += (k[8] +((uint64_t)k[ 9]<< 8)+((uint64_t)k[10]<<16)+((uint64_t)k[11]<<24) + +((uint64_t)k[12]<<32)+((uint64_t)k[13]<<40)+((uint64_t)k[14]<<48)+((uint64_t)k[15]<<56)); + c += (k[16] +((uint64_t)k[17]<< 8)+((uint64_t)k[18]<<16)+((uint64_t)k[19]<<24) + +((uint64_t)k[20]<<32)+((uint64_t)k[21]<<40)+((uint64_t)k[22]<<48)+((uint64_t)k[23]<<56)); + mix64(a,b,c); + k += 24; len -= 24; + } + + /*------------------------------------- handle the last 23 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 23: c+=((uint64_t)k[22]<<56); + case 22: c+=((uint64_t)k[21]<<48); + case 21: c+=((uint64_t)k[20]<<40); + case 20: c+=((uint64_t)k[19]<<32); + case 19: c+=((uint64_t)k[18]<<24); + case 18: c+=((uint64_t)k[17]<<16); + case 17: c+=((uint64_t)k[16]<<8); + /* the first byte of c is reserved for the length */ + case 16: b+=((uint64_t)k[15]<<56); + case 15: b+=((uint64_t)k[14]<<48); + case 14: b+=((uint64_t)k[13]<<40); + case 13: b+=((uint64_t)k[12]<<32); + case 12: b+=((uint64_t)k[11]<<24); + case 11: b+=((uint64_t)k[10]<<16); + case 10: b+=((uint64_t)k[ 9]<<8); + case 9: b+=((uint64_t)k[ 8]); + case 8: a+=((uint64_t)k[ 7]<<56); + case 7: a+=((uint64_t)k[ 6]<<48); + case 6: a+=((uint64_t)k[ 5]<<40); + case 5: a+=((uint64_t)k[ 4]<<32); + case 4: a+=((uint64_t)k[ 3]<<24); + case 3: a+=((uint64_t)k[ 2]<<16); + case 2: a+=((uint64_t)k[ 1]<<8); + case 1: a+=((uint64_t)k[ 0]); + /* case 0: nothing left to add */ + } + mix64(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + + +#ifdef SELOPT_WRITE + +/* +------------------------------------------------------------------------------ +This generates a minimal perfect hash function. That means, given a +set of n keys, this determines a hash function that maps each of +those keys into a value in 0..n-1 with no collisions. + +The perfect hash function first uses a normal hash function on the key +to determine (a,b) such that the pair (a,b) is distinct for all +keys, then it computes a^scramble[tab[b]] to get the final perfect hash. +tab[] is an array of 1-byte values and scramble[] is a 256-term array of +2-byte or 4-byte values. If there are n keys, the length of tab[] is a +power of two between n/3 and n. + +I found the idea of computing distinct (a,b) values in "Practical minimal +perfect hash functions for large databases", Fox, Heath, Chen, and Daoud, +Communications of the ACM, January 1992. They found the idea in Chichelli +(CACM Jan 1980). Beyond that, our methods differ. + +The key is hashed to a pair (a,b) where a in 0..*alen*-1 and b in +0..*blen*-1. A fast hash function determines both a and b +simultaneously. Any decent hash function is likely to produce +hashes so that (a,b) is distinct for all pairs. I try the hash +using different values of *salt* until all pairs are distinct. + +The final hash is (a XOR scramble[tab[b]]). *scramble* is a +predetermined mapping of 0..255 into 0..smax-1. *tab* is an +array that we fill in in such a way as to make the hash perfect. + +First we fill in all values of *tab* that are used by more than one +key. We try all possible values for each position until one works. + +This leaves m unmapped keys and m values that something could hash to. +If you treat unmapped keys as lefthand nodes and unused hash values +as righthand nodes, and draw a line connecting each key to each hash +value it could map to, you get a bipartite graph. We attempt to +find a perfect matching in this graph. If we succeed, we have +determined a perfect hash for the whole set of keys. + +*scramble* is used because (a^tab[i]) clusters keys around *a*. +------------------------------------------------------------------------------ +*/ + +typedef uint64_t ub8; +#define UB8MAXVAL 0xffffffffffffffffLL +#define UB8BITS 64 +typedef uint32_t ub4; +#define UB4MAXVAL 0xffffffff +#define UB4BITS 32 +typedef uint16_t ub2; +#define UB2MAXVAL 0xffff +#define UB2BITS 16 +typedef uint8_t ub1; +#define UB1MAXVAL 0xff +#define UB1BITS 8 + +#define TRUE 1 +#define FALSE 0 + +#define SCRAMBLE_LEN 256 // ((ub4)1<<16) /* length of *scramble* */ +#define RETRY_INITKEY 2048 /* number of times to try to find distinct (a,b) */ +#define RETRY_PERFECT 4 /* number of times to try to make a perfect hash */ + + +/* representation of a key */ +struct key +{ + ub1 *name_k; /* the actual key */ + ub4 len_k; /* the length of the actual key */ + ub4 hash_k; /* the initial hash value for this key */ +/* beyond this point is mapping-dependent */ + ub4 a_k; /* a, of the key maps to (a,b) */ + ub4 b_k; /* b, of the key maps to (a,b) */ + struct key *nextb_k; /* next key with this b */ +}; +typedef struct key key; + +/* things indexed by b of original (a,b) pair */ +struct bstuff +{ + ub2 val_b; /* hash=a^tabb[b].val_b */ + key *list_b; /* tabb[i].list_b is list of keys with b==i */ + ub4 listlen_b; /* length of list_b */ + ub4 water_b; /* high watermark of who has visited this map node */ +}; +typedef struct bstuff bstuff; + +/* things indexed by final hash value */ +struct hstuff +{ + key *key_h; /* tabh[i].key_h is the key with a hash of i */ +}; +typedef struct hstuff hstuff; + +/* things indexed by queue position */ +struct qstuff +{ + bstuff *b_q; /* b that currently occupies this hash */ + ub4 parent_q; /* queue position of parent that could use this hash */ + ub2 newval_q; /* what to change parent tab[b] to to use this hash */ + ub2 oldval_q; /* original value of tab[b] */ +}; +typedef struct qstuff qstuff; + + +/* +------------------------------------------------------------------------------ +Find the mapping that will produce a perfect hash +------------------------------------------------------------------------------ +*/ + +/* return the ceiling of the log (base 2) of val */ +static ub4 log2u(ub4 val) +{ + ub4 i; + for (i=0; ((ub4)1<>const3)); + x = (x+(x<>const5)); + } + return x; +} + +/* initialize scramble[] with distinct random values in 0..smax-1 */ +static void scrambleinit(ub4 *scramble, ub4 smax) +// ub4 *scramble; /* hash is a^scramble[tab[b]] */ +// ub4 smax; /* scramble values should be in 0..smax-1 */ +{ + ub4 i; + + /* fill scramble[] with distinct random integers in 0..smax-1 */ + for (i=0; ib_k + * check if the initial hash might work + */ +static int inittab(bstuff *tabb, ub4 blen, key *keys, ub4 nkeys, int complete) +// bstuff *tabb; /* output, list of keys with b for (a,b) */ +// ub4 blen; /* length of tabb */ +// key *keys; /* list of keys already hashed */ +// int complete; /* TRUE means to complete init despite collisions */ +{ + int nocollision = TRUE; + ub4 i; + + memset((void *)tabb, 0, (size_t)(sizeof(bstuff)*blen)); + + /* Two keys with the same (a,b) guarantees a collision */ + for (i = 0; i < nkeys; i++) { + key *mykey = keys+i; + key *otherkey; + + for (otherkey=tabb[mykey->b_k].list_b; + otherkey; + otherkey=otherkey->nextb_k) + { + if (mykey->a_k == otherkey->a_k) + { + nocollision = FALSE; + if (!complete) + return FALSE; + } + } + ++tabb[mykey->b_k].listlen_b; + mykey->nextb_k = tabb[mykey->b_k].list_b; + tabb[mykey->b_k].list_b = mykey; + } + + /* no two keys have the same (a,b) pair */ + return nocollision; +} + + +/* Do the initial hash for normal mode (use lookup and checksum) */ +static void initnorm(key *keys, ub4 nkeys, ub4 alen, ub4 blen, ub4 smax, ub8 salt) +// key *keys; /* list of all keys */ +// ub4 alen; /* (a,b) has a in 0..alen-1, a power of 2 */ +// ub4 blen; /* (a,b) has b in 0..blen-1, a power of 2 */ +// ub4 smax; /* maximum range of computable hash values */ +// ub4 salt; /* used to initialize the hash function */ +// gencode *final; /* output, code for the final hash */ +{ + ub4 loga = log2u(alen); /* log based 2 of blen */ + ub4 i; + for (i = 0; i < nkeys; i++) { + key *mykey = keys+i; + ub8 hash = lookup8(mykey->name_k, mykey->len_k, salt); + mykey->a_k = (loga > 0) ? (ub4)(hash >> (UB8BITS-loga)) : 0; + mykey->b_k = (blen > 1) ? (hash & (blen-1)) : 0; + } +} + + +/* Try to apply an augmenting list */ +static int apply(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 *scramble, ub4 tail, int rollback) +// bstuff *tabb; +// hstuff *tabh; +// qstuff *tabq; +// ub4 blen; +// ub4 *scramble; +// ub4 tail; +// int rollback; /* FALSE applies augmenting path, TRUE rolls back */ +{ + ub4 hash; + key *mykey; + bstuff *pb; + ub4 child; + ub4 parent; + ub4 stabb; /* scramble[tab[b]] */ + + /* walk from child to parent */ + for (child=tail-1; child; child=parent) + { + parent = tabq[child].parent_q; /* find child's parent */ + pb = tabq[parent].b_q; /* find parent's list of siblings */ + + /* erase old hash values */ + stabb = scramble[pb->val_b]; + for (mykey=pb->list_b; mykey; mykey=mykey->nextb_k) + { + hash = mykey->a_k^stabb; + if (mykey == tabh[hash].key_h) + { /* erase hash for all of child's siblings */ + tabh[hash].key_h = (key *)0; + } + } + + /* change pb->val_b, which will change the hashes of all parent siblings */ + pb->val_b = (rollback ? tabq[child].oldval_q : tabq[child].newval_q); + + /* set new hash values */ + stabb = scramble[pb->val_b]; + for (mykey=pb->list_b; mykey; mykey=mykey->nextb_k) + { + hash = mykey->a_k^stabb; + if (rollback) + { + if (parent == 0) continue; /* root never had a hash */ + } + else if (tabh[hash].key_h) + { + /* very rare: roll back any changes */ + apply(tabb, tabh, tabq, blen, scramble, tail, TRUE); + return FALSE; /* failure, collision */ + } + tabh[hash].key_h = mykey; + } + } + return TRUE; +} + + +/* +------------------------------------------------------------------------------- +augment(): Add item to the mapping. + +Construct a spanning tree of *b*s with *item* as root, where each +parent can have all its hashes changed (by some new val_b) with +at most one collision, and each child is the b of that collision. + +I got this from Tarjan's "Data Structures and Network Algorithms". The +path from *item* to a *b* that can be remapped with no collision is +an "augmenting path". Change values of tab[b] along the path so that +the unmapped key gets mapped and the unused hash value gets used. + +Assuming 1 key per b, if m out of n hash values are still unused, +you should expect the transitive closure to cover n/m nodes before +an unused node is found. Sum(i=1..n)(n/i) is about nlogn, so expect +this approach to take about nlogn time to map all single-key b's. +------------------------------------------------------------------------------- +*/ +static int augment(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 *scramble, ub4 smax, bstuff *item, ub4 nkeys, + ub4 highwater) +// bstuff *tabb; /* stuff indexed by b */ +// hstuff *tabh; /* which key is associated with which hash, indexed by hash */ +// qstuff *tabq; /* queue of *b* values, this is the spanning tree */ +// ub4 blen; /* length of tabb */ +// ub4 *scramble; /* final hash is a^scramble[tab[b]] */ +// ub4 smax; /* highest value in scramble */ +// bstuff *item; /* &tabb[b] for the b to be mapped */ +// ub4 nkeys; /* final hash must be in 0..nkeys-1 */ +// ub4 highwater; /* a value higher than any now in tabb[].water_b */ +{ + ub4 q; /* current position walking through the queue */ + ub4 tail; /* tail of the queue. 0 is the head of the queue. */ + ub4 limit=UB1MAXVAL+1; + ub4 highhash = smax; + + /* initialize the root of the spanning tree */ + tabq[0].b_q = item; + tail = 1; + + /* construct the spanning tree by walking the queue, add children to tail */ + for (q=0; qval_b */ + + if (q == 1) + break; /* don't do transitive closure */ + + for (i=0; ilist_b; mykey; mykey=mykey->nextb_k) + { + key *childkey; + ub4 hash = mykey->a_k^scramble[i]; + + if (hash >= highhash) break; /* out of bounds */ + childkey = tabh[hash].key_h; + + if (childkey) + { + bstuff *hitb = &tabb[childkey->b_k]; + + if (childb) + { + if (childb != hitb) break; /* hit at most one child b */ + } + else + { + childb = hitb; /* remember this as childb */ + if (childb->water_b == highwater) break; /* already explored */ + } + } + } + if (mykey) continue; /* myb with i has multiple collisions */ + + /* add childb to the queue of reachable things */ + if (childb) childb->water_b = highwater; + tabq[tail].b_q = childb; + tabq[tail].newval_q = i; /* how to make parent (myb) use this hash */ + tabq[tail].oldval_q = myb->val_b; /* need this for rollback */ + tabq[tail].parent_q = q; + ++tail; + + if (!childb) + { /* found an *i* with no collisions? */ + /* try to apply the augmenting path */ + if (apply(tabb, tabh, tabq, blen, scramble, tail, FALSE)) + return TRUE; /* success, item was added to the perfect hash */ + + --tail; /* don't know how to handle such a child! */ + } + } + } + return FALSE; +} + + +/* find a mapping that makes this a perfect hash */ +static int perfect(bstuff *tabb, hstuff *tabh, qstuff *tabq, ub4 blen, ub4 smax, ub4 *scramble, ub4 nkeys) +{ + ub4 maxkeys; /* maximum number of keys for any b */ + ub4 i, j; + +#if SELOPT_DEBUG + fprintf(stderr, " blen %d smax %d nkeys %d\n", blen, smax, nkeys); +#endif + + /* clear any state from previous attempts */ + memset((void *)tabh, 0, sizeof(hstuff)*smax); + memset((void *)tabq, 0, sizeof(qstuff)*(blen+1)); + + for (maxkeys=0,i=0; i maxkeys) + maxkeys = tabb[i].listlen_b; + + /* In descending order by number of keys, map all *b*s */ + for (j=maxkeys; j>0; --j) + for (i=0; i= RETRY_INITKEY) + { + /* Try to put more bits in (A,B) to make distinct (A,B) more likely */ + if (*alen < maxalen) + { + *alen *= 2; + } + else if (*blen < smax) + { + *blen *= 2; + delete[] tabq; + delete[] *tabb; + *tabb = new bstuff[*blen]; + tabq = new qstuff[*blen+1]; + } + bad_initkey = 0; + bad_perfect = 0; + } + continue; /* two keys have same (a,b) pair */ + } + + /* Given distinct (A,B) for all keys, build a perfect hash */ + if (!perfect(*tabb, tabh, tabq, *blen, smax, scramble, nkeys)) + { + if (++bad_perfect >= RETRY_PERFECT) + { + if (*blen < smax) + { + *blen *= 2; + delete[] *tabb; + delete[] tabq; + *tabb = new bstuff[*blen]; + tabq = new qstuff[*blen+1]; + --si; /* we know this salt got distinct (A,B) */ + } + else + { + return 0; + } + bad_perfect = 0; + } + continue; + } + + break; + } + + /* free working memory */ + delete[] tabh; + delete[] tabq; + + return 1; +} + +/* +------------------------------------------------------------------------------ +Input/output type routines +------------------------------------------------------------------------------ +*/ + +/* get the list of keys */ +static void getkeys(key **keys, ub4 *nkeys, const string_map& strings) +{ + key *buf = new key[strings.size()]; + size_t i; + string_map::const_iterator s; + for (i = 0, s = strings.begin(); s != strings.end(); ++s, ++i) { + key *mykey = buf+i; + mykey->name_k = (ub1 *)s->first; + mykey->len_k = (ub4)strlen(s->first); + } + *keys = buf; + *nkeys = (ub4)strings.size(); +} + + +static perfect_hash +make_perfect(const string_map& strings) +{ + ub4 nkeys; /* number of keys */ + key *keys; /* head of list of keys */ + bstuff *tab; /* table indexed by b */ + ub4 smax; /* scramble[] values in 0..smax-1, a power of 2 */ + ub4 alen; /* a in 0..alen-1, a power of 2 */ + ub4 blen; /* b in 0..blen-1, a power of 2 */ + ub8 salt; /* a parameter to the hash function */ + ub4 scramble[SCRAMBLE_LEN]; /* used in final hash function */ + int ok; + int i; + perfect_hash result; + + /* read in the list of keywords */ + getkeys(&keys, &nkeys, strings); + + /* find the hash */ + smax = ((ub4)1< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mega-dylib-utils.h" + +/* + This is a compatibility driver to allow us to support migrating to the new cache codebase and format without forcing B&I to alter gencaches. + This tool only supports flags used by B&I + This intention is for this tool to be removed in the near future and replaced with a tool that is not commandline compatible, + but allows shared caches to be built directly out of BuildRecords. + */ + +/* + Example commandline: + [60042] INFO - Executing Command: /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/update_dyld_shared_cache/update_dyld_shared_cache-357.0.91~2/Root/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/local/bin/update_dyld_shared_cache + -debug + -root /BuildRoot//Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.Internal.sdk + -dylib_list /tmp/dylibs.K93aInternalOS.60042 + -cache_dir /tmp/K93aInternalOS.60042 + -arch armv7 + -iPhone + -dont_map_local_symbols + -overlay /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/XPCService_caches/XPCService_caches-119~94/Root/K93aInternalOS/ + -overlay /SWE/Teams/DT-SWB/iOS/Binaries/Binaries5/libxpc_caches/libxpc_caches-649~18/Root/K93aInternalOS/ + */ + +// record warnings and add to .map file +static std::vector> sWarnings; + + +static void write_cache(const char* cachePath, SharedCache& cache) { + // create temp file for cache + int fd = ::open(cachePath, O_CREAT | O_RDWR | O_TRUNC, 0644); + if ( fd == -1 ) + terminate("can't create temp file %s, errnor=%d", cachePath, errno); + + // try to allocate whole cache file contiguously + fstore_t fcntlSpec = { F_ALLOCATECONTIG|F_ALLOCATEALL, F_PEOFPOSMODE, 0, static_cast(cache.fileSize()), 0 }; + ::fcntl(fd, F_PREALLOCATE, &fcntlSpec); + + // write out cache file + if ( ::pwrite(fd, cache.buffer().get(), cache.fileSize(), 0) != cache.fileSize() ) + terminate("write() failure creating cache file, errno=%d", errno); + + // flush to disk and close + int result = ::fcntl(fd, F_FULLFSYNC, NULL); + if ( result == -1 ) + warning("fcntl(F_FULLFSYNC) failed with errno=%d for %s\n", errno, cachePath); + ::close(fd); + + // write .map file + char mapPath[MAXPATHLEN]; + strlcpy(mapPath, cachePath, MAXPATHLEN); + strlcat(mapPath, ".map", MAXPATHLEN); + cache.writeCacheMapFile(mapPath); + + // append any warnings encountered + if ( !sWarnings.empty() ) { + FILE* fmap = ::fopen(mapPath, "a"); + if ( fmap != NULL ) { + fprintf(fmap, "\n\n"); + for ( std::unique_ptr& msg : sWarnings ) { + fprintf(fmap, "# %s", &*msg); + } + ::fclose(fmap); + } + } +} + +int main(int argc, const char* argv[]) +{ + std::set onlyArchs; + const char *rootPath = NULL; + std::vector searchPaths; + std::vector> dylibs; + const char* dylibListFile = NULL; +// bool force = false; +// bool keepSignatures = false; + bool explicitCacheDir = false; + bool dontMapLocalSymbols = false; + bool verbose = false; + bool iPhoneOS = false; + const char* cacheDir = NULL; + const char* archStr = NULL; + ArchPair archPair(CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL); + + // parse command line options + for(int i=1; i < argc; ++i) { + const char* arg = argv[i]; + if ( arg[0] == '-' ) { + if ( strcmp(arg, "-debug") == 0 ) { + verbose = true; + } + else if ( strcmp(arg, "-dont_map_local_symbols") == 0 ) { + dontMapLocalSymbols = true; + } + else if ( strcmp(arg, "-iPhone") == 0 ) { + iPhoneOS = true; + } + else if ( strcmp(arg, "-dylib_list") == 0 ) { + dylibListFile = argv[++i]; + if ( dylibListFile == NULL ) + terminate("-dylib_list missing path argument\n"); + } + else if ( (strcmp(arg, "-root") == 0) || (strcmp(arg, "--root") == 0) ) { + rootPath = argv[++i]; + } + else if ( strcmp(arg, "-overlay") == 0 ) { + const char* path = argv[++i]; + if ( path == NULL ) + terminate("-overlay missing path argument\n"); + searchPaths.push_back(path); + } + else if ( strcmp(arg, "-cache_dir") == 0 ) { + cacheDir = argv[++i]; + if ( cacheDir == NULL ) + terminate("-cache_dir missing path argument\n"); + explicitCacheDir = true; + } + else if ( strcmp(arg, "-arch") == 0 ) { + archStr = argv[++i]; + archPair = archForString(archStr); // terminates if unknown + } + else if ( strcmp(arg, "-force") == 0 ) { + // ignore. always forced for iOS + } + else { + //usage(); + terminate("unknown option: %s\n", arg); + } + } + else { + //usage(); + terminate("unknown option: %s\n", arg); + } + } + + if (!rootPath) { + terminate("-root is a required option\n"); + } + if (!iPhoneOS) { + terminate("-iPhone is a required option\n"); + } + if (!cacheDir) { + terminate("-cache_dir is a required option\n"); + } + if (!dylibListFile) { + terminate("-dylib_list is a required option\n"); + } + if (!archStr) { + terminate("-arch is a required option\n"); + } + + char prodCachePath[MAXPATHLEN]; + char devCachePath[MAXPATHLEN]; + strcpy(prodCachePath, cacheDir); + if ( prodCachePath[strlen(prodCachePath)-1] != '/' ) + strcat(prodCachePath, "/"); + strcat(prodCachePath, "dyld_shared_cache_"); + strcat(prodCachePath, archStr); + strcpy(devCachePath, prodCachePath); + strcat(devCachePath, ".development"); + + verboseLog("developement cache path = %s\n", devCachePath); + verboseLog("cache path = %s\n", prodCachePath); + + // create cache dirs if needed + char neededDirs[1024]; + strcpy(neededDirs, prodCachePath); + char* lastSlash = strrchr(neededDirs, '/'); + if ( lastSlash != NULL ) + lastSlash[1] = '\0'; + struct stat stat_buf; + if ( stat(neededDirs, &stat_buf) != 0 ) { + const char* afterSlash = &neededDirs[1]; + char* slash; + while ( (slash = strchr(afterSlash, '/')) != NULL ) { + *slash = '\0'; + ::mkdir(neededDirs, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); + *slash = '/'; + afterSlash = slash+1; + } + } + + std::string dylibOrderFile = toolDir() + "/dylib-order.txt"; + std::string dirtyDataOrderFile = toolDir() + "/dirty-data-segments-order.txt"; + + std::unordered_map> dependents; + SharedCache devCache(rootPath, searchPaths, dylibListFile, archPair, dylibOrderFile, dirtyDataOrderFile); + + if ( devCache.fileSize() == 0 ) + terminate("Could not find all necessary dylibs\n"); + + devCache.buildUnoptimizedCache(); + + SharedCache prodCache = devCache; + + prodCache.optimizeForProduction(); + devCache.optimizeForDevelopment(); + + verboseLog("developement cache size = %llu", devCache.fileSize()); + verboseLog("developement cache vm size = %llu", devCache.vmSize()); + + // optimize cache + write_cache(devCachePath, devCache); + if ( devCache.vmSize()+align(devCache.vmSize()/200, sharedRegionRegionAlignment(archPair)) > sharedRegionRegionSize(archPair)) { + terminate("update_dyld_shared_cache[%u] for arch=%s, shared cache will not fit in shared regions address space. Overflow amount: %llu\n", + getpid(), archStr, devCache.vmSize()+align(devCache.vmSize()/200, sharedRegionRegionAlignment(archPair)) - sharedRegionRegionSize(archPair)); + } + + write_cache(prodCachePath, prodCache); + + return 0; +} + + +void uniqueWarning(const char* format, ...) +{ + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} + +void log(const char * __restrict format, ...) +{ + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} +void verboseLog(const char* format, ...) +{ + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); +} + +void warning(const char* format, ...) +{ + fprintf(stderr, "update_dyld_shared_cache warning: "); + va_list list; + va_start(list, format); + char* msg; + vasprintf(&msg, format, list); + va_end(list); + fprintf(stderr, "%s\n", msg); + sWarnings.push_back(std::unique_ptr(msg)); +} + +void terminate(const char* format, ...) +{ + fprintf(stderr, "update_dyld_shared_cache error: "); + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + exit(1); +} diff --git a/dyld/launch-cache/Architectures.hpp b/dyld/launch-cache/Architectures.hpp new file mode 100644 index 0000000..fe3eea4 --- /dev/null +++ b/dyld/launch-cache/Architectures.hpp @@ -0,0 +1,62 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005-2011 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 __ARCHITECTURES__ +#define __ARCHITECTURES__ + +#include "FileAbstraction.hpp" + + +// +// Architectures +// +struct x86 +{ + typedef Pointer32 P; + +}; + +struct x86_64 +{ + typedef Pointer64 P; +}; + +struct arm +{ + typedef Pointer32 P; + +}; + +struct arm64 +{ + typedef Pointer64 P; + +}; + + + + +#endif // __ARCHITECTURES__ + + diff --git a/dyld/launch-cache/CacheFileAbstraction.hpp b/dyld/launch-cache/CacheFileAbstraction.hpp new file mode 100644 index 0000000..b1ac76c --- /dev/null +++ b/dyld/launch-cache/CacheFileAbstraction.hpp @@ -0,0 +1,418 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_ABSTRACTION__ +#define __DYLD_CACHE_ABSTRACTION__ + +#include "dyld_cache_format.h" + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +template +class dyldCacheHeader { +public: + const char* magic() const INLINE { return fields.magic; } + void set_magic(const char* value) INLINE { memcpy(fields.magic, value, 16); } + + uint32_t mappingOffset() const INLINE { return E::get32(fields.mappingOffset); } + void set_mappingOffset(uint32_t value) INLINE { E::set32(fields.mappingOffset, value); } + + uint32_t mappingCount() const INLINE { return E::get32(fields.mappingCount); } + void set_mappingCount(uint32_t value) INLINE { E::set32(fields.mappingCount, value); } + + uint32_t imagesOffset() const INLINE { return E::get32(fields.imagesOffset); } + void set_imagesOffset(uint32_t value) INLINE { E::set32(fields.imagesOffset, value); } + + uint32_t imagesCount() const INLINE { return E::get32(fields.imagesCount); } + void set_imagesCount(uint32_t value) INLINE { E::set32(fields.imagesCount, value); } + + uint64_t dyldBaseAddress() const INLINE { return E::get64(fields.dyldBaseAddress); } + void set_dyldBaseAddress(uint64_t value) INLINE { E::set64(fields.dyldBaseAddress, value); } + + 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); } + + uint64_t slideInfoSize() const INLINE { return E::get64(fields.slideInfoSize); } + void set_slideInfoSize(uint64_t value) INLINE { E::set64(fields.slideInfoSize, value); } + + uint64_t localSymbolsOffset() const INLINE { return E::get64(fields.localSymbolsOffset); } + void set_localSymbolsOffset(uint64_t value) INLINE { E::set64(fields.localSymbolsOffset, value); } + + uint64_t localSymbolsSize() const INLINE { return E::get64(fields.localSymbolsSize); } + void set_localSymbolsSize(uint64_t value) INLINE { E::set64(fields.localSymbolsSize, value); } + + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + + uint64_t cacheType() const INLINE { return E::get64(fields.cacheType); } + void set_cacheType(uint64_t value) INLINE { E::set64(fields.cacheType, value); } + + uint32_t branchPoolsOffset() const INLINE { return E::get32(fields.branchPoolsOffset); } + void set_branchPoolsOffset(uint32_t value) INLINE { E::set32(fields.branchPoolsOffset, value); } + + uint32_t branchPoolsCount() const INLINE { return E::get32(fields.branchPoolsCount); } + void set_branchPoolsCount(uint32_t value) INLINE { E::set32(fields.branchPoolsCount, value); } + + uint64_t accelerateInfoAddr() const INLINE { return E::get64(fields.accelerateInfoAddr); } + void set_accelerateInfoAddr(uint64_t value) INLINE { E::set64(fields.accelerateInfoAddr, value); } + + uint64_t accelerateInfoSize() const INLINE { return E::get64(fields.accelerateInfoSize); } + void set_accelerateInfoSize(uint64_t value) INLINE { E::set64(fields.accelerateInfoSize, value); } + + uint64_t imagesTextOffset() const INLINE { return E::get64(fields.imagesTextOffset); } + void set_imagesTextOffset(uint64_t value) INLINE { E::set64(fields.imagesTextOffset, value); } + + uint64_t imagesTextCount() const INLINE { return E::get64(fields.imagesTextCount); } + void set_imagesTextCount(uint64_t value) INLINE { E::set64(fields.imagesTextCount, value); } + +private: + dyld_cache_header fields; +}; + + + +template +class dyldCacheFileMapping { +public: + uint64_t address() const INLINE { return E::get64(fields.address); } + void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } + + uint64_t 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.fileOffset); } + void set_file_offset(uint64_t value) INLINE { E::set64(fields.fileOffset, 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.initProt); } + void set_init_prot(uint32_t value) INLINE { E::set32((uint32_t&)fields.initProt, value); } + +private: + dyld_cache_mapping_info fields; +}; + + +template +class dyldCacheImageInfo { +public: + uint64_t address() const INLINE { return E::get64(fields.address); } + void set_address(uint64_t value) INLINE { E::set64(fields.address, value); } + + uint64_t modTime() const INLINE { return E::get64(fields.modTime); } + void set_modTime(uint64_t value) INLINE { E::set64(fields.modTime, value); } + + uint64_t inode() const INLINE { return E::get64(fields.inode); } + void set_inode(uint64_t value) INLINE { E::set64(fields.inode, value); } + + uint32_t pathFileOffset() const INLINE { return E::get32(fields.pathFileOffset); } + void set_pathFileOffset(uint32_t value) INLINE { E::set32(fields.pathFileOffset, value); fields.pad=0; } + +private: + dyld_cache_image_info fields; +}; + +template +class dyldCacheImageTextInfo { +public: + const uint8_t* uuid() const INLINE { return fields.uuid; } + void set_uuid(const uint8_t value[16]) INLINE { memcpy(fields.uuid, value, 16); } + + uint64_t loadAddress() const INLINE { return E::get64(fields.loadAddress); } + void set_loadAddress(uint64_t value) INLINE { E::set64(fields.loadAddress, value); } + + uint32_t textSegmentSize() const INLINE { return E::get32(fields.textSegmentSize); } + void set_textSegmentSize(uint32_t value) INLINE { E::set32(fields.textSegmentSize, value); } + + uint32_t pathOffset() const INLINE { return E::get32(fields.pathOffset); } + void set_pathOffset(uint32_t value) INLINE { E::set32(fields.pathOffset, value); } + +private: + dyld_cache_image_text_info fields; +}; + + + +template +class dyldCacheImageInfoExtra { +public: + uint64_t exportsTrieAddr() const INLINE { return E::get64(fields.exportsTrieAddr); } + void set_exportsTrieAddr(uint64_t value) INLINE { E::set64(fields.exportsTrieAddr, value); } + + uint64_t weakBindingsAddr() const INLINE { return E::get64(fields.weakBindingsAddr); } + void set_weakBindingsAddr(uint64_t value) INLINE { E::set64(fields.weakBindingsAddr, value); } + + uint32_t exportsTrieSize() const INLINE { return E::get32(fields.exportsTrieSize); } + void set_exportsTrieSize(uint32_t value) INLINE { E::set32(fields.exportsTrieSize, value); } + + uint32_t weakBindingsSize() const INLINE { return E::get32(fields.weakBindingsSize); } + void set_weakBindingsSize(uint32_t value) INLINE { E::set32(fields.weakBindingsSize, value); } + + uint32_t dependentsStartArrayIndex() const INLINE { return E::get32(fields.dependentsStartArrayIndex); } + void set_dependentsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.dependentsStartArrayIndex, value); } + + uint32_t reExportsStartArrayIndex() const INLINE { return E::get32(fields.reExportsStartArrayIndex); } + void set_reExportsStartArrayIndex(uint32_t value) INLINE { E::set32(fields.reExportsStartArrayIndex, value); } + +private: + dyld_cache_image_info_extra fields; +}; + + +template +class dyldCacheAcceleratorInfo { +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 imageExtrasCount() const INLINE { return E::get32(fields.imageExtrasCount); } + void set_imageExtrasCount(uint32_t value) INLINE { E::set32(fields.imageExtrasCount, value); } + + uint32_t imagesExtrasOffset() const INLINE { return E::get32(fields.imagesExtrasOffset); } + void set_imagesExtrasOffset(uint32_t value) INLINE { E::set32(fields.imagesExtrasOffset, value); } + + uint32_t bottomUpListOffset() const INLINE { return E::get32(fields.bottomUpListOffset); } + void set_bottomUpListOffset(uint32_t value) INLINE { E::set32(fields.bottomUpListOffset, value); } + + uint32_t dylibTrieOffset() const INLINE { return E::get32(fields.dylibTrieOffset); } + void set_dylibTrieOffset(uint32_t value) INLINE { E::set32(fields.dylibTrieOffset, value); } + + uint32_t dylibTrieSize() const INLINE { return E::get32(fields.dylibTrieSize); } + void set_dylibTrieSize(uint32_t value) INLINE { E::set32(fields.dylibTrieSize, value); } + + uint32_t initializersOffset() const INLINE { return E::get32(fields.initializersOffset); } + void set_initializersOffset(uint32_t value) INLINE { E::set32(fields.initializersOffset, value); } + + uint32_t initializersCount() const INLINE { return E::get32(fields.initializersCount); } + void set_initializersCount(uint32_t value) INLINE { E::set32(fields.initializersCount, value); } + + uint32_t dofSectionsOffset() const INLINE { return E::get32(fields.dofSectionsOffset); } + void set_dofSectionsOffset(uint32_t value) INLINE { E::set32(fields.dofSectionsOffset, value); } + + uint32_t dofSectionsCount() const INLINE { return E::get32(fields.dofSectionsCount); } + void set_dofSectionsCount(uint32_t value) INLINE { E::set32(fields.dofSectionsCount, value); } + + uint32_t reExportListOffset() const INLINE { return E::get32(fields.reExportListOffset); } + void set_reExportListOffset(uint32_t value) INLINE { E::set32(fields.reExportListOffset, value); } + + uint32_t reExportCount() const INLINE { return E::get32(fields.reExportCount); } + void set_reExportCount(uint32_t value) INLINE { E::set32(fields.reExportCount, value); } + + uint32_t depListOffset() const INLINE { return E::get32(fields.depListOffset); } + void set_depListOffset(uint32_t value) INLINE { E::set32(fields.depListOffset, value); } + + uint32_t depListCount() const INLINE { return E::get32(fields.depListCount); } + void set_depListCount(uint32_t value) INLINE { E::set32(fields.depListCount, value); } + + uint32_t rangeTableOffset() const INLINE { return E::get32(fields.rangeTableOffset); } + void set_rangeTableOffset(uint32_t value) INLINE { E::set32(fields.rangeTableOffset, value); } + + uint32_t rangeTableCount() const INLINE { return E::get32(fields.rangeTableCount); } + void set_rangeTableCount(uint32_t value) INLINE { E::set32(fields.rangeTableCount, value); } + + uint64_t dyldSectionAddr() const INLINE { return E::get64(fields.dyldSectionAddr); } + void set_dyldSectionAddr(uint64_t value) INLINE { E::set64(fields.dyldSectionAddr, value); } + + +private: + dyld_cache_accelerator_info fields; +}; + + +template +class dyldCacheAcceleratorInitializer { +public: + uint32_t functionOffset() const INLINE { return E::get32(fields.functionOffset); } + void set_functionOffset(uint32_t value) INLINE { E::set32(fields.functionOffset, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_accelerator_initializer fields; +}; + + +template +class dyldCacheAcceleratorRangeEntry { +public: + uint64_t startAddress() const INLINE { return E::get64(fields.startAddress); } + void set_startAddress(uint64_t value) INLINE { E::set64(fields.startAddress, value); } + + uint32_t size() const INLINE { return E::get32(fields.size); } + void set_size(uint32_t value) INLINE { E::set32(fields.size, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_range_entry fields; +}; + +template +class dyldCacheAcceleratorDOFEntry { +public: + uint64_t sectionAddress() const INLINE { return E::get64(fields.sectionAddress); } + void set_sectionAddress(uint64_t value) INLINE { E::set64(fields.sectionAddress, value); } + + uint32_t sectionSize() const INLINE { return E::get32(fields.sectionSize); } + void set_sectionSize(uint32_t value) INLINE { E::set32(fields.sectionSize, value); } + + uint32_t imageIndex() const INLINE { return E::get32(fields.imageIndex); } + void set_imageIndex(uint32_t value) INLINE { E::set32(fields.imageIndex, value); } + +private: + dyld_cache_accelerator_dof fields; +}; + + +template +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 +}; + + +template +class dyldCacheSlideInfo2 { +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 page_starts_offset() const INLINE { return E::get32(fields.page_starts_offset); } + void set_page_starts_offset(uint32_t value) INLINE { E::set32(fields.page_starts_offset, value); } + + uint32_t page_starts_count() const INLINE { return E::get32(fields.page_starts_count); } + void set_page_starts_count(uint32_t value) INLINE { E::set32(fields.page_starts_count, value); } + + uint32_t page_extras_offset() const INLINE { return E::get32(fields.page_extras_offset); } + void set_page_extras_offset(uint32_t value) INLINE { E::set32(fields.page_extras_offset, value); } + + uint32_t page_extras_count() const INLINE { return E::get32(fields.page_extras_count); } + void set_page_extras_count(uint32_t value) INLINE { E::set32(fields.page_extras_count, value); } + + uint32_t page_size() const INLINE { return E::get32(fields.page_size); } + void set_page_size(uint32_t value) INLINE { E::set32(fields.page_size, value); } + + uint64_t delta_mask() const INLINE { return E::get64(fields.delta_mask); } + void set_delta_mask(uint64_t value) INLINE { E::set64(fields.delta_mask, value); } + + uint64_t value_add() const INLINE { return E::get64(fields.value_add); } + void set_value_add(uint64_t value) INLINE { E::set64(fields.value_add, value); } + + uint16_t page_starts(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index]); } + void set_page_starts(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_starts_offset)))[index], value); } + + uint16_t page_extras(unsigned index) const INLINE { return E::get16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index]); } + void set_page_extras(unsigned index, uint16_t value) INLINE { return E::set16(((uint16_t*)(((uint8_t*)this)+E::get16(fields.page_extras_offset)))[index], value); } + + +private: + dyld_cache_slide_info2 fields; +}; + + + +template +class dyldCacheLocalSymbolsInfo { +public: + uint32_t nlistOffset() const INLINE { return E::get32(fields.nlistOffset); } + void set_nlistOffset(uint32_t value) INLINE { E::set32(fields.nlistOffset, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + + uint32_t stringsOffset() const INLINE { return E::get32(fields.stringsOffset); } + void set_stringsOffset(uint32_t value) INLINE { E::set32(fields.stringsOffset, value); } + + uint32_t stringsSize() const INLINE { return E::get32(fields.stringsSize); } + void set_stringsSize(uint32_t value) INLINE { E::set32(fields.stringsSize, value); } + + uint32_t entriesOffset() const INLINE { return E::get32(fields.entriesOffset); } + void set_entriesOffset(uint32_t value) INLINE { E::set32(fields.entriesOffset, value); } + + uint32_t entriesCount() const INLINE { return E::get32(fields.entriesCount); } + void set_entriesCount(uint32_t value) INLINE { E::set32(fields.entriesCount, value); } + +private: + dyld_cache_local_symbols_info fields; +}; + + +template +class dyldCacheLocalSymbolEntry { +public: + uint32_t dylibOffset() const INLINE { return E::get32(fields.dylibOffset); } + void set_dylibOffset(uint32_t value) INLINE { E::set32(fields.dylibOffset, value); } + + uint32_t nlistStartIndex() const INLINE { return E::get32(fields.nlistStartIndex); } + void set_nlistStartIndex(uint32_t value) INLINE { E::set32(fields.nlistStartIndex, value); } + + uint32_t nlistCount() const INLINE { return E::get32(fields.nlistCount); } + void set_nlistCount(uint32_t value) INLINE { E::set32(fields.nlistCount, value); } + +private: + dyld_cache_local_symbols_entry fields; +}; + + + + +#endif // __DYLD_CACHE_ABSTRACTION__ + + diff --git a/dyld/launch-cache/FileAbstraction.hpp b/dyld/launch-cache/FileAbstraction.hpp new file mode 100644 index 0000000..1d73d74 --- /dev/null +++ b/dyld/launch-cache/FileAbstraction.hpp @@ -0,0 +1,163 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __FILE_ABSTRACTION__ +#define __FILE_ABSTRACTION__ + + +#include +#include +#include + +#ifdef __OPTIMIZE__ +#define INLINE __attribute__((always_inline)) +#else +#define INLINE +#endif + +// +// This abstraction layer is for use with file formats that have 64-bit/32-bit and Big-Endian/Little-Endian variants +// +// For example: to make a utility that handles 32-bit little enidan files use: Pointer32 +// +// +// get16() read a 16-bit number from an E endian struct +// set16() write a 16-bit number to an E endian struct +// get32() read a 32-bit number from an E endian struct +// set32() write a 32-bit number to an E endian struct +// get64() read a 64-bit number from an E endian struct +// set64() write a 64-bit number to an E endian struct +// +// getBits() read a bit field from an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// setBits() write a bit field to an E endian struct (bitCount=number of bits in field, firstBit=bit index of field) +// +// getBitsRaw() read a bit field from a struct with native endianness +// setBitsRaw() write a bit field from a struct with native endianness +// + +class BigEndian +{ +public: + static uint16_t get16(const uint16_t& from) INLINE { return OSReadBigInt16(&from, 0); } + static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteBigInt16(&into, 0, value); } + + static uint32_t get32(const uint32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static int32_t get32(const int32_t& from) INLINE { return OSReadBigInt32(&from, 0); } + static void set32(int32_t& into, int32_t value) INLINE { OSWriteBigInt32(&into, 0, value); } + + static uint64_t get64(const uint64_t& from) INLINE { return OSReadBigInt64(&from, 0); } + static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteBigInt64(&into, 0, value); } + + static uint32_t getBits(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return getBitsRaw(get32(from), firstBit, bitCount); } + static void setBits(uint32_t& into, uint32_t value, + uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = get32(into); setBitsRaw(temp, value, firstBit, bitCount); set32(into, temp); } + + static uint32_t getBitsRaw(const uint32_t& from, + uint8_t firstBit, uint8_t bitCount) INLINE { return ((from >> (32-firstBit-bitCount)) & ((1<> firstBit) & ((1< +class Pointer32 +{ +public: + typedef uint32_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get32(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set32(into, (uint32_t)value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+3) & ~(T)3; } + template + static T round_down(T value) { return value & ~(T)3; } +}; + + +template +class Pointer64 +{ +public: + typedef uint64_t uint_t; + typedef _E E; + + static uint64_t getP(const uint_t& from) INLINE { return _E::get64(from); } + static void setP(uint_t& into, uint64_t value) INLINE { _E::set64(into, value); } + + // Round to a P-size boundary + template + static T round_up(T value) { return (value+7) & ~(T)7; } + template + static T round_down(T value) { return value & ~(T)7; } +}; + + + + + +#endif // __FILE_ABSTRACTION__ + + diff --git a/dyld/launch-cache/MachOFileAbstraction.hpp b/dyld/launch-cache/MachOFileAbstraction.hpp new file mode 100644 index 0000000..ebd298d --- /dev/null +++ b/dyld/launch-cache/MachOFileAbstraction.hpp @@ -0,0 +1,968 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ +*/ +#ifndef __MACH_O_FILE_ABSTRACTION__ +#define __MACH_O_FILE_ABSTRACTION__ + +#include +#include +#include +#include + +// suport older versions of mach-o/loader.h +#ifndef LC_UUID +#define LC_UUID 0x1b +struct uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; +#endif + +#ifndef S_16BYTE_LITERALS + #define S_16BYTE_LITERALS 0xE +#endif + +#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 CPU_SUBTYPE_ARM_V7F + #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#endif +#ifndef CPU_SUBTYPE_ARM_V7K + #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#endif +#ifndef CPU_SUBTYPE_ARM_V7S + #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#endif +#ifndef CPU_SUBTYPE_ARM64_ALL + #define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#endif +#ifndef CPU_TYPE_ARM64 + #define CPU_TYPE_ARM64 ((cpu_type_t) (CPU_TYPE_ARM | CPU_ARCH_ABI64)) +#endif + +#define ARM64_RELOC_UNSIGNED 0 // for pointers + + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#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 + +#ifndef LC_FUNCTION_STARTS + #define LC_FUNCTION_STARTS 0x26 +#endif + +#ifndef LC_DATA_IN_CODE + #define LC_DATA_IN_CODE 0x29 +#endif + +#ifndef LC_DYLIB_CODE_SIGN_DRS + #define LC_DYLIB_CODE_SIGN_DRS 0x2B +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif + + +#define DYLD_CACHE_ADJ_V2_FORMAT 0x7F + +#define DYLD_CACHE_ADJ_V2_POINTER_32 0x01 +#define DYLD_CACHE_ADJ_V2_POINTER_64 0x02 +#define DYLD_CACHE_ADJ_V2_DELTA_32 0x03 +#define DYLD_CACHE_ADJ_V2_DELTA_64 0x04 +#define DYLD_CACHE_ADJ_V2_ARM64_ADRP 0x05 +#define DYLD_CACHE_ADJ_V2_ARM64_OFF12 0x06 +#define DYLD_CACHE_ADJ_V2_ARM64_BR26 0x07 +#define DYLD_CACHE_ADJ_V2_ARM_MOVW_MOVT 0x08 +#define DYLD_CACHE_ADJ_V2_ARM_BR24 0x09 +#define DYLD_CACHE_ADJ_V2_THUMB_MOVW_MOVT 0x0A +#define DYLD_CACHE_ADJ_V2_THUMB_BR22 0x0B +#define DYLD_CACHE_ADJ_V2_IMAGE_OFF_32 0x0C + +#define MH_HAS_OBJC 0x40000000 + +#ifndef CPU_SUBTYPE_ARM64_E + #define CPU_SUBTYPE_ARM64_E 2 +#endif + +#include "FileAbstraction.hpp" +#include "Architectures.hpp" + +// utility to pair together a cpu-type and cpu-sub-type +struct ArchPair +{ + uint32_t arch; + uint32_t subtype; + + ArchPair(uint32_t cputype, uint32_t cpusubtype) : arch(cputype), subtype(cpusubtype) {} + + bool operator<(const ArchPair& other) const { + if ( this->arch != other.arch ) + return (this->arch < other.arch); + return (this->subtype < other.subtype); + } + + bool operator==(const ArchPair& other) const { + return this->arch == other.arch && this->subtype == other.subtype; + } +}; + + +// +// This abstraction layer makes every mach-o file look like a 64-bit mach-o file with native endianness +// + +// +// mach-o load command +// +template +class macho_load_command { +public: + uint32_t cmd() const INLINE { return E::get32(command.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(command.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(command.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(command.cmdsize, value); } + + typedef typename P::E E; +private: + load_command command; +}; + + +// +// mach-o segment load command +// +template struct macho_segment_content {}; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; +template <> struct macho_segment_content > { segment_command fields; enum { CMD = LC_SEGMENT }; }; +template <> struct macho_segment_content > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; }; + +template +class macho_segment_command { +public: + uint32_t cmd() const INLINE { return E::get32(segment.fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(segment.fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(segment.fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(segment.fields.cmdsize, value); } + + const char* segname() const INLINE { return segment.fields.segname; } + void set_segname(const char* value) INLINE { strncpy(segment.fields.segname, value, 16); } + + uint64_t vmaddr() const INLINE { return P::getP(segment.fields.vmaddr); } + void set_vmaddr(uint64_t value) INLINE { P::setP(segment.fields.vmaddr, value); } + + uint64_t vmsize() const INLINE { return P::getP(segment.fields.vmsize); } + void set_vmsize(uint64_t value) INLINE { P::setP(segment.fields.vmsize, value); } + + uint64_t fileoff() const INLINE { return P::getP(segment.fields.fileoff); } + void set_fileoff(uint64_t value) INLINE { P::setP(segment.fields.fileoff, value); } + + uint64_t filesize() const INLINE { return P::getP(segment.fields.filesize); } + void set_filesize(uint64_t value) INLINE { P::setP(segment.fields.filesize, value); } + + uint32_t maxprot() const INLINE { return E::get32(segment.fields.maxprot); } + void set_maxprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.maxprot, value); } + + uint32_t initprot() const INLINE { return E::get32(segment.fields.initprot); } + void set_initprot(uint32_t value) INLINE { E::set32((uint32_t&)segment.fields.initprot, value); } + + uint32_t nsects() const INLINE { return E::get32(segment.fields.nsects); } + void set_nsects(uint32_t value) INLINE { E::set32(segment.fields.nsects, value); } + + uint32_t flags() const INLINE { return E::get32(segment.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(segment.fields.flags, value); } + + enum { + CMD = macho_segment_content

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

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

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

::CMD + }; +private: + macho_routines_content

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

entry; +}; + + + +// +// mach-o relocation info +// +template +class macho_relocation_info { +public: + uint32_t r_address() const INLINE { return E::get32(address); } + void set_r_address(uint32_t value) INLINE { E::set32(address, value); } + + uint32_t r_symbolnum() const INLINE { return E::getBits(other, 0, 24); } + void set_r_symbolnum(uint32_t value) INLINE { E::setBits(other, value, 0, 24); } + + bool r_pcrel() const INLINE { return E::getBits(other, 24, 1); } + void set_r_pcrel(bool value) INLINE { E::setBits(other, value, 24, 1); } + + uint8_t r_length() const INLINE { return E::getBits(other, 25, 2); } + void set_r_length(uint8_t value) INLINE { E::setBits(other, value, 25, 2); } + + bool r_extern() const INLINE { return E::getBits(other, 27, 1); } + void set_r_extern(bool value) INLINE { E::setBits(other, value, 27, 1); } + + uint8_t r_type() const INLINE { return E::getBits(other, 28, 4); } + void set_r_type(uint8_t value) INLINE { E::setBits(other, value, 28, 4); } + + void set_r_length() INLINE { set_r_length((sizeof(typename P::uint_t)==8) ? 3 : 2); } + + typedef typename P::E E; +private: + uint32_t address; + uint32_t other; +}; + + +// +// mach-o scattered relocation info +// The bit fields are always in big-endian order (see mach-o/reloc.h) +// +template +class macho_scattered_relocation_info { +public: + bool r_scattered() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 0, 1); } + void set_r_scattered(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 0, 1); E::set32(other, temp); } + + bool r_pcrel() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 1, 1); } + void set_r_pcrel(bool x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 1, 1); E::set32(other, temp); } + + uint8_t r_length() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 2, 2); } + void set_r_length(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 2, 2); E::set32(other, temp); } + + uint8_t r_type() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 4, 4); } + void set_r_type(uint8_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 4, 4); E::set32(other, temp); } + + uint32_t r_address() const INLINE { return BigEndian::getBitsRaw(E::get32(other), 8, 24); } + void set_r_address(uint32_t x) INLINE { uint32_t temp = E::get32(other); BigEndian::setBitsRaw(temp, x, 8, 24); E::set32(other, temp); } + + uint32_t r_value() const INLINE { return E::get32(value); } + void set_r_value(uint32_t x) INLINE { E::set32(value, x); } + + uint32_t r_other() const INLINE { return other; } + + typedef typename P::E E; +private: + uint32_t other; + uint32_t value; +}; + + +// +// mach-o file header +// +template struct macho_header_content {}; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; +template <> struct macho_header_content > { mach_header fields; }; +template <> struct macho_header_content > { mach_header_64 fields; }; + +template +class macho_header { +public: + uint32_t magic() const INLINE { return E::get32(header.fields.magic); } + void set_magic(uint32_t value) INLINE { E::set32(header.fields.magic, value); } + + uint32_t cputype() const INLINE { return E::get32(header.fields.cputype); } + void set_cputype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cputype, value); } + + uint32_t cpusubtype() const INLINE { return E::get32(header.fields.cpusubtype); } + void set_cpusubtype(uint32_t value) INLINE { E::set32((uint32_t&)header.fields.cpusubtype, value); } + + uint32_t filetype() const INLINE { return E::get32(header.fields.filetype); } + void set_filetype(uint32_t value) INLINE { E::set32(header.fields.filetype, value); } + + uint32_t ncmds() const INLINE { return E::get32(header.fields.ncmds); } + void set_ncmds(uint32_t value) INLINE { E::set32(header.fields.ncmds, value); } + + uint32_t sizeofcmds() const INLINE { return E::get32(header.fields.sizeofcmds); } + void set_sizeofcmds(uint32_t value) INLINE { E::set32(header.fields.sizeofcmds, value); } + + uint32_t flags() const INLINE { return E::get32(header.fields.flags); } + void set_flags(uint32_t value) INLINE { E::set32(header.fields.flags, value); } + + uint32_t reserved() const INLINE { return E::get32(header.fields.reserved); } + void set_reserved(uint32_t value) INLINE { E::set32(header.fields.reserved, value); } + + const macho_segment_command

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

* cmds = (macho_load_command

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

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

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

::CMD ) { + const macho_segment_command

* segcmd = (macho_segment_command

*)cmd; + if (0 == strncmp(segname, segcmd->segname(), 16)) { + return segcmd; + } + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + const macho_section

* getSection(const char *segname, const char *sectname) const + { + const macho_segment_command

* segcmd = getSegment(segname); + if (!segcmd) return NULL; + + const macho_section

* sectcmd = (macho_section

*)(segcmd+1); + 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; + } + } + + if (strcmp(segname, "__DATA") == 0) + return getSection("__DATA_CONST", sectname); + return NULL; + } + + const macho_load_command

* getLoadCommand(int query) const + { + const macho_load_command

* cmds = (macho_load_command

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

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

* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd() == query ) { + return cmd; + } + cmd = (macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return NULL; + } + + typedef typename P::E E; +private: + macho_header_content

header; +}; + + + +// +// compressed dyld info load command +// +template +class macho_dyld_info_command { +public: + uint32_t cmd() const INLINE { return E::get32(fields.cmd); } + void set_cmd(uint32_t value) INLINE { E::set32(fields.cmd, value); } + + uint32_t cmdsize() const INLINE { return E::get32(fields.cmdsize); } + void set_cmdsize(uint32_t value) INLINE { E::set32(fields.cmdsize, value); } + + uint32_t rebase_off() const INLINE { return E::get32(fields.rebase_off); } + void set_rebase_off(uint32_t value) INLINE { E::set32(fields.rebase_off, value); } + + uint32_t rebase_size() const INLINE { return E::get32(fields.rebase_size); } + void set_rebase_size(uint32_t value) INLINE { E::set32(fields.rebase_size, value); } + + uint32_t bind_off() const INLINE { return E::get32(fields.bind_off); } + void set_bind_off(uint32_t value) INLINE { E::set32(fields.bind_off, value); } + + uint32_t bind_size() const INLINE { return E::get32(fields.bind_size); } + void set_bind_size(uint32_t value) INLINE { E::set32(fields.bind_size, value); } + + uint32_t weak_bind_off() const INLINE { return E::get32(fields.weak_bind_off); } + void set_weak_bind_off(uint32_t value) INLINE { E::set32(fields.weak_bind_off, value); } + + uint32_t weak_bind_size() const INLINE { return E::get32(fields.weak_bind_size); } + void set_weak_bind_size(uint32_t value) INLINE { E::set32(fields.weak_bind_size, value); } + + uint32_t lazy_bind_off() const INLINE { return E::get32(fields.lazy_bind_off); } + void set_lazy_bind_off(uint32_t value) INLINE { E::set32(fields.lazy_bind_off, value); } + + uint32_t lazy_bind_size() const INLINE { return E::get32(fields.lazy_bind_size); } + void set_lazy_bind_size(uint32_t value) INLINE { E::set32(fields.lazy_bind_size, value); } + + uint32_t export_off() const INLINE { return E::get32(fields.export_off); } + void set_export_off(uint32_t value) INLINE { E::set32(fields.export_off, value); } + + uint32_t export_size() const INLINE { return E::get32(fields.export_size); } + void set_export_size(uint32_t value) INLINE { E::set32(fields.export_size, value); } + + + typedef typename P::E E; +private: + dyld_info_command fields; +}; + +#ifndef NO_ULEB +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + throw "malformed uleb128 extends beyond trie"; + + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) + throw "uleb128 too big for 64-bits"; + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + +inline int64_t read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= (((int64_t)(byte & 0x7f)) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return result; +} + +#endif + + +#endif // __MACH_O_FILE_ABSTRACTION__ + + diff --git a/dyld/launch-cache/MachOTrie.hpp b/dyld/launch-cache/MachOTrie.hpp new file mode 100644 index 0000000..d2f137e --- /dev/null +++ b/dyld/launch-cache/MachOTrie.hpp @@ -0,0 +1,399 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008-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 __MACH_O_TRIE__ +#define __MACH_O_TRIE__ + +#include +#include + +#include "MachOFileAbstraction.hpp" + + +namespace mach_o { +namespace trie { + +struct Edge +{ + Edge(const char* s, struct Node* n) : fSubString(s), fChild(n) { } + ~Edge() { } + const char* fSubString; + struct Node* fChild; + +}; + +struct Node +{ + Node(const char* s) : fCummulativeString(s), fAddress(0), fFlags(0), + fOther(0), fImportedName(NULL), fOrdered(false), + fHaveExportInfo(false), fTrieOffset(0) {} + ~Node() { } + const char* fCummulativeString; + std::vector fChildren; + uint64_t fAddress; + uint64_t fFlags; + uint64_t fOther; + const char* fImportedName; + bool fOrdered; + bool fHaveExportInfo; + uint32_t fTrieOffset; + + 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::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + long 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, other, importName); + return; + } + else { + for (long i=subStringLen-1; i > 0; --i) { + if ( strncmp(e.fSubString, partialStr, i) == 0 ) { + // found a common substring, splice in new node + // was A -> C, now A -> B -> C + char* bNodeCummStr = strdup(e.fChild->fCummulativeString); + bNodeCummStr[strlen(bNodeCummStr)+i-subStringLen] = '\0'; + //node* aNode = this; + Node* bNode = new Node(bNodeCummStr); + Node* cNode = e.fChild; + char* abEdgeStr = strdup(e.fSubString); + abEdgeStr[i] = '\0'; + char* bcEdgeStr = strdup(&e.fSubString[i]); + Edge& abEdge = e; + abEdge.fSubString = abEdgeStr; + abEdge.fChild = bNode; + Edge bcEdge(bcEdgeStr, cNode); + bNode->fChildren.push_back(bcEdge); + bNode->addSymbol(fullStr, address, flags, 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; + } + + void addOrderedNodes(const char* name, std::vector& orderedNodes) { + if ( !fOrdered ) { + orderedNodes.push_back(this); + //fprintf(stderr, "ordered %p %s\n", this, fCummulativeString); + fOrdered = true; + } + const char* partialStr = &name[strlen(fCummulativeString)]; + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + long subStringLen = strlen(e.fSubString); + if ( strncmp(e.fSubString, partialStr, subStringLen) == 0 ) { + // already have matching edge, go down that path + e.fChild->addOrderedNodes(name, orderedNodes); + return; + } + } + } + + // byte for terminal node size in bytes, or 0x00 if not terminal node + // teminal node (uleb128 flags, uleb128 addr [uleb128 other]) + // byte for child node count + // each child: zero terminated substring, uleb128 node offset + bool updateOffset(uint32_t& offset) { + 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::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + nodeSize += strlen(e.fSubString) + 1 + uleb128_size(e.fChild->fTrieOffset); + } + bool result = (fTrieOffset != offset); + fTrieOffset = offset; + //fprintf(stderr, "updateOffset %p %05d %s\n", this, fTrieOffset, fCummulativeString); + offset += nodeSize; + // return true if fTrieOffset was changed + return result; + } + + void appendToStream(std::vector& out) { + if ( fHaveExportInfo ) { + if ( fFlags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + if ( fImportedName != NULL ) { + // nodes with re-export info: size, flags, ordinal, string + uint32_t nodeSize = (uint32_t)(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 uleb128 of zero is one byte of zero + out.push_back(0); + } + // write number of children + out.push_back(fChildren.size()); + // write each child + for (std::vector::iterator it = fChildren.begin(); it != fChildren.end(); ++it) { + Edge& e = *it; + append_string(e.fSubString, out); + append_uleb128(e.fChild->fTrieOffset, out); + } + } + +private: + static void append_uleb128(uint64_t value, std::vector& out) { + uint8_t byte; + do { + byte = value & 0x7F; + value &= ~0x7F; + if ( value != 0 ) + byte |= 0x80; + out.push_back(byte); + value = value >> 7; + } while( byte >= 0x80 ); + } + + static void append_string(const char* str, std::vector& out) { + for (const char* s = str; *s != '\0'; ++s) + out.push_back(*s); + out.push_back('\0'); + } + + static unsigned int uleb128_size(uint64_t value) { + uint32_t result = 0; + do { + value = value >> 7; + ++result; + } while ( value != 0 ); + return result; + } + + +}; + +inline uint64_t read_uleb128(const uint8_t*& p, const uint8_t* end) { + uint64_t result = 0; + int bit = 0; + do { + if (p == end) +#if __EXCEPTIONS + throw "malformed uleb128 extends beyond trie"; +#else + return result; +#endif + uint64_t slice = *p & 0x7f; + + if (bit >= 64 || slice << bit >> bit != slice) +#if __EXCEPTIONS + throw "uleb128 too big for 64-bits"; +#else + return result; +#endif + else { + result |= (slice << bit); + bit += 7; + } + } + while (*p++ & 0x80); + return result; +} + + + +struct Entry +{ + const char* name; + uint64_t address; + uint64_t flags; + uint64_t other; + const char* importName; +}; + + + +inline void makeTrie(const std::vector& entries, std::vector& output) +{ + Node start(strdup("")); + + // make nodes for all exported symbols + for (std::vector::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 orderedNodes; + orderedNodes.reserve(entries.size()*2); + for (std::vector::const_iterator it = entries.begin(); it != entries.end(); ++it) { + start.addOrderedNodes(it->name, orderedNodes); + } + + // assign each node in the vector an offset in the trie stream, iterating until all uleb128 sizes have stabilized + bool more; + do { + uint32_t offset = 0; + more = false; + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + if ( (*it)->updateOffset(offset) ) + more = true; + } + } while ( more ); + + // create trie stream + for (std::vector::iterator it = orderedNodes.begin(); it != orderedNodes.end(); ++it) { + (*it)->appendToStream(output); + } +} + +struct EntryWithOffset +{ + uintptr_t nodeOffset; + Entry entry; + + bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } +}; + + + +static inline void processExportNode(const uint8_t* const start, const uint8_t* p, const uint8_t* const end, + char* cummulativeString, int curStrOffset, + std::vector& output) +{ + if ( p >= end ) +#if __EXCEPTIONS + throw "malformed trie, node past end"; +#else + return; +#endif + const uint8_t terminalSize = read_uleb128(p, end); + const uint8_t* children = p + terminalSize; + if ( terminalSize != 0 ) { + EntryWithOffset e; + e.nodeOffset = p-start; + e.entry.name = strdup(cummulativeString); + e.entry.flags = 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++; + const uint8_t* s = children; + for (uint8_t i=0; i < childrenCount; ++i) { + int edgeStrLen = 0; + while (*s != '\0') { + cummulativeString[curStrOffset+edgeStrLen] = *s++; + ++edgeStrLen; + } + cummulativeString[curStrOffset+edgeStrLen] = *s++; + uint32_t childNodeOffet = (uint32_t)read_uleb128(s, end); + processExportNode(start, start+childNodeOffet, end, cummulativeString, curStrOffset+edgeStrLen, output); + } +} + + +inline void parseTrie(const uint8_t* start, const uint8_t* end, std::vector& output) +{ + // empty trie has no entries + if ( start == end ) + return; + char cummulativeString[32000]; + std::vector entries; + processExportNode(start, start, end, cummulativeString, 0, entries); + // to preserve tie layout order, sort by node offset + std::sort(entries.begin(), entries.end()); + // copy to output + output.reserve(entries.size()); + for (std::vector::iterator it=entries.begin(); it != entries.end(); ++it) + output.push_back(it->entry); +} + + + + +}; // namespace trie +}; // namespace mach_o + + +#endif // __MACH_O_TRIE__ + + diff --git a/dyld/launch-cache/dsc_extractor.cpp b/dyld/launch-cache/dsc_extractor.cpp new file mode 100644 index 0000000..e55d74a --- /dev/null +++ b/dyld/launch-cache/dsc_extractor.cpp @@ -0,0 +1,693 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + +#include "dsc_iterator.h" +#include "dsc_extractor.h" +#include "MachOTrie.hpp" + +#include +#include +#include +#include +#include +#include + +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 CStringHash { +public: + size_t operator()(const char* __s) const { + size_t __h = 0; + for ( ; *__s; ++__s) + __h = 5 * __h + *__s; + return __h; + }; +}; +class CStringEquals { +public: + bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } +}; +typedef std::unordered_map, CStringHash, CStringEquals> NameToSegments; + +// Filter to find individual symbol re-exports in trie +class NotReExportSymbol { +public: + NotReExportSymbol(const std::set &rd) :_reexportDeps(rd) {} + bool operator()(const mach_o::trie::Entry &entry) const { + bool result = isSymbolReExport(entry); + if (result) { + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + ::free((void*)entry.name); + const_cast(&entry)->name = NULL; + } + return result; + } +private: + bool isSymbolReExport(const mach_o::trie::Entry &entry) const { + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_KIND_MASK) != EXPORT_SYMBOL_FLAGS_KIND_REGULAR ) + return true; + if ( (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) == 0 ) + return true; + // If the symbol comes from a dylib that is re-exported, this is not an individual symbol re-export + if ( _reexportDeps.count((int)entry.other) != 0 ) + return true; + return false; + } + const std::set &_reexportDeps; +}; + + +template +int optimize_linkedit(macho_header* mh, uint64_t textOffsetInCache, const void* mapped_cache, uint64_t* newSize) +{ + typedef typename A::P P; + typedef typename A::P::E E; + 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 unsigned origLoadCommandsSize = mh->sizeofcmds(); + unsigned bytesRemaining = origLoadCommandsSize; + unsigned removedCount = 0; + const macho_load_command

* const cmds = (macho_load_command

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

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

* cmd = cmds; + macho_segment_command

* linkEditSegCmd = NULL; + macho_symtab_command

* symtab = NULL; + macho_dysymtab_command

* dynamicSymTab = NULL; + macho_linkedit_data_command

* functionStarts = NULL; + macho_linkedit_data_command

* dataInCode = NULL; + uint32_t exportsTrieOffset = 0; + uint32_t exportsTrieSize = 0; + std::set reexportDeps; + int depIndex = 0; + for (uint32_t i = 0; i < cmdCount; ++i) { + bool remove = false; + switch ( cmd->cmd() ) { + case macho_segment_command

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

* segCmd = (macho_segment_command

*)cmd; + segCmd->set_fileoff(cumulativeFileSize); + macho_section

* const sectionsStart = (macho_section

*)((char*)segCmd + sizeof(macho_segment_command

)); + macho_section

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

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

* dyldInfo = (macho_dyld_info_command

*)cmd; + exportsTrieOffset = dyldInfo->export_off(); + exportsTrieSize = dyldInfo->export_size(); + dyldInfo->set_rebase_off(0); + dyldInfo->set_rebase_size(0); + dyldInfo->set_bind_off(0); + 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); + } + break; + case LC_SYMTAB: + symtab = (macho_symtab_command

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

*)cmd; + break; + case LC_FUNCTION_STARTS: + functionStarts = (macho_linkedit_data_command

*)cmd; + break; + case LC_DATA_IN_CODE: + dataInCode = (macho_linkedit_data_command

*)cmd; + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + ++depIndex; + if ( cmd->cmd() == LC_REEXPORT_DYLIB ) { + reexportDeps.insert(depIndex); + } + break; + case LC_SEGMENT_SPLIT_INFO: + // dylibs iOS 9 dyld caches have bogus LC_SEGMENT_SPLIT_INFO + remove = true; + break; + } + uint32_t cmdSize = cmd->cmdsize(); + macho_load_command

* nextCmd = (macho_load_command

*)(((uint8_t*)cmd)+cmdSize); + if ( remove ) { + ::memmove((void*)cmd, (void*)nextCmd, bytesRemaining); + ++removedCount; + } + else { + bytesRemaining -= cmdSize; + cmd = nextCmd; + } + } + // zero out stuff removed + ::bzero((void*)cmd, bytesRemaining); + // update header + mh->set_ncmds(cmdCount - removedCount); + mh->set_sizeofcmds(origLoadCommandsSize - bytesRemaining); + + // 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; + } + + const uint64_t newFunctionStartsOffset = linkEditSegCmd->fileoff(); + uint32_t functionStartsSize = 0; + if ( functionStarts != NULL ) { + // copy function starts from original cache file to new mapped dylib file + functionStartsSize = functionStarts->datasize(); + memcpy((char*)mh + newFunctionStartsOffset, (char*)mapped_cache + functionStarts->dataoff(), functionStartsSize); + } + const uint64_t newDataInCodeOffset = (newFunctionStartsOffset + functionStartsSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + uint32_t dataInCodeSize = 0; + if ( dataInCode != NULL ) { + // copy data-in-code info from original cache file to new mapped dylib file + dataInCodeSize = dataInCode->datasize(); + memcpy((char*)mh + newDataInCodeOffset, (char*)mapped_cache + dataInCode->dataoff(), dataInCodeSize); + } + + std::vector exports; + if ( exportsTrieSize != 0 ) { + const uint8_t* exportsStart = ((uint8_t*)mapped_cache) + exportsTrieOffset; + const uint8_t* exportsEnd = &exportsStart[exportsTrieSize]; + mach_o::trie::parseTrie(exportsStart, exportsEnd, exports); + exports.erase(std::remove_if(exports.begin(), exports.end(), NotReExportSymbol(reexportDeps)), exports.end()); + } + + // look for local symbol info in unmapped part of shared cache + dyldCacheHeader* header = (dyldCacheHeader*)mapped_cache; + macho_nlist

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

* allLocalNlists = (macho_nlist

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

* const mergedSymTabStart = (macho_nlist

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

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

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // skip any locals in cache + if ( (s->n_type() & (N_TYPE|N_EXT)) == N_SECT ) + continue; + ++newSymCount; + } + } + + // add room for N_INDR symbols for re-exported symbols + newSymCount += exports.size(); + + // copy symbol entries and strings from original cache file to new mapped dylib file + const uint64_t newSymTabOffset = (newDataInCodeOffset + dataInCodeSize + sizeof(pint_t) - 1) & (-sizeof(pint_t)); // pointer align + const uint64_t newIndSymTabOffset = newSymTabOffset + newSymCount*sizeof(macho_nlist

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

* const newSymTabStart = (macho_nlist

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

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

* s = mergedSymTabStart; s != mergedSymTabend; ++s) { + // if we have better local symbol info, skip any locals here + if ( (localNlists != NULL) && ((s->n_type() & (N_TYPE|N_EXT)) == N_SECT) ) + continue; + *t = *s; + t->set_n_strx(poolOffset); + const char* symName = &mergedStringPoolStart[s->n_strx()]; + if ( symName > mergedStringPoolEnd ) + symName = ""; + strcpy(&newStringPoolStart[poolOffset], symName); + poolOffset += (strlen(symName) + 1); + ++t; + ++symbolsCopied; + } + // recreate N_INDR symbols in extracted dylibs for debugger + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + strcpy(&newStringPoolStart[poolOffset], it->name); + t->set_n_strx(poolOffset); + poolOffset += (strlen(it->name) + 1); + t->set_n_type(N_INDR | N_EXT); + t->set_n_sect(0); + t->set_n_desc(0); + const char* importName = it->importName; + if ( *importName == '\0' ) + importName = it->name; + strcpy(&newStringPoolStart[poolOffset], importName); + t->set_n_value(poolOffset); + poolOffset += (strlen(importName) + 1); + ++t; + ++symbolsCopied; + } + if ( localNlists != NULL ) { + // update load command to reflect new count of locals + dynamicSymTab->set_ilocalsym(symbolsCopied); + dynamicSymTab->set_nlocalsym(localNlistCount); + // copy local symbols + for (uint32_t i=0; i < localNlistCount; ++i) { + const char* localName = &localStrings[localNlists[i].n_strx()]; + if ( localName > localStringsEnd ) + localName = ""; + *t = localNlists[i]; + t->set_n_strx(poolOffset); + strcpy(&newStringPoolStart[poolOffset], localName); + poolOffset += (strlen(localName) + 1); + ++t; + ++symbolsCopied; + } + } + + if ( newSymCount != symbolsCopied ) { + fprintf(stderr, "symbol count miscalculation\n"); + return -1; + } + + // pointer align string pool size + while ( (poolOffset % sizeof(pint_t)) != 0 ) + ++poolOffset; + // copy indirect symbol table + uint32_t* newIndSymTab = (uint32_t*)((char*)mh + newIndSymTabOffset); + memcpy(newIndSymTab, mergedIndSymTab, dynamicSymTab->nindirectsyms()*sizeof(uint32_t)); + + // update load commands + if ( functionStarts != NULL ) { + functionStarts->set_dataoff((uint32_t)newFunctionStartsOffset); + functionStarts->set_datasize(functionStartsSize); + } + if ( dataInCode != NULL ) { + dataInCode->set_dataoff((uint32_t)newDataInCodeOffset); + dataInCode->set_datasize(dataInCodeSize); + } + symtab->set_nsyms(symbolsCopied); + symtab->set_symoff((uint32_t)newSymTabOffset); + symtab->set_stroff((uint32_t)newStringPoolOffset); + symtab->set_strsize(poolOffset); + dynamicSymTab->set_extreloff(0); + dynamicSymTab->set_nextrel(0); + dynamicSymTab->set_locreloff(0); + dynamicSymTab->set_nlocrel(0); + dynamicSymTab->set_indirectsymoff((uint32_t)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); + + // Xcode 6 leaks in dyld_shared_cache_extract_dylibs + for (std::vector::iterator it = exports.begin(); it != exports.end(); ++it) { + ::free((void*)(it->name)); + } + + + 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 ) { + 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 +size_t dylib_maker(const void* mapped_cache, std::vector &dylib_data, const std::vector& segments) { + typedef typename A::P P; + + size_t additionalSize = 0; + for(std::vector::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(base_ptr) +#define FA reinterpret_cast(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 + uint64_t totalSize = 0; + uint64_t textOffsetInCache = 0; + for( std::vector::const_iterator it=segments.begin(); it != segments.end(); ++it) { + + if(strcmp(it->segName, "__TEXT") == 0 ) { + textOffsetInCache = it->offset; + const macho_header

*textMH = reinterpret_cast*>((uint8_t*)mapped_cache+textOffsetInCache); + 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(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(((macho_header

*)(base_ptr+offsetInFatFile)), textOffsetInCache, mapped_cache, &newSize); + + // update fat header with new file size + dylib_data.resize((size_t)(offsetInFatFile+newSize)); + base_ptr = &dylib_data.front(); + FA->size = OSSwapHostToBigInt32(newSize); +#undef FH +#undef FA + return offsetInFatFile; +} + + +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, (size_t)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&, const std::vector&) = NULL; + if ( strcmp((char*)mapped_cache, "dyld_v1 i386") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 x86_64h") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv5") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv6") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 armv7") == 0 ) + dylib_create_func = dylib_maker; + else if ( strncmp((char*)mapped_cache, "dyld_v1 armv7", 14) == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64") == 0 ) + dylib_create_func = dylib_maker; + else if ( strcmp((char*)mapped_cache, "dyld_v1 arm64e") == 0 ) + dylib_create_func = dylib_maker; + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + munmap(mapped_cache, (size_t)statbuf.st_size); + return -1; + } + + // iterate through all images in cache and build map of dylibs and segments + __block NameToSegments map; + __block int result = dyld_shared_cache_iterate(mapped_cache, (uint32_t)statbuf.st_size, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + map[dylibInfo->path].push_back(seg_info(segInfo->name, segInfo->fileOffset, segInfo->fileSize)); + }); + + if(result != 0) { + fprintf(stderr, "Error: dyld_shared_cache_iterate_segments_with_slide failed.\n"); + munmap(mapped_cache, (size_t)statbuf.st_size); + return result; + } + + // for each dylib instantiate a dylib file + dispatch_group_t group = dispatch_group_create(); + dispatch_semaphore_t sema = dispatch_semaphore_create(2); + 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 unsigned count = 0; + + for ( NameToSegments::iterator it = map.begin(); it != map.end(); ++it) { + dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + dispatch_group_async(group, process_queue, ^{ + + 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, it->second.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); + result = -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); + result = -1; + return; + } + + std::vector *vec = new std::vector((size_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); + result = -1; + return; + } + + const size_t offset = dylib_create_func(mapped_cache, *vec, it->second); + + dispatch_group_async(group, writer_queue, ^{ + progress(count++, (unsigned)map.size()); + + if(offset != vec->size()) { + //Write out the first page, and everything after offset + if( pwrite(fd, &vec->front(), 4096, 0) == -1 + || pwrite(fd, &vec->front() + offset, vec->size() - offset, offset) == -1) { + fprintf(stderr, "error writing, errnor=%d\n", errno); + result = -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, (size_t)statbuf.st_size); + return result; +} + + + +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 +// test program +#include +#include +#include + + +typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + +int main(int argc, const char* argv[]) +{ + if ( argc != 3 ) { + fprintf(stderr, "usage: dsc_extractor \n"); + return 1; + } + + //void* handle = dlopen("/Volumes/my/src/dyld/build/Debug/dsc_extractor.bundle", RTLD_LAZY); + void* handle = dlopen("/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/usr/lib/dsc_extractor.bundle", RTLD_LAZY); + if ( handle == NULL ) { + fprintf(stderr, "dsc_extractor.bundle could not be loaded\n"); + return 1; + } + + extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); + if ( proc == NULL ) { + fprintf(stderr, "dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); + return 1; + } + + int result = (*proc)(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 + + + + diff --git a/dyld/launch-cache/dsc_extractor.h b/dyld/launch-cache/dsc_extractor.h new file mode 100644 index 0000000..a8620d0 --- /dev/null +++ b/dyld/launch-cache/dsc_extractor.h @@ -0,0 +1,43 @@ +/* -*- 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 + +#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_ + diff --git a/dyld/launch-cache/dsc_iterator.cpp b/dyld/launch-cache/dsc_iterator.cpp new file mode 100644 index 0000000..7aa84ca --- /dev/null +++ b/dyld/launch-cache/dsc_iterator.cpp @@ -0,0 +1,249 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + + +#include "dsc_iterator.h" +#include "dyld_cache_format.h" +#define NO_ULEB +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" + + +namespace dyld { + + + // convert an address in the shared region where the cache would normally be mapped, into an address where the cache is currently mapped + template + const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr) + { + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) { + uint64_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address(); + const uint8_t* result = &cache[cacheOffset]; + if ( result < cacheEnd ) + return result; + else + return NULL; + } + } + return NULL; + } + + // call the callback block on each segment in this image + template + int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader, uint64_t cache_unslid_base_address, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) + { + typedef typename A::P P; + typedef typename A::P::E E; + dyld_shared_cache_dylib_info dylibInfo; + dyld_shared_cache_segment_info segInfo; + dylibInfo.version = 2; + dylibInfo.isAlias = (dylibPath < (char*)firstSeg); // paths for aliases are store between cache header and first segment + dylibInfo.machHeader = machHeader; + dylibInfo.path = dylibPath; + dylibInfo.inode = inode; + dylibInfo.modTime = modTime; + const macho_header

* mh = (const macho_header

*)machHeader; + const macho_load_command

* const cmds = (macho_load_command

*)(machHeader + sizeof(macho_header

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

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

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

::CMD ) { + macho_segment_command

* segCmd = (macho_segment_command

*)cmd; + uint64_t fileOffset = segCmd->fileoff(); + // work around until 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 + if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) ) + sizem = (cacheEnd-cache)-fileOffset; + } + segInfo.version = 2; + segInfo.name = segCmd->segname(); + segInfo.fileOffset = fileOffset; + segInfo.fileSize = sizem; + if ( segCmd->filesize() > segCmd->vmsize() ) + return -1; + segInfo.address = segCmd->vmaddr(); + segInfo.addressOffset = segInfo.address - cache_unslid_base_address; + callback(&dylibInfo, &segInfo); + } + cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + return 0; + } + + + // call walkSegments on each image in the cache + template + int walkImages(const uint8_t* cache, uint32_t size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) + { + // Sanity check there is at least a header + if ( (size > 0) && (size < 0x7000) ) + return -1; + typedef typename A::P::E E; + typedef typename A::P P; + const dyldCacheHeader* header = (dyldCacheHeader*)cache; + const dyldCacheImageInfo* dylibs = (dyldCacheImageInfo*)&cache[header->imagesOffset()]; + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)&cache[header->mappingOffset()]; + uint64_t unslid_base_address = mappings[0].address(); + uint64_t greatestMappingOffset = 0; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (size != 0) && (mappings[i].file_offset() > size) ) + return -1; + uint64_t endOffset = mappings[i].file_offset()+mappings[i].size(); + if ( (size != 0) && (endOffset > size) ) + return -1; + if ( endOffset > greatestMappingOffset ) + greatestMappingOffset = endOffset; + } + const uint8_t* cacheEnd = &cache[size]; + if ( size == 0 ) { + // Zero size means old API is being used, assume all mapped + cacheEnd = &cache[greatestMappingOffset]; + } + else { + // verifiy mappings are not bigger than size + if ( size < greatestMappingOffset ) + return -1; + } + // verify all image infos are mapped + if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd ) + return -1; + const uint8_t* firstSeg = NULL; + for (uint32_t i=0; i < header->imagesCount(); ++i) { + const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset(); + uint64_t inode = dylibs[i].inode(); + uint64_t modTime = dylibs[i].modTime(); + if ( (const uint8_t*)dylibPath > cacheEnd ) + return -1; + const uint8_t* machHeader = mappedAddress(cache, cacheEnd, dylibs[i].address()); + if ( machHeader == NULL ) + return -1; + if ( machHeader > cacheEnd ) + return -1; + if ( firstSeg == NULL ) + firstSeg = machHeader; + int result = walkSegments(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, unslid_base_address, callback); + if ( result != 0 ) + return result; + } + return 0; + } + +} + + +// Given a pointer to an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment +// in each dylib in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) { + const uint8_t* cache = (uint8_t*)shared_cache_file; + if ( strcmp((char*)cache, "dyld_v1 i386") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 x86_64h") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else if ( strcmp((char*)cache, "dyld_v1 arm64e") == 0 ) + return dyld::walkImages(cache, shared_cache_size, callback); + else + return -1; +} + + +// implement old version by calling new version +int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) +{ + return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) { + callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0); + }); +} + +// implement non-block version by calling block version +int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData) +{ + 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); + }); +} + diff --git a/dyld/launch-cache/dsc_iterator.h b/dyld/launch-cache/dsc_iterator.h new file mode 100644 index 0000000..d0ea94d --- /dev/null +++ b/dyld/launch-cache/dsc_iterator.h @@ -0,0 +1,84 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +struct dyld_shared_cache_dylib_info { + uint32_t version; // current version 2 + // following fields all exist in version 1 + uint32_t isAlias; // this is alternate path (symlink) + const void* machHeader; // of dylib in mapped cached file + const char* path; // of dylib + const uuid_t* uuid; // of dylib, or NULL is missing + // following fields all exist in version 2 + uint64_t inode; // of dylib file or path hash + uint64_t modTime; // of dylib file +}; +typedef struct dyld_shared_cache_dylib_info dyld_shared_cache_dylib_info; + +struct dyld_shared_cache_segment_info { + uint64_t version; // initial version 1 + // following fields exist in version 1 + const char* name; // of segment + uint64_t fileOffset; // of segment in cache file + uint64_t fileSize; // of segment + uint64_t address; // of segment when cache mapped with ASLR (sliding) off + // following fields exist in version 2 + uint64_t addressOffset; // of segment from base of mapped cache +}; +typedef struct dyld_shared_cache_segment_info dyld_shared_cache_segment_info; + + +#ifdef __cplusplus +extern "C" { +#endif + + +// Given a pointer and size of an in-memory copy of a dyld shared cache file, +// this routine will call the callback block once for each segment in each dylib +// in the shared cache file. +// Returns -1 if there was an error, otherwise 0. +extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size, + void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)); + + + +// +// The following iterator functions are deprecated: +// +typedef void (^dyld_shared_cache_iterator_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress); +typedef void (^dyld_shared_cache_iterator_slide_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide); +typedef void (*dyld_shared_cache_iterator_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, void* userData); +typedef void (*dyld_shared_cache_iterator_slide_nb_t)(const char* dylibName, const char* segName, uint64_t offset, uint64_t sizem, uint64_t mappedddress, uint64_t slide, void* userData); + +extern int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t callback, void* userData) __attribute__((deprecated)); +extern int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t callback, void* userData) __attribute__((deprecated)); + + +#ifdef __cplusplus +} +#endif diff --git a/dyld/launch-cache/dyld_cache_format.h b/dyld/launch-cache/dyld_cache_format.h new file mode 100644 index 0000000..57b5d07 --- /dev/null +++ b/dyld/launch-cache/dyld_cache_format.h @@ -0,0 +1,265 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2006-2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef __DYLD_CACHE_FORMAT__ +#define __DYLD_CACHE_FORMAT__ + +#include +#include +#include +#include + + +struct dyld_cache_header +{ + char magic[16]; // e.g. "dyld_v0 i386" + 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 + uint64_t localSymbolsOffset; // file offset of where local symbols are stored + uint64_t localSymbolsSize; // size of local symbols information + uint8_t uuid[16]; // unique value for each shared cache file + uint64_t cacheType; // 0 for development, 1 for production + uint32_t branchPoolsOffset; // file offset to table of uint64_t pool addresses + uint32_t branchPoolsCount; // number of uint64_t entries + uint64_t accelerateInfoAddr; // (unslid) address of optimization info + uint64_t accelerateInfoSize; // size of optimization info + uint64_t imagesTextOffset; // file offset to first dyld_cache_image_text_info + uint64_t imagesTextCount; // number of dyld_cache_image_text_info entries +}; + + +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 +{ + uint64_t address; + uint64_t modTime; + uint64_t inode; + uint32_t pathFileOffset; + uint32_t pad; +}; + +struct dyld_cache_image_info_extra +{ + uint64_t exportsTrieAddr; // address of trie in unslid cache + uint64_t weakBindingsAddr; + uint32_t exportsTrieSize; + uint32_t weakBindingsSize; + uint32_t dependentsStartArrayIndex; + uint32_t reExportsStartArrayIndex; +}; + + +struct dyld_cache_accelerator_info +{ + uint32_t version; // currently 1 + uint32_t imageExtrasCount; // does not include aliases + uint32_t imagesExtrasOffset; // offset into this chunk of first dyld_cache_image_info_extra + uint32_t bottomUpListOffset; // offset into this chunk to start of 16-bit array of sorted image indexes + uint32_t dylibTrieOffset; // offset into this chunk to start of trie containing all dylib paths + uint32_t dylibTrieSize; // size of trie containing all dylib paths + uint32_t initializersOffset; // offset into this chunk to start of initializers list + uint32_t initializersCount; // size of initializers list + uint32_t dofSectionsOffset; // offset into this chunk to start of DOF sections list + uint32_t dofSectionsCount; // size of initializers list + uint32_t reExportListOffset; // offset into this chunk to start of 16-bit array of re-exports + uint32_t reExportCount; // size of re-exports + uint32_t depListOffset; // offset into this chunk to start of 16-bit array of dependencies (0x8000 bit set if upward) + uint32_t depListCount; // size of dependencies + uint32_t rangeTableOffset; // offset into this chunk to start of ss + uint32_t rangeTableCount; // size of dependencies + uint64_t dyldSectionAddr; // address of libdyld's __dyld section in unslid cache +}; + +struct dyld_cache_accelerator_initializer +{ + uint32_t functionOffset; // address offset from start of cache mapping + uint32_t imageIndex; +}; + +struct dyld_cache_range_entry +{ + uint64_t startAddress; // unslid address of start of region + uint32_t size; + uint32_t imageIndex; +}; + +struct dyld_cache_accelerator_dof +{ + uint64_t sectionAddress; // unslid address of start of region + uint32_t sectionSize; + uint32_t imageIndex; +}; + +struct dyld_cache_image_text_info +{ + uuid_t uuid; + uint64_t loadAddress; // unslid address of start of __TEXT + uint32_t textSegmentSize; + uint32_t pathOffset; // offset from start of cache file +}; + +// The rebasing info is to allow the kernel to lazily rebase DATA pages of the +// dyld shared cache. Rebasing is adding the slide to interior pointers. +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]; +}; + + +// The version 2 of the slide info uses a different compression scheme. Since +// only interior pointers (pointers that point within the cache) are rebased +// (slid), we know the possible range of the pointers and thus know there are +// unused bits in each pointer. We use those bits to form a linked list of +// locations needing rebasing in each page. +// +// Definitions: +// +// pageIndex = (pageAddress - startOfAllDataAddress)/info->page_size +// pageStarts[] = info + info->page_starts_offset +// pageExtras[] = info + info->page_extras_offset +// valueMask = ~(info->delta_mask) +// deltaShift = __builtin_ctzll(info->delta_mask) - 2 +// +// There are three cases: +// +// 1) pageStarts[pageIndex] == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE +// The page contains no values that need rebasing. +// +// 2) (pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA) == 0 +// All rebase locations are in one linked list. The offset of the first +// rebase location in the page is pageStarts[pageIndex] * 4. +// +// 3) pageStarts[pageIndex] & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA +// Multiple linked lists are needed for all rebase locations in a page. +// The pagesExtras array contains 2 or more entries each of which is the +// start of a new linked list in the page. The first is at: +// extrasStartIndex = (pageStarts[pageIndex] & 0x3FFF) +// The next is at extrasStartIndex+1. The last is denoted by +// having the high bit (DYLD_CACHE_SLIDE_PAGE_ATTR_END) of the pageExtras[] +// set. +// +// For 64-bit architectures, there is always enough free bits to encode all +// possible deltas. The info->delta_mask field shows where the delta is located +// in the pointer. That value must be masked off (valueMask) before the slide +// is added to the pointer. +// +// For 32-bit architectures, there are only three bits free (the three most +// significant bits). To extract the delta, you must first subtract value_add +// from the pointer value, then AND with delta_mask, then shift by deltaShift. +// That still leaves a maximum delta to the next rebase location of 28 bytes. +// To reduce the number or chains needed, an optimization was added. Turns +// out zero is common in the DATA region. A zero can be turned into a +// non-rebasing entry in the linked list. The can be done because nothing +// in the shared cache should point out of its dylib to the start of the shared +// cache. +// +// The code for processing a linked list (chain) is: +// +// uint32_t delta = 1; +// while ( delta != 0 ) { +// uint8_t* loc = pageStart + pageOffset; +// uintptr_t rawValue = *((uintptr_t*)loc); +// delta = ((rawValue & deltaMask) >> deltaShift); +// uintptr_t newValue = (rawValue & valueMask); +// if ( newValue != 0 ) { +// newValue += valueAdd; +// newValue += slideAmount; +// } +// *((uintptr_t*)loc) = newValue; +// pageOffset += delta; +// } +// +// +struct dyld_cache_slide_info2 +{ + uint32_t version; // currently 2 + uint32_t page_size; // currently 4096 (may also be 16384) + uint32_t page_starts_offset; + uint32_t page_starts_count; + uint32_t page_extras_offset; + uint32_t page_extras_count; + uint64_t delta_mask; // which (contiguous) set of bits contains the delta to the next rebase location + uint64_t value_add; + //uint16_t page_starts[page_starts_count]; + //uint16_t page_extras[page_extras_count]; +}; +#define DYLD_CACHE_SLIDE_PAGE_ATTRS 0xC000 // high bits of uint16_t are flags +#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA 0x8000 // index is into extras array (not starts array) +#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE 0x4000 // page has no rebasing +#define DYLD_CACHE_SLIDE_PAGE_ATTR_END 0x8000 // last chain entry for page + + +struct dyld_cache_local_symbols_info +{ + uint32_t nlistOffset; // offset into this chunk of nlist entries + uint32_t nlistCount; // count of nlist entries + uint32_t stringsOffset; // offset into this chunk of string pool + uint32_t stringsSize; // byte count of string pool + uint32_t entriesOffset; // offset into this chunk of array of dyld_cache_local_symbols_entry + uint32_t entriesCount; // number of elements in dyld_cache_local_symbols_entry array +}; + +struct dyld_cache_local_symbols_entry +{ + uint32_t dylibOffset; // offset in cache file of start of dylib + uint32_t nlistStartIndex; // start index of locals for this dylib + uint32_t nlistCount; // number of local symbols for this dylib +}; + + + +#define MACOSX_DYLD_SHARED_CACHE_DIR "/private/var/db/dyld/" +#define IPHONE_DYLD_SHARED_CACHE_DIR "/System/Library/Caches/com.apple.dyld/" +#define DYLD_SHARED_CACHE_BASE_NAME "dyld_shared_cache_" +#define DYLD_SHARED_CACHE_DEVELOPMENT_EXT ".development" + +static const uint64_t kDyldSharedCacheTypeDevelopment = 0; +static const uint64_t kDyldSharedCacheTypeProduction = 1; + + + + +#endif // __DYLD_CACHE_FORMAT__ + + diff --git a/dyld/launch-cache/dyld_shared_cache_util.cpp b/dyld/launch-cache/dyld_shared_cache_util.cpp new file mode 100644 index 0000000..4c56c35 --- /dev/null +++ b/dyld/launch-cache/dyld_shared_cache_util.cpp @@ -0,0 +1,940 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dsc_iterator.h" +#include "dsc_extractor.h" +#include "dyld_cache_format.h" +#include "Architectures.hpp" +#include "MachOFileAbstraction.hpp" +#include "CacheFileAbstraction.hpp" +#include "Trie.hpp" + +enum Mode { + modeNone, + modeList, + modeMap, + modeDependencies, + modeSlideInfo, + modeAcceleratorInfo, + modeTextInfo, + modeLinkEdit, + modeLocalSymbols, + modeInfo, + modeSize, + modeExtract +}; + +struct Options { + Mode mode; + const char* dependentsOfPath; + const void* mappedCache; + const char* extractionDir; + bool printUUIDs; + bool printVMAddrs; + bool printDylibVersions; + bool printInodes; +}; + +struct TextInfo { + uint64_t textSize; + const char* path; +}; + +struct TextInfoSorter { + bool operator()(const TextInfo& left, const TextInfo& right) { + return (left.textSize > right.textSize); + } +}; + +struct Results { + std::map pageToContent; + uint64_t linkeditBase; + bool dependentTargetFound; + std::vector textSegments; +}; + + + +void usage() { + fprintf(stderr, "Usage: dyld_shared_cache_util -list [ -uuid ] [-vmaddr] | -dependents [ -versions ] | -linkedit | -map | -slide_info | -info | -extract [ shared-cache-file ] \n"); +} + +#if __x86_64__ +static bool isHaswell() +{ + // check system is capable of running x86_64h code + struct host_basic_info info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + mach_port_t hostPort = mach_host_self(); + kern_return_t result = host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&info, &count); + mach_port_deallocate(mach_task_self(), hostPort); + if ( result != KERN_SUCCESS ) + return false; + return ( info.cpu_subtype == CPU_SUBTYPE_X86_64_H ); +} +#endif + +/* + * Get the path to the native shared cache for this host + */ +static const char* default_shared_cache_path() { +#if __i386__ + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "i386"; +#elif __x86_64__ + if ( isHaswell() ) + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64h"; + else + return MACOSX_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "x86_64"; +#elif __ARM_ARCH_5TEJ__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv5"; +#elif __ARM_ARCH_6K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv6"; +#elif __ARM_ARCH_7K__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7k"; +#elif __ARM_ARCH_7A__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7"; +#elif __ARM_ARCH_7F__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7f"; +#elif __ARM_ARCH_7S__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "armv7s"; +#elif __arm64e__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64e"; +#elif __arm64__ + return IPHONE_DYLD_SHARED_CACHE_DIR DYLD_SHARED_CACHE_BASE_NAME "arm64"; +#else + #error unsupported architecture +#endif +} + +typedef void (*segment_callback_t)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results); + + + +/* + * List dependencies from the mach-o header at headerAddr + * in the same format as 'otool -L' + */ +template +void print_dependencies(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) { + typedef typename A::P P; + typedef typename A::P::E E; + + if ( strcmp(options.dependentsOfPath, dylibInfo->path) != 0 ) + return; + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + + const macho_dylib_command

* dylib_cmd; + const macho_header

* mh = (const macho_header

*)dylibInfo->machHeader; + const macho_load_command

* const cmds = (macho_load_command

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

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

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

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } + results.dependentTargetFound = true; +} + +/* + * Print out a dylib from the shared cache, optionally including the UUID or unslid load address + */ +template +void print_list(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) +{ + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + + if ( options.printVMAddrs ) + printf("0x%08llX ", segInfo->address); + if ( options.printInodes ) + printf("0x%08llX 0x%08llX ", dylibInfo->inode, dylibInfo->modTime); + if ( options.printUUIDs ) { + if ( dylibInfo->uuid != NULL ) { + const uint8_t* uuid = (uint8_t*)dylibInfo->uuid;; + printf("<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> ", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + else + printf("< no uuid in dylib > "); + } + if ( dylibInfo->isAlias ) + printf("[alias] %s\n", dylibInfo->path); + else + printf("%s\n", dylibInfo->path); +} + + +template +void collect_size(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) +{ + if ( strcmp(segInfo->name, "__TEXT") != 0 ) + return; + if ( dylibInfo->isAlias ) + return; + + TextInfo info; + info.textSize = segInfo->fileSize; + info.path = dylibInfo->path; + results.textSegments.push_back(info); +} + + + + +static void add_linkedit(uint32_t pageStart, uint32_t pageEnd, const char* message, Results& results) +{ + for (uint32_t p = pageStart; p <= pageEnd; p += 4096) { + std::map::iterator pos = results.pageToContent.find(p); + if ( pos == results.pageToContent.end() ) { + results.pageToContent[p] = strdup(message); + } + else { + const char* oldMessage = pos->second; + char* newMesssage; + asprintf(&newMesssage, "%s, %s", oldMessage, message); + results.pageToContent[p] = newMesssage; + ::free((void*)oldMessage); + } + } +} + + +/* + * get LINKEDIT info for dylib + */ +template +void process_linkedit(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, + const Options& options, Results& results) { + typedef typename A::P P; + typedef typename A::P::E E; + // filter out symlinks + if ( dylibInfo->isAlias ) + return; + const macho_header

* mh = (const macho_header

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

* const cmds = (macho_load_command

*)((long)mh + sizeof(macho_header

)); + const macho_load_command

* cmd = cmds; + for (uint32_t i = 0; i < ncmds; i++) { + if ( cmd->cmd() == LC_DYLD_INFO_ONLY ) { + macho_dyld_info_command

* dyldInfo = (macho_dyld_info_command

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

*)(((uint8_t*)cmd)+cmd->cmdsize()); + } +} + + +/* + * Print out a .map file similar to what update_dyld_shared_cache created when the cache file was built + */ +template +void print_map(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo, const Options& options, Results& results) { + if ( !dylibInfo->isAlias ) + printf("0x%08llX - 0x%08llX %s %s\n", segInfo->address, segInfo->address + segInfo->fileSize, segInfo->name, dylibInfo->path); +} + + +static void checkMode(Mode mode) { + if ( mode != modeNone ) { + fprintf(stderr, "Error: select one of: -list, -dependents, -info, -slide_info, -linkedit, -map, -extract, or -size\n"); + usage(); + exit(1); + } +} + +int main (int argc, const char* argv[]) { + + const char* sharedCachePath = default_shared_cache_path(); + + Options options; + options.mode = modeNone; + options.printUUIDs = false; + options.printVMAddrs = false; + options.printDylibVersions = false; + options.printInodes = false; + options.dependentsOfPath = NULL; + options.extractionDir = NULL; + + for (uint32_t i = 1; i < argc; i++) { + const char* opt = argv[i]; + if (opt[0] == '-') { + if (strcmp(opt, "-list") == 0) { + checkMode(options.mode); + options.mode = modeList; + } + else if (strcmp(opt, "-dependents") == 0) { + checkMode(options.mode); + options.mode = modeDependencies; + options.dependentsOfPath = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -depdendents requires an argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-linkedit") == 0) { + checkMode(options.mode); + options.mode = modeLinkEdit; + } + else if (strcmp(opt, "-info") == 0) { + checkMode(options.mode); + options.mode = modeInfo; + } + else if (strcmp(opt, "-slide_info") == 0) { + checkMode(options.mode); + options.mode = modeSlideInfo; + } + else if (strcmp(opt, "-accelerator_info") == 0) { + checkMode(options.mode); + options.mode = modeAcceleratorInfo; + } + else if (strcmp(opt, "-text_info") == 0) { + checkMode(options.mode); + options.mode = modeTextInfo; + } + else if (strcmp(opt, "-local_symbols") == 0) { + checkMode(options.mode); + options.mode = modeLocalSymbols; + } + else if (strcmp(opt, "-map") == 0) { + checkMode(options.mode); + options.mode = modeMap; + } + else if (strcmp(opt, "-size") == 0) { + checkMode(options.mode); + options.mode = modeSize; + } + else if (strcmp(opt, "-extract") == 0) { + checkMode(options.mode); + options.mode = modeExtract; + options.extractionDir = argv[++i]; + if ( i >= argc ) { + fprintf(stderr, "Error: option -extract requires a directory argument\n"); + usage(); + exit(1); + } + } + else if (strcmp(opt, "-uuid") == 0) { + options.printUUIDs = true; + } + else if (strcmp(opt, "-inode") == 0) { + options.printInodes = true; + } + else if (strcmp(opt, "-versions") == 0) { + options.printDylibVersions = true; + } + else if (strcmp(opt, "-vmaddr") == 0) { + options.printVMAddrs = true; + } + else { + fprintf(stderr, "Error: unrecognized option %s\n", opt); + usage(); + exit(1); + } + } + else { + sharedCachePath = opt; + } + } + + if ( options.mode == modeNone ) { + fprintf(stderr, "Error: select one of -list, -dependents, -info, -linkedit, or -map\n"); + usage(); + exit(1); + } + + if ( options.mode != modeSlideInfo ) { + if ( options.printUUIDs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -uuid option ignored outside of -list mode\n"); + + if ( options.printVMAddrs && (options.mode != modeList) ) + fprintf(stderr, "Warning: -vmaddr option ignored outside of -list mode\n"); + + if ( options.printDylibVersions && (options.mode != modeDependencies) ) + fprintf(stderr, "Warning: -versions option ignored outside of -dependents mode\n"); + + if ( (options.mode == modeDependencies) && (options.dependentsOfPath == NULL) ) { + fprintf(stderr, "Error: -dependents given, but no dylib path specified\n"); + usage(); + exit(1); + } + } + + struct stat statbuf; + if ( ::stat(sharedCachePath, &statbuf) == -1 ) { + fprintf(stderr, "Error: stat() failed for dyld shared cache at %s, errno=%d\n", sharedCachePath, errno); + exit(1); + } + + int cache_fd = ::open(sharedCachePath, O_RDONLY); + if ( cache_fd < 0 ) { + fprintf(stderr, "Error: open() failed for shared cache file at %s, errno=%d\n", sharedCachePath, errno); + exit(1); + } + options.mappedCache = ::mmap(NULL, (size_t)statbuf.st_size, PROT_READ, MAP_PRIVATE, cache_fd, 0); + if (options.mappedCache == MAP_FAILED) { + fprintf(stderr, "Error: mmap() for shared cache at %s failed, errno=%d\n", sharedCachePath, errno); + exit(1); + } + + if ( options.mode == modeSlideInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( header->slideInfoOffset() == 0 ) { + fprintf(stderr, "Error: dyld shared cache does not contain slide info\n"); + exit(1); + } + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + const dyldCacheFileMapping* dataMapping = &mappings[1]; + uint64_t dataStartAddress = dataMapping->address(); + uint64_t dataSize = dataMapping->size(); + const dyldCacheSlideInfo* slideInfoHeader = (dyldCacheSlideInfo*)((char*)options.mappedCache+header->slideInfoOffset()); + printf("slide info version=%d\n", slideInfoHeader->version()); + if ( slideInfoHeader->version() == 1 ) { + 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 if ( slideInfoHeader->version() == 2 ) { + const dyldCacheSlideInfo2* slideInfo = (dyldCacheSlideInfo2*)(slideInfoHeader); + printf("page_size=%d\n", slideInfo->page_size()); + printf("delta_mask=0x%016llX\n", slideInfo->delta_mask()); + printf("value_add=0x%016llX\n", slideInfo->value_add()); + printf("page_starts_count=%d, page_extras_count=%d\n", slideInfo->page_starts_count(), slideInfo->page_extras_count()); + const uint16_t* starts = (uint16_t* )((char*)slideInfo + slideInfo->page_starts_offset()); + const uint16_t* extras = (uint16_t* )((char*)slideInfo + slideInfo->page_extras_offset()); + for (int i=0; i < slideInfo->page_starts_count(); ++i) { + const uint16_t start = starts[i]; + if ( start == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE ) { + printf("page[% 5d]: no rebasing\n", i); + } + else if ( start & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA ) { + printf("page[% 5d]: ", i); + int j=(start & 0x3FFF); + bool done = false; + do { + uint16_t aStart = extras[j]; + printf("start=0x%04X ", aStart & 0x3FFF); + done = (extras[j] & DYLD_CACHE_SLIDE_PAGE_ATTR_END); + ++j; + } while ( !done ); + printf("\n"); + } + else { + printf("page[% 5d]: start=0x%04X\n", i, starts[i]); + } + } + } + } + else if ( options.mode == modeInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + printf("uuid: "); + if ( header->mappingOffset() >= 0x68 ) { + const uint8_t* uuid = header->uuid(); + printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X\n", + uuid[0], uuid[1], uuid[2], uuid[3], + uuid[4], uuid[5], uuid[6], uuid[7], + uuid[8], uuid[9], uuid[10], uuid[11], + uuid[12], uuid[13], uuid[14], uuid[15]); + } + else { + printf("n/a\n"); + } + if ( header->mappingOffset() >= 0xE0 ) { + // HACK until this uses new header + uint32_t platform = *((uint32_t*)(((char*)header) + 0xD8)); + uint32_t simulator = *((uint32_t*)(((char*)header) + 0xDC)); + switch (platform) { + case 1: + printf("platform: macOS\n"); + break; + case 2: + if ( simulator & 0x400 ) + printf("platform: iOS simulator\n"); + else + printf("platform: iOS\n"); + break; + case 3: + if ( simulator & 0x400 ) + printf("platform: tvOS simulator\n"); + else + printf("platform: tvOS\n"); + break; + case 4: + if ( simulator & 0x400 ) + printf("platform: watchOS simulator\n"); + else + printf("platform: watchOS\n"); + break; + case 5: + printf("platform: bridgeOS\n"); + break; + default: + printf("platform: 0x%08X 0x%08X\n", platform, simulator); + } + } + printf("image count: %u\n", header->imagesCount()); + if ( (header->mappingOffset() >= 0x78) && (header->branchPoolsOffset() != 0) ) { + printf("branch pool count: %u\n", header->branchPoolsCount()); + } + printf("mappings:\n"); + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( mappings[i].init_prot() & VM_PROT_EXECUTE ) + printf(" __TEXT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + else if ( mappings[i]. init_prot() & VM_PROT_WRITE ) + printf(" __DATA %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + else if ( mappings[i].init_prot() & VM_PROT_READ ) + printf(" __LINKEDIT %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + mappings[i].size()/(1024*1024), mappings[i].file_offset(), mappings[i].file_offset() + mappings[i].size(), + mappings[i].address(), mappings[i].address() + mappings[i].size()); + } + if ( header->codeSignatureOffset() != 0 ) { + uint64_t size = statbuf.st_size - header->codeSignatureOffset(); + uint64_t csAddr = mappings[header->mappingCount()-1].address() + mappings[header->mappingCount()-1].size(); + if ( size != 0 ) + printf(" code sign %3lluMB, file offset: 0x%08llX -> 0x%08llX, address: 0x%08llX -> 0x%08llX\n", + size/(1024*1024), header->codeSignatureOffset(), header->codeSignatureOffset() + size, csAddr, csAddr + size); + } + printf("slide info: %4lluKB, file offset: 0x%08llX -> 0x%08llX\n", + header->slideInfoSize()/1024, header->slideInfoOffset(), header->slideInfoOffset() + header->slideInfoSize()); + if ( header->localSymbolsOffset() != 0 ) + printf("local symbols: %3lluMB, file offset: 0x%08llX -> 0x%08llX\n", + header->localSymbolsSize()/(1024*1024), header->localSymbolsOffset(), header->localSymbolsOffset() + header->localSymbolsSize()); + if ( (header->mappingOffset() >= 0x78) && (header->accelerateInfoSize() != 0) ) + printf("accelerate tab: %3lluKB, address: 0x%08llX -> 0x%08llX\n", + header->accelerateInfoSize()/1024, header->accelerateInfoAddr(), header->accelerateInfoAddr() + header->accelerateInfoSize()); + } + else if ( options.mode == modeAcceleratorInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( (header->mappingOffset() < sizeof(dyldCacheHeader)) || (header->accelerateInfoSize() == 0) ) { + printf("no accelerator info\n"); + } + else { + const dyldCacheFileMapping* mappings = (dyldCacheFileMapping*)((char*)options.mappedCache + header->mappingOffset()); + uint64_t aiAddr = header->accelerateInfoAddr(); + dyldCacheAcceleratorInfo* accelInfo = NULL; + for (uint32_t i=0; i < header->mappingCount(); ++i) { + if ( (mappings[i].address() <= aiAddr) && (aiAddr < mappings[i].address()+mappings[i].size()) ) { + uint64_t offset = aiAddr - mappings[i].address() + mappings[i].file_offset(); + accelInfo = (dyldCacheAcceleratorInfo*)((uint8_t*)options.mappedCache + offset); + } + } + if ( accelInfo == NULL ) { + printf("accelerator info not in any mapped range\n"); + } + else { + const dyldCacheImageInfo* images = (dyldCacheImageInfo*)((char*)options.mappedCache + header->imagesOffset()); + const dyldCacheImageInfoExtra* imagesExtra = (dyldCacheImageInfoExtra*)((char*)accelInfo + accelInfo->imagesExtrasOffset()); + const uint16_t* dependencyArray = (uint16_t*)((char*)accelInfo + accelInfo->depListOffset()); + const uint16_t* reExportArray = (uint16_t*)((char*)accelInfo + accelInfo->reExportListOffset()); + printf("extra image info (count=%u):\n", accelInfo->imageExtrasCount()); + for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) { + printf(" image[%3u] %s:\n", i, (char*)options.mappedCache +images[i].pathFileOffset()); + printf(" exports trie: addr=0x%llX, size=0x%08X\n", imagesExtra[i].exportsTrieAddr(), imagesExtra[i].exportsTrieSize()); + if ( imagesExtra[i].weakBindingsSize() ) + printf(" weak bind info: addr=0x%llX, size=0x%08X\n", imagesExtra[i].weakBindingsAddr(), imagesExtra[i].weakBindingsSize()); + printf(" dependents: "); + for (uint32_t d=imagesExtra[i].dependentsStartArrayIndex(); dependencyArray[d] != 0xFFFF; ++d) { + uint16_t depIndex = dependencyArray[d]; + if ( depIndex & 0x8000 ) + printf(" up(%d) ", depIndex & 0x7FFF); + else + printf(" %d ", depIndex); + } + printf("\n"); + printf(" re-exports: "); + for (uint32_t r=imagesExtra[i].reExportsStartArrayIndex(); reExportArray[r] != 0xFFFF; ++r) + printf(" %d ", reExportArray[r]); + printf("\n"); + } + printf("libdyld.dylib:\n"); + printf(" __dyld section address: 0x%llX\n", accelInfo->dyldSectionAddr()); + printf("initializers (count=%u):\n", accelInfo->initializersCount()); + const dyldCacheAcceleratorInitializer* initializers = (dyldCacheAcceleratorInitializer*)((char*)accelInfo + accelInfo->initializersOffset()); + for (uint32_t i=0; i < accelInfo->initializersCount(); ++i) { + printf(" image[%3u] 0x%llX\n", initializers[i].imageIndex(), mappings[0].address() + initializers[i].functionOffset()); + } + printf("DOF sections (count=%u):\n", accelInfo->dofSectionsCount()); + const dyldCacheAcceleratorDOFEntry* dofs = (dyldCacheAcceleratorDOFEntry*)((char*)accelInfo + accelInfo->dofSectionsOffset()); + for (uint32_t i=0; i < accelInfo->dofSectionsCount(); ++i) { + printf(" image[%3u] 0x%llX -> 0x%llX\n", dofs[i].imageIndex(), dofs[i].sectionAddress(), dofs[i].sectionAddress()+dofs[i].sectionSize()); + } + printf("bottom up order (count=%u):\n", accelInfo->imageExtrasCount()); + const uint16_t* bottomUpArray = (uint16_t*)((char*)accelInfo + accelInfo->bottomUpListOffset()); + for (uint32_t i=0; i < accelInfo->imageExtrasCount(); ++i) { + unsigned imageIndex = bottomUpArray[i]; + if ( imageIndex < accelInfo->imageExtrasCount() ) + printf(" image[%3u] %s\n", imageIndex, (char*)options.mappedCache + images[imageIndex].pathFileOffset()); + else + printf(" image[%3u] BAD INDEX\n", imageIndex); + } + printf("range table (count=%u):\n", accelInfo->rangeTableCount()); + const dyldCacheAcceleratorRangeEntry* rangeTable = (dyldCacheAcceleratorRangeEntry*)((char*)accelInfo + accelInfo->rangeTableOffset()); + for (uint32_t i=0; i < accelInfo->rangeTableCount(); ++i) { + const dyldCacheAcceleratorRangeEntry& entry = rangeTable[i]; + printf(" 0x%llX -> 0x%llX %s\n", entry.startAddress(), entry.startAddress() + entry.size(), (char*)options.mappedCache + images[entry.imageIndex()].pathFileOffset()); + } + printf("dylib trie (size=%u):\n", accelInfo->dylibTrieSize()); + const uint8_t* dylibTrieStart = (uint8_t*)accelInfo + accelInfo->dylibTrieOffset(); + const uint8_t* dylibTrieEnd = dylibTrieStart + accelInfo->dylibTrieSize(); + std::vector dylibEntries; + if ( !Trie::parseTrie(dylibTrieStart, dylibTrieEnd, dylibEntries) ) + printf(" malformed dylibs trie\n"); + for (const DylibIndexTrie::Entry& x : dylibEntries) { + printf(" image[%3u] %s\n", x.info.index, x.name.c_str()); + } + } + } + } + else if ( options.mode == modeTextInfo ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( (header->mappingOffset() < sizeof(dyldCacheHeader)) || (header->imagesTextCount() == 0) ) { + printf("no text info\n"); + } + else { + const dyldCacheImageTextInfo* imagesText = (dyldCacheImageTextInfo*)((char*)options.mappedCache + header->imagesTextOffset()); + const dyldCacheImageTextInfo* imagesTextEnd = &imagesText[header->imagesTextCount()]; + printf("dylib text infos (count=%llu):\n", header->imagesTextCount()); + for (const dyldCacheImageTextInfo* p=imagesText; p < imagesTextEnd; ++p) { + printf(" 0x%09llX -> 0x%09llX <", p->loadAddress(), p->loadAddress() + p->textSegmentSize()); + for (int i=0; i<16; ++i) { + switch (i) { + case 4: + case 6: + case 8: + case 10: + printf("-"); + break; + } + printf("%02X", p->uuid()[i]); + } + printf("> %s\n", (char*)options.mappedCache + p->pathOffset()); + } + } + } + else if ( options.mode == modeLocalSymbols ) { + const dyldCacheHeader* header = (dyldCacheHeader*)options.mappedCache; + if ( header->localSymbolsOffset() == 0 ) { + fprintf(stderr, "Error: dyld shared cache does not contain local symbols info\n"); + exit(1); + } + const bool is64 = (strstr((char*)options.mappedCache, "64") != NULL); + const dyldCacheImageInfo* imageInfos = (dyldCacheImageInfo*)((char*)options.mappedCache + header->imagesOffset()); + const dyldCacheLocalSymbolsInfo* localsInfo = (dyldCacheLocalSymbolsInfo*)((char*)options.mappedCache + header->localSymbolsOffset()); + const uint32_t nlistFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->nlistOffset()); + const uint32_t nlistCount = localsInfo->nlistCount(); + const uint32_t nlistByteSize = is64 ? nlistCount*16 : nlistCount*12; + const uint32_t stringsFileOffset = (uint32_t)(header->localSymbolsOffset() + localsInfo->stringsOffset()); + const uint32_t stringsSize = localsInfo->stringsSize(); + const uint32_t entriesCount = localsInfo->entriesCount(); + const dyldCacheLocalSymbolEntry* entries = (dyldCacheLocalSymbolEntry*)((char*)localsInfo + localsInfo->entriesOffset()); + printf("local symbols nlist array: %3uMB, file offset: 0x%08X -> 0x%08X\n", nlistByteSize/(1024*1024), nlistFileOffset, nlistFileOffset+nlistByteSize); + printf("local symbols string pool: %3uMB, file offset: 0x%08X -> 0x%08X\n", stringsSize/(1024*1024), stringsFileOffset, stringsFileOffset+stringsSize); + printf("local symbols by dylib (count=%d):\n", entriesCount); + //const char* stringPool = (char*)options.mappedCache + stringsFileOffset; + for (int i=0; i < entriesCount; ++i) { + const char* imageName = (char*)options.mappedCache + imageInfos[i].pathFileOffset(); + printf(" nlistStartIndex=%5d, nlistCount=%5d, image=%s\n", entries[i].nlistStartIndex(), entries[i].nlistCount(), imageName); + #if 0 + if ( is64 ) { + const nlist_64* symTab = (nlist_64*)((char*)options.mappedCache + nlistFileOffset); + for (int e=0; e < entries[i].nlistCount(); ++e) { + const nlist_64* entry = &symTab[entries[i].nlistStartIndex()+e]; + printf(" nlist[%d].str=%d, %s\n", e, entry->n_un.n_strx, &stringPool[entry->n_un.n_strx]); + printf(" nlist[%d].value=0x%0llX\n", e, entry->n_value); + } + } + #endif + } + } + else if ( options.mode == modeExtract ) { + char pathBuffer[PATH_MAX]; + uint32_t bufferSize = PATH_MAX; + if ( _NSGetExecutablePath(pathBuffer, &bufferSize) != 0 ) { + fprintf(stderr, "Error: could not get path of program\n"); + return 1; + } + char* last = strrchr(pathBuffer, '/'); + strcpy(last+1, "../../lib/dsc_extractor.bundle"); + void* handle = dlopen(pathBuffer, RTLD_LAZY); + if ( handle == NULL ) { + fprintf(stderr, "Error: dsc_extractor.bundle could not be loaded at %s\n", pathBuffer); + return 1; + } + + typedef int (*extractor_proc)(const char* shared_cache_file_path, const char* extraction_root_path, + void (^progress)(unsigned current, unsigned total)); + + extractor_proc proc = (extractor_proc)dlsym(handle, "dyld_shared_cache_extract_dylibs_progress"); + if ( proc == NULL ) { + fprintf(stderr, "Error: dsc_extractor.bundle did not have dyld_shared_cache_extract_dylibs_progress symbol\n"); + return 1; + } + + int result = (*proc)(sharedCachePath, options.extractionDir, ^(unsigned c, unsigned total) { } ); + return result; + } + else { + segment_callback_t callback = nullptr; + if ( strcmp((char*)options.mappedCache, "dyld_v1 i386") == 0 ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strcmp((char*)options.mappedCache, "dyld_v1 x86_64") == 0) + || (strcmp((char*)options.mappedCache, "dyld_v1 x86_64h") == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strncmp((char*)options.mappedCache, "dyld_v1 armv", 14) == 0) + || (strncmp((char*)options.mappedCache, "dyld_v1 armv", 13) == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else if ( (strcmp((char*)options.mappedCache, "dyld_v1 arm64") == 0) + || (strcmp((char*)options.mappedCache, "dyld_v1 arm64e") == 0) ) { + switch ( options.mode ) { + case modeList: + callback = print_list; + break; + case modeMap: + callback = print_map; + break; + case modeDependencies: + callback = print_dependencies; + break; + case modeLinkEdit: + callback = process_linkedit; + break; + case modeSize: + callback = collect_size; + break; + case modeNone: + case modeInfo: + case modeSlideInfo: + case modeAcceleratorInfo: + case modeTextInfo: + case modeLocalSymbols: + case modeExtract: + break; + } + } + else { + fprintf(stderr, "Error: unrecognized dyld shared cache magic.\n"); + exit(1); + } + + __block Results results; + results.dependentTargetFound = false; + int iterateResult = dyld_shared_cache_iterate(options.mappedCache, (uint32_t)statbuf.st_size, + ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo ) { + (callback)(dylibInfo, segInfo, options, results); + }); + if ( iterateResult != 0 ) { + fprintf(stderr, "Error: malformed shared cache file\n"); + exit(1); + } + + if ( options.mode == modeLinkEdit ) { + // dump -linkedit information + for (std::map::iterator it = results.pageToContent.begin(); it != results.pageToContent.end(); ++it) { + printf("0x%08X %s\n", it->first, it->second); + } + } + else if ( options.mode == modeSize ) { + std::sort(results.textSegments.begin(), results.textSegments.end(), TextInfoSorter()); + for (std::vector::iterator it = results.textSegments.begin(); it != results.textSegments.end(); ++it) { + printf(" 0x%08llX %s\n", it->textSize, it->path); + } + } + + if ( (options.mode == modeDependencies) && options.dependentsOfPath && !results.dependentTargetFound) { + fprintf(stderr, "Error: could not find '%s' in the shared cache at\n %s\n", options.dependentsOfPath, sharedCachePath); + exit(1); + } + } + return 0; +} diff --git a/dyld/src/ImageLoader.cpp b/dyld/src/ImageLoader.cpp new file mode 100644 index 0000000..13117d5 --- /dev/null +++ b/dyld/src/ImageLoader.cpp @@ -0,0 +1,1345 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-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@ + */ + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageLoader.h" + + +uint32_t ImageLoader::fgImagesUsedFromSharedCache = 0; +uint32_t ImageLoader::fgImagesWithUsedPrebinding = 0; +uint32_t ImageLoader::fgImagesRequiringCoalescing = 0; +uint32_t ImageLoader::fgImagesHasWeakDefinitions = 0; +uint32_t ImageLoader::fgTotalRebaseFixups = 0; +uint32_t ImageLoader::fgTotalBindFixups = 0; +uint32_t ImageLoader::fgTotalBindSymbolsResolved = 0; +uint32_t ImageLoader::fgTotalBindImageSearches = 0; +uint32_t ImageLoader::fgTotalLazyBindFixups = 0; +uint32_t ImageLoader::fgTotalPossibleLazyBindFixups = 0; +uint32_t ImageLoader::fgTotalSegmentsMapped = 0; +uint64_t ImageLoader::fgTotalBytesMapped = 0; +uint64_t ImageLoader::fgTotalBytesPreFetched = 0; +uint64_t ImageLoader::fgTotalLoadLibrariesTime; +uint64_t ImageLoader::fgTotalObjCSetupTime = 0; +uint64_t ImageLoader::fgTotalDebuggerPausedTime = 0; +uint64_t ImageLoader::fgTotalRebindCacheTime = 0; +uint64_t ImageLoader::fgTotalRebaseTime; +uint64_t ImageLoader::fgTotalBindTime; +uint64_t ImageLoader::fgTotalWeakBindTime; +uint64_t ImageLoader::fgTotalDOF; +uint64_t ImageLoader::fgTotalInitTime; +uint16_t ImageLoader::fgLoadOrdinal = 0; +uint32_t ImageLoader::fgSymbolTrieSearchs = 0; +std::vectorImageLoader::fgInterposingTuples; +uintptr_t ImageLoader::fgNextPIEDylibAddress = 0; + + + +ImageLoader::ImageLoader(const char* path, unsigned int libCount) + : fPath(path), fRealPath(NULL), fDevice(0), fInode(0), fLastModified(0), + fPathHash(0), fDlopenReferenceCount(0), fInitializerRecursiveLock(NULL), + fDepth(0), fLoadOrder(fgLoadOrdinal++), fState(0), fLibraryCount(libCount), + fAllLibraryChecksumsAndLoadAddressesMatch(false), fLeaveMapped(false), fNeverUnload(false), + fHideSymbols(false), fMatchByInstallName(false), + fInterposed(false), fRegisteredDOF(false), fAllLazyPointersBound(false), + fBeingRemoved(false), fAddFuncNotified(false), + fPathOwnedByImage(false), fIsReferencedDownward(false), + fWeakSymbolsBound(false) +{ + if ( fPath != NULL ) + fPathHash = hash(fPath); + if ( libCount > 512 ) + dyld::throwf("too many dependent dylibs in %s", path); +} + + +void ImageLoader::deleteImage(ImageLoader* image) +{ + delete image; +} + + +ImageLoader::~ImageLoader() +{ + if ( fRealPath != NULL ) + delete [] fRealPath; + if ( fPathOwnedByImage && (fPath != NULL) ) + delete [] fPath; +} + +void ImageLoader::setFileInfo(dev_t device, ino_t inode, time_t modDate) +{ + fDevice = device; + fInode = inode; + fLastModified = modDate; +} + +void ImageLoader::setMapped(const LinkContext& context) +{ + fState = dyld_image_state_mapped; + context.notifySingle(dyld_image_state_mapped, this, NULL); // note: can throw exception +} + +int ImageLoader::compare(const ImageLoader* right) const +{ + if ( this->fDepth == right->fDepth ) { + if ( this->fLoadOrder == right->fLoadOrder ) + return 0; + else if ( this->fLoadOrder < right->fLoadOrder ) + return -1; + else + return 1; + } + else { + if ( this->fDepth < right->fDepth ) + return -1; + else + return 1; + } +} + +void ImageLoader::setPath(const char* path) +{ + if ( fPathOwnedByImage && (fPath != NULL) ) + delete [] fPath; + fPath = new char[strlen(path)+1]; + strcpy((char*)fPath, path); + fPathOwnedByImage = true; // delete fPath when this image is destructed + fPathHash = hash(fPath); + fRealPath = NULL; +} + +void ImageLoader::setPathUnowned(const char* path) +{ + if ( fPathOwnedByImage && (fPath != NULL) ) { + delete [] fPath; + } + fPath = path; + fPathOwnedByImage = false; + fPathHash = hash(fPath); +} + +void ImageLoader::setPaths(const char* path, const char* realPath) +{ + this->setPath(path); + fRealPath = new char[strlen(realPath)+1]; + strcpy((char*)fRealPath, realPath); +} + +const char* ImageLoader::getRealPath() const +{ + if ( fRealPath != NULL ) + return fRealPath; + else + return fPath; +} + + +uint32_t ImageLoader::hash(const char* path) +{ + // this does not need to be a great hash + // it is just used to reduce the number of strcmp() calls + // of existing images when loading a new image + uint32_t h = 0; + for (const char* s=path; *s != '\0'; ++s) + h = h*5 + *s; + return h; +} + +bool ImageLoader::matchInstallPath() const +{ + return fMatchByInstallName; +} + +void ImageLoader::setMatchInstallPath(bool match) +{ + fMatchByInstallName = match; +} + +bool ImageLoader::statMatch(const struct stat& stat_buf) const +{ + return ( (this->fDevice == stat_buf.st_dev) && (this->fInode == stat_buf.st_ino) ); +} + +const char* ImageLoader::shortName(const char* fullName) +{ + // try to return leaf name + if ( fullName != NULL ) { + const char* s = strrchr(fullName, '/'); + if ( s != NULL ) + return &s[1]; + } + return fullName; +} + +const char* ImageLoader::getShortName() const +{ + return shortName(fPath); +} + +void ImageLoader::setLeaveMapped() +{ + fLeaveMapped = true; +} + +void ImageLoader::setHideExports(bool hide) +{ + fHideSymbols = hide; +} + +bool ImageLoader::hasHiddenExports() const +{ + return fHideSymbols; +} + +bool ImageLoader::isLinked() const +{ + return (fState >= dyld_image_state_bound); +} + +time_t ImageLoader::lastModified() const +{ + return fLastModified; +} + +bool ImageLoader::containsAddress(const void* addr) const +{ + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uint8_t* start = (const uint8_t*)segActualLoadAddress(i); + const uint8_t* end = (const uint8_t*)segActualEndAddress(i); + if ( (start <= addr) && (addr < end) && !segUnaccessible(i) ) + return true; + } + return false; +} + +bool ImageLoader::overlapsWithAddressRange(const void* start, const void* end) const +{ + 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 true; + if ( (segStart < start) && (end < segEnd) ) + return true; + } + return false; +} + +void ImageLoader::getMappedRegions(MappedRegion*& regions) const +{ + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + MappedRegion region; + region.address = segActualLoadAddress(i); + region.size = segSize(i); + *regions++ = region; + } +} + + + +bool ImageLoader::dependsOn(ImageLoader* image) { + for(unsigned int i=0; i < libraryCount(); ++i) { + if ( libImage(i) == image ) + return true; + } + return false; +} + + +static bool notInImgageList(const ImageLoader* image, const ImageLoader** dsiStart, const ImageLoader** dsiCur) +{ + for (const ImageLoader** p = dsiStart; p < dsiCur; ++p) + if ( *p == image ) + return false; + return true; +} + +bool ImageLoader::findExportedSymbolAddress(const LinkContext& context, const char* symbolName, + const ImageLoader* requestorImage, int requestorOrdinalOfDef, + bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const +{ + const Symbol* sym = this->findExportedSymbol(symbolName, true, foundIn); + if ( sym != NULL ) { + *address = (*foundIn)->getExportedSymbolAddress(sym, context, requestorImage, runResolver); + return true; + } + return false; +} + + +// private method that handles circular dependencies by only search any image once +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImagesExcept(const char* name, + const ImageLoader** dsiStart, const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const +{ + const ImageLoader::Symbol* sym; + // search self + if ( notInImgageList(this, dsiStart, dsiCur) ) { + sym = this->findExportedSymbol(name, false, this->getPath(), foundIn); + if ( sym != NULL ) + return sym; + *dsiCur++ = this; + } + + // search directly dependent libraries + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { + sym = dependentImage->findExportedSymbol(name, false, libPath(i), foundIn); + if ( sym != NULL ) + return sym; + } + } + + // search indirectly dependent libraries + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( (dependentImage != NULL) && notInImgageList(dependentImage, dsiStart, dsiCur) ) { + *dsiCur++ = dependentImage; + sym = dependentImage->findExportedSymbolInDependentImagesExcept(name, dsiStart, dsiCur, dsiEnd, foundIn); + if ( sym != NULL ) + return sym; + } + } + + return NULL; +} + + +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const +{ + unsigned int imageCount = context.imageCount()+2; + const ImageLoader* dontSearchImages[imageCount]; + dontSearchImages[0] = this; // don't search this image + const ImageLoader** cur = &dontSearchImages[1]; + return this->findExportedSymbolInDependentImagesExcept(name, &dontSearchImages[0], cur, &dontSearchImages[imageCount], foundIn); +} + +const ImageLoader::Symbol* ImageLoader::findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const +{ + unsigned int imageCount = context.imageCount()+2; + const ImageLoader* dontSearchImages[imageCount]; + const ImageLoader** cur = &dontSearchImages[0]; + 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); +} + + +uintptr_t ImageLoader::interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* inImage, const ImageLoader* onlyInImage) +{ + //dyld::log("interposedAddress(0x%08llX), tupleCount=%lu\n", (uint64_t)address, fgInterposingTuples.size()); + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + //dyld::log(" interposedAddress: replacee=0x%08llX, replacement=0x%08llX, neverImage=%p, onlyImage=%p, inImage=%p\n", + // (uint64_t)it->replacee, (uint64_t)it->replacement, it->neverImage, it->onlyImage, inImage); + // replace all references to 'replacee' with 'replacement' + if ( (address == it->replacee) && (inImage != it->neverImage) && ((it->onlyImage == NULL) || (inImage == it->onlyImage)) ) { + if ( context.verboseInterposing ) { + dyld::log("dyld interposing: replace 0x%lX with 0x%lX\n", it->replacee, it->replacement); + } + return it->replacement; + } + } + return address; +} + +void ImageLoader::addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count) +{ + for(size_t i=0; i < count; ++i) { + ImageLoader::InterposeTuple tuple; + tuple.replacement = (uintptr_t)array[i].replacement; + tuple.neverImage = NULL; + tuple.onlyImage = this; + tuple.replacee = (uintptr_t)array[i].replacee; + // chain to any existing interpositions + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + if ( (it->replacee == tuple.replacee) && (it->onlyImage == this) ) { + tuple.replacee = it->replacement; + } + } + ImageLoader::fgInterposingTuples.push_back(tuple); + } +} + + +void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath) +{ + //dyld::log("ImageLoader::link(%s) refCount=%d, neverUnload=%d\n", imagePath, fDlopenReferenceCount, fNeverUnload); + + // clear error strings + (*context.setErrorStrings)(0, NULL, NULL, NULL); + + uint64_t t0 = mach_absolute_time(); + this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath); + context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly); + + // we only do the loading step for preflights + if ( preflightOnly ) + return; + + uint64_t t1 = mach_absolute_time(); + context.clearAllDepths(); + this->recursiveUpdateDepth(context.imageCount()); + + uint64_t t2 = mach_absolute_time(); + this->recursiveRebase(context); + context.notifyBatch(dyld_image_state_rebased, false); + + uint64_t t3 = mach_absolute_time(); + this->recursiveBind(context, forceLazysBound, neverUnload); + + uint64_t t4 = mach_absolute_time(); + if ( !context.linkingMainExecutable ) + this->weakBind(context); + uint64_t t5 = mach_absolute_time(); + + context.notifyBatch(dyld_image_state_bound, false); + uint64_t t6 = mach_absolute_time(); + + std::vector dofs; + this->recursiveGetDOFSections(context, dofs); + context.registerDOFs(dofs); + 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)(0, NULL, NULL, NULL); + + fgTotalLoadLibrariesTime += t1 - t0; + fgTotalRebaseTime += t3 - t2; + fgTotalBindTime += t4 - t3; + fgTotalWeakBindTime += t5 - t4; + fgTotalDOF += t7 - t6; + + // done with initial dylib loads + fgNextPIEDylibAddress = 0; +} + + +void ImageLoader::printReferenceCounts() +{ + dyld::log(" dlopen=%d for %s\n", fDlopenReferenceCount, getPath() ); +} + + +bool ImageLoader::decrementDlopenReferenceCount() +{ + if ( fDlopenReferenceCount == 0 ) + return true; + --fDlopenReferenceCount; + return false; +} + + +// upward dylib initializers can be run too soon +// To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs +// have their initialization postponed until after the recursion through downward dylibs +// has completed. +void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread, + InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images) +{ + uint32_t maxImageCount = context.imageCount()+2; + ImageLoader::UninitedUpwards upsBuffer[maxImageCount]; + ImageLoader::UninitedUpwards& ups = upsBuffer[0]; + ups.count = 0; + // Calling recursive init on all images in images list, building a new list of + // uninitialized upward dependencies. + for (uintptr_t i=0; i < images.count; ++i) { + images.images[i]->recursiveInitialization(context, thisThread, images.images[i]->getPath(), timingInfo, ups); + } + // If any upward dependencies remain, init them. + if ( ups.count > 0 ) + processInitializers(context, thisThread, timingInfo, ups); +} + + +void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) +{ + uint64_t t1 = mach_absolute_time(); + mach_port_t thisThread = mach_thread_self(); + ImageLoader::UninitedUpwards up; + up.count = 1; + up.images[0] = this; + processInitializers(context, thisThread, timingInfo, up); + context.notifyBatch(dyld_image_state_initialized, false); + mach_port_deallocate(mach_task_self(), thisThread); + uint64_t t2 = mach_absolute_time(); + fgTotalInitTime += (t2 - t1); +} + + +void ImageLoader::bindAllLazyPointers(const LinkContext& context, bool recursive) +{ + if ( ! fAllLazyPointersBound ) { + fAllLazyPointersBound = true; + + if ( recursive ) { + // bind lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->bindAllLazyPointers(context, recursive); + } + } + // bind lazies in this image + this->doBindJustLazies(context); + } +} + + +bool ImageLoader::allDependentLibrariesAsWhenPreBound() const +{ + return fAllLibraryChecksumsAndLoadAddressesMatch; +} + + +void ImageLoader::markedUsedRecursive(const std::vector& dynamicReferences) +{ + // already visited here + if ( fMarkedInUse ) + return; + fMarkedInUse = true; + + // clear mark on all statically dependent dylibs + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + dependentImage->markedUsedRecursive(dynamicReferences); + } + } + + // clear mark on all dynamically dependent dylibs + for (std::vector::const_iterator it=dynamicReferences.begin(); it != dynamicReferences.end(); ++it) { + if ( it->from == this ) + it->to->markedUsedRecursive(dynamicReferences); + } + +} + +unsigned int ImageLoader::recursiveUpdateDepth(unsigned int maxDepth) +{ + // the purpose of this phase is to make the images sortable such that + // in a sort list of images, every image that an image depends on + // occurs in the list before it. + if ( fDepth == 0 ) { + // break cycles + fDepth = maxDepth; + + // get depth of dependents + unsigned int minDependentDepth = maxDepth; + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( (dependentImage != NULL) && !libIsUpward(i) ) { + unsigned int d = dependentImage->recursiveUpdateDepth(maxDepth); + if ( d < minDependentDepth ) + minDependentDepth = d; + } + } + + // make me less deep then all my dependents + fDepth = minDependentDepth - 1; + } + + return fDepth; +} + + +void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath) +{ + if ( fState < dyld_image_state_dependents_mapped ) { + // break cycles + fState = dyld_image_state_dependents_mapped; + + // get list of libraries this image needs + DependentLibraryInfo libraryInfos[fLibraryCount]; + this->doGetDependentLibraries(libraryInfos); + + // get list of rpaths that this image adds + std::vector rpathsFromThisImage; + this->getRPaths(context, rpathsFromThisImage); + const RPathChain thisRPaths(&loaderRPaths, &rpathsFromThisImage); + + // try to load each + bool canUsePrelinkingInfo = true; + for(unsigned int i=0; i < fLibraryCount; ++i){ + ImageLoader* dependentLib; + bool depLibReExported = false; + bool depLibRequired = false; + bool depLibCheckSumsMatch = false; + DependentLibraryInfo& requiredLibInfo = libraryInfos[i]; + if ( preflightOnly && context.inSharedCache(requiredLibInfo.name) ) { + // dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized + // in preflight mode, don't even load dylib that are in the shared cache because they will never be unloaded + setLibImage(i, NULL, false, false); + continue; + } + try { + unsigned cacheIndex; + dependentLib = context.loadLibrary(requiredLibInfo.name, true, this->getPath(), &thisRPaths, cacheIndex); + if ( dependentLib == this ) { + // found circular reference, perhaps DYLD_LIBARY_PATH is causing this rdar://problem/3684168 + dependentLib = context.loadLibrary(requiredLibInfo.name, false, NULL, NULL, cacheIndex); + if ( dependentLib != this ) + dyld::warn("DYLD_ setting caused circular dependency in %s\n", this->getPath()); + } + if ( fNeverUnload ) + dependentLib->setNeverUnload(); + if ( requiredLibInfo.upward ) { + } + else { + dependentLib->fIsReferencedDownward = true; + } + LibraryInfo actualInfo = dependentLib->doGetLibraryInfo(requiredLibInfo.info); + depLibRequired = requiredLibInfo.required; + depLibCheckSumsMatch = ( actualInfo.checksum == requiredLibInfo.info.checksum ); + depLibReExported = requiredLibInfo.reExported; + if ( ! depLibReExported ) { + // for pre-10.5 binaries that did not use LC_REEXPORT_DYLIB + depLibReExported = dependentLib->isSubframeworkOf(context, this) || this->hasSubLibrary(context, dependentLib); + } + // check found library version is compatible + // 0xFFFFFFFF is wildcard that matches any version + if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (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 + //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", + // requiredLibInfo.info.checksum, actualInfo.checksum, this->getPath(), dependentLib->getPath()); + // if ( dependentLib->getSlide() != 0 ) + // fprintf(stderr, "dyld: dependent library slid for %s referencing %s\n", this->getPath(), dependentLib->getPath()); + //} + } + catch (const char* msg) { + //if ( context.verbosePrebinding ) + // fprintf(stderr, "dyld: exception during processing for %s referencing %s\n", this->getPath(), dependentLib->getPath()); + if ( requiredLibInfo.required ) { + fState = dyld_image_state_mapped; + // record values for possible use by CrashReporter or Finder + if ( strstr(msg, "Incompatible library version") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_VERSION, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "architecture") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_WRONG_ARCH, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "file system sandbox") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_FILE_SYSTEM_SANDBOX, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "code signature") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_CODE_SIGNATURE, this->getPath(), requiredLibInfo.name, NULL); + else if ( strstr(msg, "malformed") != NULL ) + (*context.setErrorStrings)(DYLD_EXIT_REASON_MALFORMED_MACHO, this->getPath(), requiredLibInfo.name, NULL); + else + (*context.setErrorStrings)(DYLD_EXIT_REASON_DYLIB_MISSING, this->getPath(), requiredLibInfo.name, NULL); + const char* newMsg = dyld::mkstringf("Library not loaded: %s\n Referenced from: %s\n Reason: %s", requiredLibInfo.name, this->getRealPath(), msg); + free((void*)msg); // our free() will do nothing if msg is a string literal + throw newMsg; + } + free((void*)msg); // our free() will do nothing if msg is a string literal + // ok if weak library not found + dependentLib = NULL; + canUsePrelinkingInfo = false; // this disables all prebinding, we may want to just slam import vectors for this lib to zero + } + setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward); + } + fAllLibraryChecksumsAndLoadAddressesMatch = canUsePrelinkingInfo; + + // tell each to load its dependents + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths, libraryInfos[i].name); + } + } + + // do deep prebind check + if ( fAllLibraryChecksumsAndLoadAddressesMatch ) { + for(unsigned int i=0; i < libraryCount(); ++i){ + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + if ( !dependentImage->allDependentLibrariesAsWhenPreBound() ) + fAllLibraryChecksumsAndLoadAddressesMatch = false; + } + } + } + + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromThisImage.begin(); it != rpathsFromThisImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + + } +} + +void ImageLoader::recursiveRebase(const LinkContext& context) +{ + if ( fState < dyld_image_state_rebased ) { + // break cycles + fState = dyld_image_state_rebased; + + try { + // rebase lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveRebase(context); + } + + // rebase this image + doRebase(context); + + // notify + context.notifySingle(dyld_image_state_rebased, this, NULL); + } + 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; + } + } +} + + + +void ImageLoader::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload) +{ + // Normally just non-lazy pointers are bound immediately. + // The exceptions are: + // 1) DYLD_BIND_AT_LAUNCH will cause lazy pointers to be bound immediately + // 2) some API's (e.g. RTLD_NOW) can cause lazy pointers to be bound immediately + if ( fState < dyld_image_state_bound ) { + // break cycles + fState = dyld_image_state_bound; + + try { + // bind lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveBind(context, forceLazysBound, neverUnload); + } + // bind this image + this->doBind(context, forceLazysBound); + // mark if lazys are also bound + if ( forceLazysBound || this->usablePrebinding(context) ) + fAllLazyPointersBound = true; + // mark as never-unload if requested + if ( neverUnload ) + this->setNeverUnload(); + + context.notifySingle(dyld_image_state_bound, this, NULL); + } + catch (const char* msg) { + // restore state + fState = dyld_image_state_rebased; + CRSetCrashLogMessage2(NULL); + throw; + } + } +} + +void ImageLoader::weakBind(const LinkContext& context) +{ + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind start:\n"); + uint64_t t1 = mach_absolute_time(); + // get set of ImageLoaders that participate in coalecsing + ImageLoader* imagesNeedingCoalescing[fgImagesRequiringCoalescing]; + unsigned imageIndexes[fgImagesRequiringCoalescing]; + int count = context.getCoalescedImages(imagesNeedingCoalescing, imageIndexes); + + // count how many have not already had weakbinding done + int countNotYetWeakBound = 0; + int countOfImagesWithWeakDefinitionsNotInSharedCache = 0; + for(int i=0; i < count; ++i) { + if ( ! imagesNeedingCoalescing[i]->weakSymbolsBound(imageIndexes[i]) ) + ++countNotYetWeakBound; + 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 ( (countOfImagesWithWeakDefinitionsNotInSharedCache > 0) && (countNotYetWeakBound > 0) ) { + // make symbol iterators for each + ImageLoader::CoalIterator iterators[count]; + ImageLoader::CoalIterator* sortedIts[count]; + for(int i=0; i < count; ++i) { + imagesNeedingCoalescing[i]->initializeCoalIterator(iterators[i], i, imageIndexes[i]); + sortedIts[i] = &iterators[i]; + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind load order %d/%d for %s\n", i, count, imagesNeedingCoalescing[i]->getIndexedPath(imageIndexes[i])); + } + + // walk all symbols keeping iterators in sync by + // only ever incrementing the iterator with the lowest symbol + int doneCount = 0; + while ( doneCount != count ) { + //for(int i=0; i < count; ++i) + // dyld::log("sym[%d]=%s ", sortedIts[i]->loadOrder, sortedIts[i]->symbolName); + //dyld::log("\n"); + // increment iterator with lowest symbol + if ( sortedIts[0]->image->incrementCoalIterator(*sortedIts[0]) ) + ++doneCount; + // re-sort iterators + for(int i=1; i < count; ++i) { + int result = strcmp(sortedIts[i-1]->symbolName, sortedIts[i]->symbolName); + if ( result == 0 ) + sortedIts[i-1]->symbolMatches = true; + if ( result > 0 ) { + // new one is bigger then next, so swap + ImageLoader::CoalIterator* temp = sortedIts[i-1]; + sortedIts[i-1] = sortedIts[i]; + sortedIts[i] = temp; + } + if ( result < 0 ) + break; + } + // process all matching symbols just before incrementing the lowest one that matches + if ( sortedIts[0]->symbolMatches && !sortedIts[0]->done ) { + const char* nameToCoalesce = sortedIts[0]->symbolName; + // pick first symbol in load order (and non-weak overrides weak) + uintptr_t targetAddr = 0; + ImageLoader* targetImage = NULL; + unsigned targetImageIndex = 0; + for(int i=0; i < count; ++i) { + if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind, found %s weak=%d in %s \n", nameToCoalesce, iterators[i].weakSymbol, iterators[i].image->getIndexedPath((unsigned)iterators[i].imageIndex)); + if ( iterators[i].weakSymbol ) { + if ( targetAddr == 0 ) { + targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); + if ( targetAddr != 0 ) { + targetImage = iterators[i].image; + targetImageIndex = (unsigned)iterators[i].imageIndex; + } + } + } + else { + targetAddr = iterators[i].image->getAddressCoalIterator(iterators[i], context); + if ( targetAddr != 0 ) { + targetImage = iterators[i].image; + targetImageIndex = (unsigned)iterators[i].imageIndex; + // strong implementation found, stop searching + break; + } + } + } + } + // tell each to bind to this symbol (unless already bound) + if ( targetAddr != 0 ) { + if ( context.verboseWeakBind ) { + dyld::log("dyld: weak binding all uses of %s to copy from %s\n", + nameToCoalesce, targetImage->getIndexedShortName(targetImageIndex)); + } + for(int i=0; i < count; ++i) { + if ( strcmp(iterators[i].symbolName, nameToCoalesce) == 0 ) { + if ( context.verboseWeakBind ) { + dyld::log("dyld: weak bind, setting all uses of %s in %s to 0x%lX from %s\n", + nameToCoalesce, iterators[i].image->getIndexedShortName((unsigned)iterators[i].imageIndex), + targetAddr, targetImage->getIndexedShortName(targetImageIndex)); + } + if ( ! iterators[i].image->weakSymbolsBound(imageIndexes[i]) ) + iterators[i].image->updateUsesCoalIterator(iterators[i], targetAddr, targetImage, targetImageIndex, context); + iterators[i].symbolMatches = false; + } + } + } + + } + } + + // mark all as having all weak symbols bound + for(int i=0; i < count; ++i) { + imagesNeedingCoalescing[i]->setWeakSymbolsBound(imageIndexes[i]); + } + } + uint64_t t2 = mach_absolute_time(); + fgTotalWeakBindTime += t2 - t1; + + if ( context.verboseWeakBind ) + dyld::log("dyld: weak bind end\n"); +} + + + +void ImageLoader::recursiveGetDOFSections(const LinkContext& context, std::vector& dofs) +{ + if ( ! fRegisteredDOF ) { + // break cycles + fRegisteredDOF = true; + + // gather lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->recursiveGetDOFSections(context, dofs); + } + this->doGetDOFSections(context, dofs); + } +} + +void ImageLoader::setNeverUnloadRecursive() { + if ( ! fNeverUnload ) { + // break cycles + fNeverUnload = true; + + // gather lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) + dependentImage->setNeverUnloadRecursive(); + } + } +} + +void ImageLoader::recursiveSpinLock(recursive_lock& rlock) +{ + // try to set image's ivar fInitializerRecursiveLock to point to this lock_info + // keep trying until success (spin) + while ( ! OSAtomicCompareAndSwapPtrBarrier(NULL, &rlock, (void**)&fInitializerRecursiveLock) ) { + // if fInitializerRecursiveLock already points to a different lock_info, if it is for + // the same thread we are on, the increment the lock count, otherwise continue to spin + if ( (fInitializerRecursiveLock != NULL) && (fInitializerRecursiveLock->thread == rlock.thread) ) + break; + } + ++(fInitializerRecursiveLock->count); +} + +void ImageLoader::recursiveSpinUnLock() +{ + if ( --(fInitializerRecursiveLock->count) == 0 ) + fInitializerRecursiveLock = NULL; +} + +void ImageLoader::InitializerTimingList::addTime(const char* name, uint64_t time) +{ + for (int i=0; i < count; ++i) { + if ( strcmp(images[i].shortName, name) == 0 ) { + images[i].initTime += time; + return; + } + } + images[count].initTime = time; + images[count].shortName = name; + ++count; +} + +void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, + InitializerTimingList& timingInfo, UninitedUpwards& uninitUps) +{ + recursive_lock lock_info(this_thread); + recursiveSpinLock(lock_info); + + if ( fState < dyld_image_state_dependents_initialized-1 ) { + uint8_t oldState = fState; + // break cycles + fState = dyld_image_state_dependents_initialized-1; + try { + // initialize lower level libraries first + for(unsigned int i=0; i < libraryCount(); ++i) { + ImageLoader* dependentImage = libImage(i); + if ( dependentImage != NULL ) { + // don't try to initialize stuff "above" me yet + if ( libIsUpward(i) ) { + uninitUps.images[uninitUps.count] = dependentImage; + uninitUps.count++; + } + else if ( dependentImage->fDepth >= fDepth ) { + dependentImage->recursiveInitialization(context, this_thread, libPath(i), timingInfo, uninitUps); + } + } + } + + // record termination order + if ( this->needsTermination() ) + context.terminationRecorder(this); + + // let objc know we are about to initialize this image + uint64_t t1 = mach_absolute_time(); + fState = dyld_image_state_dependents_initialized; + oldState = fState; + context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo); + + // initialize this image + bool hasInitializers = this->doInitialization(context); + + // let anyone know we finished initializing this image + fState = dyld_image_state_initialized; + oldState = fState; + context.notifySingle(dyld_image_state_initialized, this, NULL); + + if ( hasInitializers ) { + uint64_t t2 = mach_absolute_time(); + timingInfo.addTime(this->getShortName(), t2-t1); + } + } + catch (const char* msg) { + // this image is not initialized + fState = oldState; + recursiveSpinUnLock(); + throw; + } + } + + recursiveSpinUnLock(); +} + + +static void printTime(const char* msg, uint64_t partTime, uint64_t totalTime) +{ + static uint64_t sUnitsPerSecond = 0; + if ( sUnitsPerSecond == 0 ) { + struct mach_timebase_info timeBaseInfo; + if ( mach_timebase_info(&timeBaseInfo) == KERN_SUCCESS ) { + sUnitsPerSecond = 1000000000ULL * timeBaseInfo.denom / timeBaseInfo.numer; + } + } + if ( partTime < sUnitsPerSecond ) { + uint32_t milliSecondsTimesHundred = (uint32_t)((partTime*100000)/sUnitsPerSecond); + uint32_t milliSeconds = (uint32_t)(milliSecondsTimesHundred/100); + uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); + uint32_t percent = percentTimesTen/10; + if ( milliSeconds >= 100 ) + dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); + else if ( milliSeconds >= 10 ) + dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); + else + dyld::log("%s: %u.%02u milliseconds (%u.%u%%)\n", msg, milliSeconds, milliSecondsTimesHundred-milliSeconds*100, percent, percentTimesTen-percent*10); + } + else { + uint32_t secondsTimeTen = (uint32_t)((partTime*10)/sUnitsPerSecond); + uint32_t seconds = secondsTimeTen/10; + uint32_t percentTimesTen = (uint32_t)((partTime*1000)/totalTime); + uint32_t percent = percentTimesTen/10; + dyld::log("%s: %u.%u seconds (%u.%u%%)\n", msg, seconds, secondsTimeTen-seconds*10, percent, percentTimesTen-percent*10); + } +} + +static char* commatize(uint64_t in, char* out) +{ + uint64_t div10 = in / 10; + uint8_t delta = in - div10*10; + char* s = &out[32]; + int digitCount = 1; + *s = '\0'; + *(--s) = '0' + delta; + in = div10; + while ( in != 0 ) { + if ( (digitCount % 3) == 0 ) + *(--s) = ','; + div10 = in / 10; + delta = in - div10*10; + *(--s) = '0' + delta; + in = div10; + ++digitCount; + } + return s; +} + + +void ImageLoader::printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo) +{ + uint64_t totalTime = fgTotalLoadLibrariesTime + fgTotalRebaseTime + fgTotalBindTime + fgTotalWeakBindTime + fgTotalDOF + fgTotalInitTime; + + uint64_t totalDyldTime = totalTime - fgTotalDebuggerPausedTime - fgTotalRebindCacheTime; + printTime("Total pre-main time", totalDyldTime, totalDyldTime); + printTime(" dylib loading time", fgTotalLoadLibrariesTime-fgTotalDebuggerPausedTime, totalDyldTime); + printTime(" rebase/binding time", fgTotalRebaseTime+fgTotalBindTime+fgTotalWeakBindTime-fgTotalRebindCacheTime, totalDyldTime); + printTime(" ObjC setup time", fgTotalObjCSetupTime, totalDyldTime); + printTime(" initializer time", fgTotalInitTime-fgTotalObjCSetupTime, totalDyldTime); + dyld::log(" slowest intializers :\n"); + for (uintptr_t i=0; i < timingInfo.count; ++i) { + uint64_t t = timingInfo.images[i].initTime; + if ( t*50 < totalDyldTime ) + continue; + dyld::log("%30s ", timingInfo.images[i].shortName); + if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 ) + t -= fgTotalObjCSetupTime; + printTime("", t, totalDyldTime); + } + dyld::log("\n"); +} + +void ImageLoader::printStatisticsDetails(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)\n", imageCount, fgImagesUsedFromSharedCache); + 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 load time in ObjC", fgTotalObjCSetupTime, totalTime); + printTime(" total debugger pause time", fgTotalDebuggerPausedTime, totalTime); + printTime(" total dtrace DOF registration time", fgTotalDOF, totalTime); + dyld::log(" total rebase fixups: %s\n", commatize(fgTotalRebaseFixups, commaNum1)); + printTime(" total rebase fixups time", fgTotalRebaseTime, totalTime); + dyld::log(" total binding fixups: %s\n", commatize(fgTotalBindFixups, commaNum1)); + if ( fgTotalBindSymbolsResolved != 0 ) { + uint32_t avgTimesTen = (fgTotalBindImageSearches * 10) / fgTotalBindSymbolsResolved; + uint32_t avgInt = fgTotalBindImageSearches / fgTotalBindSymbolsResolved; + uint32_t avgTenths = avgTimesTen - (avgInt*10); + dyld::log("total binding symbol lookups: %s, average images searched per symbol: %u.%u\n", + commatize(fgTotalBindSymbolsResolved, commaNum1), avgInt, avgTenths); + } + printTime(" total binding fixups time", fgTotalBindTime, totalTime); + printTime(" total weak binding fixups time", fgTotalWeakBindTime, totalTime); + printTime(" total redo shared cached bindings time", fgTotalRebindCacheTime, totalTime); + dyld::log(" total bindings lazily fixed up: %s of %s\n", commatize(fgTotalLazyBindFixups, commaNum1), commatize(fgTotalPossibleLazyBindFixups, commaNum2)); + printTime(" total time in initializers and ObjC +load", fgTotalInitTime-fgTotalObjCSetupTime, totalTime); + for (uintptr_t i=0; i < timingInfo.count; ++i) { + uint64_t t = timingInfo.images[i].initTime; + if ( t*1000 < totalTime ) + continue; + dyld::log("%42s ", timingInfo.images[i].shortName); + if ( strncmp(timingInfo.images[i].shortName, "libSystem.", 10) == 0 ) + t -= fgTotalObjCSetupTime; + printTime("", t, totalTime); + } + +} + + +// +// copy path and add suffix to result +// +// /path/foo.dylib _debug => /path/foo_debug.dylib +// foo.dylib _debug => foo_debug.dylib +// foo _debug => foo_debug +// /path/bar _debug => /path/bar_debug +// /path/bar.A.dylib _debug => /path/bar.A_debug.dylib +// +void ImageLoader::addSuffix(const char* path, const char* suffix, char* result) +{ + strcpy(result, path); + + char* start = strrchr(result, '/'); + if ( start != NULL ) + start++; + else + start = result; + + char* dot = strrchr(start, '.'); + if ( dot != NULL ) { + strcpy(dot, suffix); + strcat(&dot[strlen(suffix)], &path[dot-result]); + } + else { + strcat(result, suffix); + } +} + + +// +// 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* ImageLoader::trieWalk(const uint8_t* start, const uint8_t* end, const char* s) +{ + //dyld::log("trieWalk(%p, %p, %s)\n", start, end, s); + ++fgSymbolTrieSearchs; + const uint8_t* p = start; + while ( p != NULL ) { + uintptr_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) ) { + //dyld::log("trieWalk(%p) returning %p\n", start, p); + return p; + } + const uint8_t* children = p + terminalSize; + if ( children > end ) { + dyld::log("trieWalk() malformed trie node, terminalSize=0x%lx extends past end of trie\n", terminalSize); + return NULL; + } + //dyld::log("trieWalk(%p) sym=%s, terminalSize=%lu, children=%p\n", start, s, terminalSize, children); + uint8_t childrenRemaining = *children++; + p = children; + uintptr_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; + // scan whole edge to get to next edge + // if edge is longer than target symbol name, don't read past end of symbol name + char c = *p; + while ( c != '\0' ) { + if ( !wrongEdge ) { + if ( c != *ss ) + wrongEdge = true; + ++ss; + } + ++p; + c = *p; + } + if ( wrongEdge ) { + // advance to next child + ++p; // skip over zero terminator + // skip over uleb128 until last byte is found + while ( (*p & 0x80) != 0 ) + ++p; + ++p; // skip over last byte of uleb128 + if ( p > end ) { + dyld::log("trieWalk() malformed trie node, child node extends past end of trie\n"); + return NULL; + } + } + else { + // the symbol so far matches this edge (child) + // so advance to the child's node + ++p; + nodeOffset = read_uleb128(p, end); + if ( (nodeOffset == 0) || ( &start[nodeOffset] > end) ) { + dyld::log("trieWalk() malformed trie child, nodeOffset=0x%lx out of range\n", nodeOffset); + return NULL; + } + s = ss; + //dyld::log("trieWalk() found matching edge advancing to node 0x%lx\n", nodeOffset); + break; + } + } + if ( nodeOffset != 0 ) + p = &start[nodeOffset]; + else + p = NULL; + } + //dyld::log("trieWalk(%p) return NULL\n", start); + return NULL; +} + + + +uintptr_t ImageLoader::read_uleb128(const uint8_t*& p, const uint8_t* end) +{ + uint64_t result = 0; + int bit = 0; + do { + if (p == end) + dyld::throwf("malformed uleb128"); + + uint64_t slice = *p & 0x7f; + + if (bit > 63) + dyld::throwf("uleb128 too big for uint64, bit=%d, result=0x%0llX", bit, result); + else { + result |= (slice << bit); + bit += 7; + } + } while (*p++ & 0x80); + return (uintptr_t)result; +} + + +intptr_t ImageLoader::read_sleb128(const uint8_t*& p, const uint8_t* end) +{ + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == end) + throw "malformed sleb128"; + byte = *p++; + result |= (((int64_t)(byte & 0x7f)) << bit); + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ( (byte & 0x40) != 0 ) + result |= (-1LL) << bit; + return (intptr_t)result; +} + + +VECTOR_NEVER_DESTRUCTED_IMPL(ImageLoader::InterposeTuple); +VECTOR_NEVER_DESTRUCTED_IMPL(ImagePair); + + + diff --git a/dyld/src/ImageLoader.h b/dyld/src/ImageLoader.h new file mode 100644 index 0000000..fd70e2d --- /dev/null +++ b/dyld/src/ImageLoader.h @@ -0,0 +1,869 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-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 __IMAGELOADER__ +#define __IMAGELOADER__ + +#include +#include +#include +#include // struct mach_timebase_info +#include // struct mach_thread_self +#include +#include +#include +#include +#include +#include +#include +#include + +#if __arm__ + #include +#endif + +#if __x86_64__ || __i386__ + #include +#else + // work around until iOS has CrashReporterClient.h + #define CRSetCrashLogMessage(x) + #define CRSetCrashLogMessage2(x) +#endif + +#ifndef SHARED_REGION_BASE_ARM64 + #define SHARED_REGION_BASE_ARM64 0x7FFF80000000LL +#endif + +#ifndef SHARED_REGION_SIZE_ARM64 + #define SHARED_REGION_SIZE_ARM64 0x10000000LL +#endif + + +#define LOG_BINDINGS 0 + +#include "mach-o/dyld_images.h" +#include "mach-o/dyld_priv.h" + +#if __i386__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_I386 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_I386 +#elif __x86_64__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_X86_64 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_X86_64 +#elif __arm__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_ARM + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM +#elif __arm64__ + #define SHARED_REGION_BASE SHARED_REGION_BASE_ARM64 + #define SHARED_REGION_SIZE SHARED_REGION_SIZE_ARM64 +#endif + +#ifndef EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER + #define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 +#endif +#ifndef EXPORT_SYMBOL_FLAGS_REEXPORT + #define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#endif + +#ifndef LC_MAIN + #define LC_MAIN (0x28|LC_REQ_DYLD) /* replacement for LC_UNIXTHREAD */ + struct entry_point_command { + uint32_t cmd; /* LC_MAIN only used in MH_EXECUTE filetypes */ + uint32_t cmdsize; /* 24 */ + uint64_t entryoff; /* file (__TEXT) offset of main() */ + uint64_t stacksize;/* if not zero, initial stack size */ + }; +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 + #define SPLIT_SEG_DYLIB_SUPPORT 0 + #define PREBOUND_IMAGE_SUPPORT __arm__ + #define TEXT_RELOC_SUPPORT __i386__ + #define SUPPORT_OLD_CRT_INITIALIZATION 0 + #define SUPPORT_LC_DYLD_ENVIRONMENT 1 + #define SUPPORT_VERSIONED_PATHS 1 + #define SUPPORT_CLASSIC_MACHO __arm__ + #define SUPPORT_ZERO_COST_EXCEPTIONS (!__USING_SJLJ_EXCEPTIONS__) + #define INITIAL_IMAGE_COUNT 150 + #define SUPPORT_ACCELERATE_TABLES (__arm__ || __arm64__) + #define SUPPORT_ROOT_PATH TARGET_IPHONE_SIMULATOR +#else + #define SPLIT_SEG_SHARED_REGION_SUPPORT 0 + #define SPLIT_SEG_DYLIB_SUPPORT __i386__ + #define PREBOUND_IMAGE_SUPPORT __i386__ + #define TEXT_RELOC_SUPPORT __i386__ + #define SUPPORT_OLD_CRT_INITIALIZATION __i386__ + #define SUPPORT_LC_DYLD_ENVIRONMENT (__i386__ || __x86_64__) + #define SUPPORT_VERSIONED_PATHS 1 + #define SUPPORT_CLASSIC_MACHO 1 + #define SUPPORT_ZERO_COST_EXCEPTIONS 1 + #define INITIAL_IMAGE_COUNT 200 + #define SUPPORT_ACCELERATE_TABLES 0 + #define SUPPORT_ROOT_PATH 1 +#endif + +#define MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE (32*1024) + +#define MH_HAS_OBJC 0x40000000 + +// optimize away dyld's initializers +#define VECTOR_NEVER_DESTRUCTED(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base() { } \ + } +#define VECTOR_NEVER_DESTRUCTED_EXTERN(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base(); \ + } +#define VECTOR_NEVER_DESTRUCTED_IMPL(type) \ + namespace std { \ + template <> \ + __vector_base >::~__vector_base() { } \ + } + +// utilities +namespace dyld { + extern __attribute__((noreturn)) void throwf(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern void log(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern void warn(const char* format, ...) __attribute__((format(printf, 1, 2))); + extern const char* mkstringf(const char* format, ...) __attribute__((format(printf, 1, 2))); +#if LOG_BINDINGS + extern void logBindings(const char* format, ...) __attribute__((format(printf, 1, 2))); +#endif +} +extern "C" int vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags); +extern "C" void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + + +#if __LP64__ + struct macho_header : public mach_header_64 {}; + struct macho_nlist : public nlist_64 {}; +#else + struct macho_header : public mach_header {}; + struct macho_nlist : public nlist {}; +#endif + + +#if __arm64__ + #define dyld_page_trunc(__addr) (__addr & (-16384)) + #define dyld_page_round(__addr) ((__addr + 16383) & (-16384)) + #define dyld_page_size 16384 +#elif __arm__ + #define dyld_page_trunc(__addr) trunc_page_kernel(__addr) + #define dyld_page_round(__addr) round_page_kernel(__addr) + #define dyld_page_size vm_kernel_page_size +#else + #define dyld_page_trunc(__addr) (__addr & (-4096)) + #define dyld_page_round(__addr) ((__addr + 4095) & (-4096)) + #define dyld_page_size 4096 +#endif + + + +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + const char*** NXArgvPtr; + const char*** environPtr; + const char** __prognamePtr; +}; + + + +// +// ImageLoader is an abstract base class. To support loading a particular executable +// file format, you make a concrete subclass of ImageLoader. +// +// For each executable file (dynamic shared object) in use, an ImageLoader is instantiated. +// +// The ImageLoader base class does the work of linking together images, but it knows nothing +// about any particular file format. +// +// +class ImageLoader { +public: + + typedef uint32_t DefinitionFlags; + static const DefinitionFlags kNoDefinitionOptions = 0; + static const DefinitionFlags kWeakDefinition = 1; + + typedef uint32_t ReferenceFlags; + static const ReferenceFlags kNoReferenceOptions = 0; + static const ReferenceFlags kWeakReference = 1; + static const ReferenceFlags kTentativeDefinition = 2; + + enum PrebindMode { kUseAllPrebinding, kUseSplitSegPrebinding, kUseAllButAppPredbinding, kUseNoPrebinding }; + enum BindingOptions { kBindingNone, kBindingLazyPointers, kBindingNeverSetLazyPointers }; + enum SharedRegionMode { kUseSharedRegion, kUsePrivateSharedRegion, kDontUseSharedRegion, kSharedRegionIsSharedCache }; + + struct Symbol; // abstact symbol + + struct MappedRegion { + uintptr_t address; + size_t size; + }; + + struct RPathChain { + RPathChain(const RPathChain* n, std::vector* p) : next(n), paths(p) {}; + const RPathChain* next; + std::vector* paths; + }; + + struct DOFInfo { + void* dof; + const mach_header* imageHeader; + const char* imageShortName; + }; + + struct DynamicReference { + ImageLoader* from; + ImageLoader* to; + }; + + struct InitializerTimingList + { + uintptr_t count; + struct { + const char* shortName; + uint64_t initTime; + } images[1]; + + void addTime(const char* name, uint64_t time); + }; + + struct LinkContext { + ImageLoader* (*loadLibrary)(const char* libraryName, bool search, const char* origin, const RPathChain* rpaths, unsigned& cacheIndex); + void (*terminationRecorder)(ImageLoader* image); + bool (*flatExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); + bool (*coalescedExportFinder)(const char* name, const Symbol** sym, const ImageLoader** image); + unsigned int (*getCoalescedImages)(ImageLoader* images[], unsigned imageIndex[]); + void (*undefinedHandler)(const char* name); + MappedRegion* (*getAllMappedRegions)(MappedRegion*); + void * (*bindingHandler)(const char *, const char *, void *); + void (*notifySingle)(dyld_image_states, const ImageLoader* image, InitializerTimingList*); + void (*notifyBatch)(dyld_image_states state, bool preflightOnly); + void (*removeImage)(ImageLoader* image); + void (*registerDOFs)(const std::vector& dofs); + void (*clearAllDepths)(); + void (*printAllDepths)(); + 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); + ImageLoader* (*findImageContainingAddress)(const void* addr); + void (*addDynamicReference)(ImageLoader* from, ImageLoader* to); +#if SUPPORT_ACCELERATE_TABLES + void (*notifySingleFromCache)(dyld_image_states, const mach_header* mh, const char* path); + dyld_image_state_change_handler (*getPreInitNotifyHandler)(unsigned index); + dyld_image_state_change_handler (*getBoundBatchHandler)(unsigned index); +#endif + +#if SUPPORT_OLD_CRT_INITIALIZATION + void (*setRunInitialzersOldWay)(); +#endif + BindingOptions bindingOptions; + int argc; + const char** argv; + const char** envp; + const char** apple; + const char* progname; + ProgramVars programVars; + ImageLoader* mainExecutable; + const char* imageSuffix; +#if SUPPORT_ROOT_PATH + const char** rootPaths; +#endif + const dyld_interpose_tuple* dynamicInterposeArray; + size_t dynamicInterposeCount; + PrebindMode prebindUsage; + SharedRegionMode sharedRegionMode; + bool dyldLoadedAtSameAddressNeededBySharedCache; + bool strictMachORequired; + bool requireCodeSignature; + bool mainExecutableCodeSigned; + bool preFetchDisabled; + bool prebinding; + bool bindFlat; + bool linkingMainExecutable; + bool startedInitializingMainExecutable; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + bool processIsRestricted; + bool processUsingLibraryValidation; +#endif + bool verboseOpts; + bool verboseEnv; + bool verboseLoading; + bool verboseMapping; + bool verboseRebase; + bool verboseBind; + bool verboseWeakBind; + bool verboseInit; + bool verboseDOF; + bool verbosePrebinding; + bool verboseCoreSymbolication; + bool verboseWarnings; + bool verboseRPaths; + bool verboseInterposing; + bool verboseCodeSignatures; + }; + + struct CoalIterator + { + ImageLoader* image; + const char* symbolName; + unsigned int loadOrder; + bool weakSymbol; + bool symbolMatches; + bool done; + // the following are private to the ImageLoader subclass + uintptr_t curIndex; + uintptr_t endIndex; + uintptr_t address; + uintptr_t type; + uintptr_t addend; + uintptr_t imageIndex; + }; + + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex) = 0; + virtual bool incrementCoalIterator(CoalIterator&) = 0; + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& context) = 0; + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context) = 0; + + struct UninitedUpwards + { + uintptr_t count; + ImageLoader* images[1]; + }; + + + // constructor is protected, but anyone can delete an image + virtual ~ImageLoader(); + + // link() takes a newly instantiated ImageLoader and does all + // fixups needed to make it usable by the process + void link(const LinkContext& context, bool forceLazysBound, bool preflight, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath); + + // runInitializers() is normally called in link() but the main executable must + // run crt code before initializers + void runInitializers(const LinkContext& context, InitializerTimingList& timingInfo); + + // called after link() forces all lazy pointers to be bound + void bindAllLazyPointers(const LinkContext& context, bool recursive); + + // used by dyld to see if a requested library is already loaded (might be symlink) + bool statMatch(const struct stat& stat_buf) const; + + // get short name of this image + const char* getShortName() const; + + // returns leaf name + static const char* shortName(const char* fullName); + + // get path used to load this image, not necessarily the "real" path + const char* getPath() const { return fPath; } + + uint32_t getPathHash() const { return fPathHash; } + + // get the "real" path for this image (e.g. no @rpath) + const char* getRealPath() const; + + // get path this image is intended to be placed on disk or NULL if no preferred install location + virtual const char* getInstallPath() const = 0; + + // image was loaded with NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME and all clients are looking for install path + bool matchInstallPath() const; + void setMatchInstallPath(bool); + + // mark that this image's exported symbols should be ignored when linking other images (e.g. RTLD_LOCAL) + void setHideExports(bool hide = true); + + // check if this image's exported symbols should be ignored when linking other images + bool hasHiddenExports() const; + + // checks if this image is already linked into the process + bool isLinked() const; + + // even if image is deleted, leave segments mapped in + void setLeaveMapped(); + + // 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; + + // checks if the specifed symbol is within this image's symbol table + virtual bool containsSymbol(const void* addr) const = 0; + + // checks if the specifed address range overlaps any of this image's segments + virtual bool overlapsWithAddressRange(const void* start, const void* end) const; + + // adds to list of ranges of memory mapped in + void getMappedRegions(MappedRegion*& region) const; + + // st_mtime from stat() on file + time_t lastModified() const; + + // only valid for main executables, returns a pointer its entry point from LC_UNIXTHREAD + virtual void* getThreadPC() const = 0; + + // only valid for main executables, returns a pointer its main from LC_

getPath(), foundIn); + } + + // gets address of implementation (code) of the specified exported symbol + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor=NULL, bool runResolver=false, const char* symbolName=NULL) const = 0; + + // gets attributes of the specified exported symbol + virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const = 0; + + // gets name of the specified exported symbol + virtual const char* getExportedSymbolName(const Symbol* sym) const = 0; + + // gets how many symbols are exported by this image + virtual uint32_t getExportedSymbolCount() const = 0; + + // gets the i'th exported symbol + virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const = 0; + + // find exported symbol as if imported by this image + // used by RTLD_NEXT + virtual const Symbol* findExportedSymbolInDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const; + + // find exported symbol as if imported by this image + // used by RTLD_SELF + virtual const Symbol* findExportedSymbolInImageOrDependentImages(const char* name, const LinkContext& context, const ImageLoader** foundIn) const; + + // gets how many symbols are imported by this image + virtual uint32_t getImportedSymbolCount() const = 0; + + // gets the i'th imported symbol + virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const = 0; + + // gets attributes of the specified imported symbol + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const = 0; + + // gets name of the specified imported symbol + virtual const char* getImportedSymbolName(const Symbol* sym) const = 0; + + // find the closest symbol before addr + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0; + + // for use with accelerator tables + virtual const char* getIndexedPath(unsigned) const { return getPath(); } + virtual const char* getIndexedShortName(unsigned) const { return getShortName(); } + + // checks if this image is a bundle and can be loaded but not linked + virtual bool isBundle() const = 0; + + // checks if this image is a dylib + virtual bool isDylib() const = 0; + + // checks if this image is a main executable + virtual bool isExecutable() const = 0; + + // checks if this image is a main executable + virtual bool isPositionIndependentExecutable() const = 0; + + // only for main executable + virtual bool forceFlat() const = 0; + + // called at runtime when a lazily bound function is first called + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) = 0; + + // called at runtime when a fast lazily bound function is first called + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) = 0; + + // calls termination routines (e.g. C++ static destructors for image) + virtual void doTermination(const LinkContext& context) = 0; + + // return if this image has initialization routines + virtual bool needsInitialization() = 0; + + // return if this image has specified section and set start and length + virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) = 0; + + // fills in info about __eh_frame and __unwind_info sections + virtual void getUnwindInfo(dyld_unwind_sections* info) = 0; + + // given a pointer into an image, find which segment and section it is in + virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) = 0; + + // the image supports being prebound + virtual bool isPrebindable() const = 0; + + // the image is prebindable and its prebinding is valid + virtual bool usablePrebinding(const LinkContext& context) const = 0; + + // add all RPATH paths this image contains + virtual void getRPaths(const LinkContext& context, std::vector&) const = 0; + + // image has or uses weak definitions that need runtime coalescing + virtual bool participatesInCoalescing() const = 0; + + // if image has a UUID, copy into parameter and return true + virtual bool getUUID(uuid_t) const = 0; + + // dynamic interpose values onto this image + virtual void dynamicInterpose(const LinkContext& context) = 0; + + // record interposing for any late binding + void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count); + + virtual const char* libPath(unsigned int) const = 0; + + // Image has objc sections, so information objc about when it comes and goes + virtual bool notifyObjC() const { return false; } + +// +// A segment is a chunk of an executable file that is mapped into memory. +// + virtual unsigned int segmentCount() const = 0; + virtual const char* segName(unsigned int) const = 0; + virtual uintptr_t segSize(unsigned int) const = 0; + virtual uintptr_t segFileSize(unsigned int) const = 0; + virtual bool segHasTrailingZeroFill(unsigned int) = 0; + virtual uintptr_t segFileOffset(unsigned int) const = 0; + virtual bool segReadable(unsigned int) const = 0; + virtual bool segWriteable(unsigned int) const = 0; + virtual bool segExecutable(unsigned int) const = 0; + virtual bool segUnaccessible(unsigned int) const = 0; + virtual bool segHasPreferredLoadAddress(unsigned int) const = 0; + virtual uintptr_t segPreferredLoadAddress(unsigned int) const = 0; + virtual uintptr_t segActualLoadAddress(unsigned int) const = 0; + virtual uintptr_t segActualEndAddress(unsigned int) const = 0; + + + // info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS + virtual uint32_t sdkVersion() const = 0; + virtual uint32_t minOSVersion() const = 0; + + // if the image contains interposing functions, register them + virtual void registerInterposing() = 0; + + // when resolving symbols look in subImage if symbol can't be found + void reExport(ImageLoader* subImage); + + void weakBind(const LinkContext& context); + + void applyInterposing(const LinkContext& context); + + dyld_image_states getState() { return (dyld_image_states)fState; } + + ino_t getInode() const { return fInode; } + dev_t getDevice() const { return fDevice; } + + // used to sort images bottom-up + int compare(const ImageLoader* right) const; + + void incrementDlopenReferenceCount() { ++fDlopenReferenceCount; } + + bool decrementDlopenReferenceCount(); + + void printReferenceCounts(); + + uint32_t dlopenCount() const { return fDlopenReferenceCount; } + + void setCanUnload() { fNeverUnload = false; fLeaveMapped = false; } + + bool neverUnload() const { return fNeverUnload; } + + void setNeverUnload() { fNeverUnload = true; fLeaveMapped = true; } + void setNeverUnloadRecursive(); + + bool isReferencedDownward() { return fIsReferencedDownward; } + + + // triggered by DYLD_PRINT_STATISTICS to write info on work done and how fast + static void printStatistics(unsigned int imageCount, const InitializerTimingList& timingInfo); + static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo); + + // used with DYLD_IMAGE_SUFFIX + static void addSuffix(const char* path, const char* suffix, char* result); + + static uint32_t hash(const char*); + + static const uint8_t* trieWalk(const uint8_t* start, const uint8_t* end, const char* stringToFind); + + // used instead of directly deleting image + static void deleteImage(ImageLoader*); + + static bool haveInterposingTuples() { return !fgInterposingTuples.empty(); } + static void clearInterposingTuples() { fgInterposingTuples.clear(); } + + bool dependsOn(ImageLoader* image); + + void setPath(const char* path); + void setPaths(const char* path, const char* realPath); + void setPathUnowned(const char* path); + + void clearDepth() { fDepth = 0; } + int getDepth() { return fDepth; } + + void setBeingRemoved() { fBeingRemoved = true; } + bool isBeingRemoved() const { return fBeingRemoved; } + + void markNotUsed() { fMarkedInUse = false; } + void markedUsedRecursive(const std::vector&); + bool isMarkedInUse() const { return fMarkedInUse; } + + void setAddFuncNotified() { fAddFuncNotified = true; } + bool addFuncNotified() const { return fAddFuncNotified; } + + struct InterposeTuple { + uintptr_t replacement; + ImageLoader* neverImage; // don't apply replacement to this image + ImageLoader* onlyImage; // only apply replacement to this image + uintptr_t replacee; + }; + + static uintptr_t read_uleb128(const uint8_t*& p, const uint8_t* end); + static intptr_t read_sleb128(const uint8_t*& p, const uint8_t* end); + +protected: + // abstract base class so all constructors protected + ImageLoader(const char* path, unsigned int libCount); + ImageLoader(const ImageLoader&); + void operator=(const ImageLoader&); + void operator delete(void* image) throw() { ::free(image); } + + + struct LibraryInfo { + uint32_t checksum; + uint32_t minVersion; + uint32_t maxVersion; + }; + + struct DependentLibrary { + ImageLoader* image; + uint32_t required : 1, + checksumMatches : 1, + isReExported : 1, + isSubFramework : 1; + }; + + struct DependentLibraryInfo { + const char* name; + LibraryInfo info; + bool required; + bool reExported; + bool upward; + }; + + + 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 bool libIsUpward(unsigned int) const = 0; + virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) = 0; + + // To link() an image, its dependent libraries are loaded, it is rebased, bound, and initialized. + // These methods do the above, exactly once, and it the right order + virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath); + virtual unsigned recursiveUpdateDepth(unsigned int maxDepth); + virtual void recursiveRebase(const LinkContext& context); + virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload); + virtual void recursiveApplyInterposing(const LinkContext& context); + virtual void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs); + virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, + ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&); + + // fill in information about dependent libraries (array length is fLibraryCount) + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) = 0; + + // called on images that are libraries, returns info about itself + virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) = 0; + + // do any fix ups in this image that depend only on the load address of the image + virtual void doRebase(const LinkContext& context) = 0; + + // do any symbolic fix ups in this image + virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0; + + // called later via API to force all lazy pointer to be bound + virtual void doBindJustLazies(const LinkContext& context) = 0; + + // if image has any dtrace DOF sections, append them to list to be registered + virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) = 0; + + // do interpose + virtual void doInterpose(const LinkContext& context) = 0; + + // run any initialization routines in this image + virtual bool doInitialization(const LinkContext& context) = 0; + + // return if this image has termination routines + virtual bool needsTermination() = 0; + + // support for runtimes in which segments don't have to maintain their relative positions + virtual bool segmentsMustSlideTogether() const = 0; + + // built with PIC code and can load at any address + virtual bool segmentsCanSlide() const = 0; + + // set how much all segments slide + virtual void setSlide(intptr_t slide) = 0; + + // returns if all dependent libraries checksum's were as expected and none slide + bool allDependentLibrariesAsWhenPreBound() const; + + // in mach-o a child tells it parent to re-export, instead of the other way around... + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const = 0; + + // in mach-o a parent library knows name of sub libraries it re-exports.. + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const = 0; + + virtual bool weakSymbolsBound(unsigned index) { return fWeakSymbolsBound; } + virtual void setWeakSymbolsBound(unsigned index) { fWeakSymbolsBound = true; } + + // set fState to dyld_image_state_memory_mapped + void setMapped(const LinkContext& context); + + void setFileInfo(dev_t device, ino_t inode, time_t modDate); + + void setDepth(uint16_t depth) { fDepth = depth; } + + static uintptr_t interposedAddress(const LinkContext& context, uintptr_t address, const ImageLoader* notInImage, const ImageLoader* onlyInImage=NULL); + + static uintptr_t fgNextPIEDylibAddress; + static uint32_t fgImagesWithUsedPrebinding; + static uint32_t fgImagesUsedFromSharedCache; + static uint32_t fgImagesHasWeakDefinitions; + static uint32_t fgImagesRequiringCoalescing; + static uint32_t fgTotalRebaseFixups; + static uint32_t fgTotalBindFixups; + static uint32_t fgTotalBindSymbolsResolved; + static uint32_t fgTotalBindImageSearches; + static uint32_t fgTotalLazyBindFixups; + static uint32_t fgTotalPossibleLazyBindFixups; + static uint32_t fgTotalSegmentsMapped; + static uint32_t fgSymbolTrieSearchs; + static uint64_t fgTotalBytesMapped; + static uint64_t fgTotalBytesPreFetched; + static uint64_t fgTotalLoadLibrariesTime; +public: + static uint64_t fgTotalObjCSetupTime; + static uint64_t fgTotalDebuggerPausedTime; + static uint64_t fgTotalRebindCacheTime; +protected: + static uint64_t fgTotalRebaseTime; + static uint64_t fgTotalBindTime; + static uint64_t fgTotalWeakBindTime; + static uint64_t fgTotalDOF; + static uint64_t fgTotalInitTime; + static std::vector fgInterposingTuples; + + const char* fPath; + const char* fRealPath; + dev_t fDevice; + ino_t fInode; + time_t fLastModified; + uint32_t fPathHash; + uint32_t fDlopenReferenceCount; // count of how many dlopens have been done on this image + + struct recursive_lock { + recursive_lock(mach_port_t t) : thread(t), count(0) {} + mach_port_t thread; + int count; + }; + void recursiveSpinLock(recursive_lock&); + void recursiveSpinUnLock(); + +private: + const ImageLoader::Symbol* findExportedSymbolInDependentImagesExcept(const char* name, const ImageLoader** dsiStart, + const ImageLoader**& dsiCur, const ImageLoader** dsiEnd, const ImageLoader** foundIn) const; + + void processInitializers(const LinkContext& context, mach_port_t this_thread, + InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& ups); + + + recursive_lock* fInitializerRecursiveLock; + uint16_t fDepth; + uint16_t fLoadOrder; + uint32_t fState : 8, + fLibraryCount : 10, + fAllLibraryChecksumsAndLoadAddressesMatch : 1, + fLeaveMapped : 1, // when unloaded, leave image mapped in cause some other code may have pointers into it + fNeverUnload : 1, // image was statically loaded by main executable + fHideSymbols : 1, // ignore this image's exported symbols when linking other images + fMatchByInstallName : 1,// look at image's install-path not its load path + fInterposed : 1, + fRegisteredDOF : 1, + fAllLazyPointersBound : 1, + fMarkedInUse : 1, + fBeingRemoved : 1, + fAddFuncNotified : 1, + fPathOwnedByImage : 1, + fIsReferencedDownward : 1, + fWeakSymbolsBound : 1; + + static uint16_t fgLoadOrdinal; +}; + + +VECTOR_NEVER_DESTRUCTED_EXTERN(ImageLoader::InterposeTuple); + + +#endif + diff --git a/dyld/src/ImageLoaderMachO.cpp b/dyld/src/ImageLoaderMachO.cpp new file mode 100644 index 0000000..feba249 --- /dev/null +++ b/dyld/src/ImageLoaderMachO.cpp @@ -0,0 +1,2788 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-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@ + */ + +// work around until conformance work is complete rdar://problem/4508801 +#define __srr0 srr0 +#define __eip eip +#define __rip rip + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageLoaderMachO.h" +#include "ImageLoaderMachOCompressed.h" +#if SUPPORT_CLASSIC_MACHO +#include "ImageLoaderMachOClassic.h" +#endif +#include "mach-o/dyld_images.h" +#include "Tracing.h" +#include "dyld.h" + +// 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 + +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + +#ifndef LC_BUILD_VERSION + #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */ + + /* + * The build_version_command contains the min OS version on which this + * binary was built to run for its platform. The list of known platforms and + * tool values following it. + */ + struct build_version_command { + uint32_t cmd; /* LC_BUILD_VERSION */ + uint32_t cmdsize; /* sizeof(struct build_version_command) plus */ + /* ntools * sizeof(struct build_tool_version) */ + uint32_t platform; /* platform */ + uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t ntools; /* number of tool entries following this */ + }; + + struct build_tool_version { + uint32_t tool; /* enum for the tool */ + uint32_t version; /* version number of the tool */ + }; + + /* Known values for the platform field above. */ + #define PLATFORM_MACOS 1 + #define PLATFORM_IOS 2 + #define PLATFORM_TVOS 3 + #define PLATFORM_WATCHOS 4 + #define PLATFORM_BRIDGEOS 5 + + /* Known values for the tool field above. */ + #define TOOL_CLANG 1 + #define TOOL_SWIFT 2 + #define TOOL_LD 3 +#endif + + + +#if TARGET_IPHONE_SIMULATOR + #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.dylib" +#else + #define LIBSYSTEM_DYLIB_PATH "/usr/lib/libSystem.B.dylib" +#endif + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + +uint32_t ImageLoaderMachO::fgSymbolTableBinarySearchs = 0; + + +ImageLoaderMachO::ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount) + : ImageLoader(path, libCount), fCoveredCodeLength(0), fMachOData((uint8_t*)mh), fLinkEditBase(NULL), fSlide(0), + fEHFrameSectionOffset(0), fUnwindInfoSectionOffset(0), fDylibIDOffset(0), +fSegmentsCount(segCount), fIsSplitSeg(false), fInSharedCache(false), +#if TEXT_RELOC_SUPPORT + fTextSegmentRebases(false), + fTextSegmentBinds(false), +#endif +#if __i386__ + fReadOnlyImportSegment(false), +#endif + fHasSubLibraries(false), fHasSubUmbrella(false), fInUmbrella(false), fHasDOFSections(false), fHasDashInit(false), + fHasInitializers(false), fHasTerminators(false), fNotifyObjC(false), fRetainForObjC(false), fRegisteredAsRequiresCoalescing(false) +{ + fIsSplitSeg = ((mh->flags & MH_SPLIT_SEGS) != 0); + + // construct SegmentMachO object for each LC_SEGMENT cmd using "placement new" to put + // each SegmentMachO object in array at end of ImageLoaderMachO object + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0, segIndex=0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* segCmd = (struct macho_segment_command*)cmd; + // ignore zero-sized segments + if ( segCmd->vmsize != 0 ) { + // record offset of load command + segOffsets[segIndex++] = (uint32_t)((uint8_t*)segCmd - fMachOData); + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + +} + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static uintptr_t pageAlign(uintptr_t value) +{ + return (value + 4095) & (-4096); +} +#endif + +// 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 inCache, bool* compressed, + unsigned int* segCount, unsigned int* libCount, const LinkContext& context, + const linkedit_data_command** codeSigCmd, + const encryption_info_command** encryptCmd) +{ + *compressed = false; + *segCount = 0; + *libCount = 0; + *codeSigCmd = NULL; + *encryptCmd = NULL; + + const uint32_t cmd_count = mh->ncmds; + const uint32_t sizeofcmds = mh->sizeofcmds; + if ( sizeofcmds > (MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE-sizeof(macho_header)) ) + dyld::throwf("malformed mach-o: load commands size (%u) > %u", sizeofcmds, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); + if ( cmd_count > (sizeofcmds/sizeof(load_command)) ) + dyld::throwf("malformed mach-o: ncmds (%u) too large to fit in sizeofcmds (%u)", cmd_count, sizeofcmds); + const struct load_command* const startCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header)); + const struct load_command* const endCmds = (struct load_command*)(((uint8_t*)mh) + sizeof(macho_header) + sizeofcmds); + const struct load_command* cmd = startCmds; + bool foundLoadCommandSegment = false; + const macho_segment_command* linkeditSegCmd = NULL; + const macho_segment_command* startOfFileSegCmd = NULL; + const dyld_info_command* dyldInfoCmd = NULL; + const symtab_command* symTabCmd = NULL; + const dysymtab_command* dynSymbTabCmd = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize; + const macho_segment_command* segCmd; + const dylib_command* dylibCmd; + if ( cmdLength < 8 ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) too small in %s", + i, cmdLength, path); + } + const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( (nextCmd > endCmds) || (nextCmd < cmd) ) { + dyld::throwf("malformed mach-o image: load command #%d length (%u) would exceed sizeofcmds (%u) in %s", + i, cmdLength, mh->sizeofcmds, path); + } + switch (cmd->cmd) { + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + if ( cmd->cmdsize != sizeof(dyld_info_command) ) + throw "malformed mach-o image: LC_DYLD_INFO size wrong"; + dyldInfoCmd = (struct dyld_info_command*)cmd; + *compressed = true; + break; + case LC_SEGMENT_COMMAND: + segCmd = (struct macho_segment_command*)cmd; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/19617624 allow unmapped segments on OSX (but not iOS) + if ( ((segCmd->filesize) > pageAlign(segCmd->vmsize)) && (segCmd->vmsize != 0) ) +#else + // dyld should support non-allocatable __LLVM segment + if ( (segCmd->filesize > segCmd->vmsize) && ((segCmd->vmsize != 0) || ((segCmd->flags & SG_NORELOC) == 0)) ) +#endif + dyld::throwf("malformed mach-o image: segment load command %s filesize (0x%0lX) is larger than vmsize (0x%0lX)", segCmd->segname, (long)segCmd->filesize , (long)segCmd->vmsize ); + if ( cmd->cmdsize < sizeof(macho_segment_command) ) + throw "malformed mach-o image: LC_SEGMENT size too small"; + if ( cmd->cmdsize != (sizeof(macho_segment_command) + segCmd->nsects * sizeof(macho_section)) ) + throw "malformed mach-o image: LC_SEGMENT size wrong for number of sections"; + // ignore zero-sized segments + if ( segCmd->vmsize != 0 ) + *segCount += 1; + if ( strcmp(segCmd->segname, "__LINKEDIT") == 0 ) { + #if TARGET_IPHONE_SIMULATOR + // Note: should check on all platforms that __LINKEDIT is read-only, but + if ( segCmd->initprot != VM_PROT_READ ) + throw "malformed mach-o image: __LINKEDIT segment does not have read-only permissions"; + #endif + if ( segCmd->fileoff == 0 ) + throw "malformed mach-o image: __LINKEDIT has fileoff==0 which overlaps mach_header"; + if ( linkeditSegCmd != NULL ) + throw "malformed mach-o image: multiple __LINKEDIT segments"; + linkeditSegCmd = segCmd; + } + else { + if ( segCmd->initprot & 0xFFFFFFF8 ) + dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in initprot", segCmd->segname, segCmd->initprot); + if ( segCmd->maxprot & 0xFFFFFFF8 ) + dyld::throwf("malformed mach-o image: %s segment has invalid permission bits (0x%X) in maxprot", segCmd->segname, segCmd->maxprot); + if ( (segCmd->initprot != 0) && ((segCmd->initprot & VM_PROT_READ) == 0) ) + dyld::throwf("malformed mach-o image: %s segment is not mapped readable", segCmd->segname); + } + if ( (segCmd->fileoff == 0) && (segCmd->filesize != 0) ) { + if ( (segCmd->initprot & VM_PROT_READ) == 0 ) + dyld::throwf("malformed mach-o image: %s segment maps start of file but is not readable", segCmd->segname); + if ( (segCmd->initprot & VM_PROT_WRITE) == VM_PROT_WRITE ) { + if ( context.strictMachORequired ) + dyld::throwf("malformed mach-o image: %s segment maps start of file but is writable", segCmd->segname); + } + if ( segCmd->filesize < (sizeof(macho_header) + mh->sizeofcmds) ) + dyld::throwf("malformed mach-o image: %s segment does not map all of load commands", segCmd->segname); + if ( startOfFileSegCmd != NULL ) + dyld::throwf("malformed mach-o image: multiple segments map start of file: %s %s", startOfFileSegCmd->segname, segCmd->segname); + startOfFileSegCmd = segCmd; + } + if ( context.strictMachORequired ) { + uintptr_t vmStart = segCmd->vmaddr; + uintptr_t vmSize = segCmd->vmsize; + uintptr_t vmEnd = vmStart + vmSize; + uintptr_t fileStart = segCmd->fileoff; + uintptr_t fileSize = segCmd->filesize; + if ( (intptr_t)(vmSize) < 0 ) + dyld::throwf("malformed mach-o image: segment load command %s vmsize too large in %s", segCmd->segname, path); + if ( vmStart > vmEnd ) + dyld::throwf("malformed mach-o image: segment load command %s wraps around address space", segCmd->segname); + if ( vmSize != fileSize ) { + if ( segCmd->initprot == 0 ) { + // allow: fileSize == 0 && initprot == 0 e.g. __PAGEZERO + // allow: vmSize == 0 && initprot == 0 e.g. __LLVM + if ( (fileSize != 0) && (vmSize != 0) ) + dyld::throwf("malformed mach-o image: unaccessable segment %s has non-zero filesize and vmsize", segCmd->segname); + } + else { + // allow: vmSize > fileSize && initprot != X e.g. __DATA + if ( vmSize < fileSize ) { + dyld::throwf("malformed mach-o image: segment %s has vmsize < filesize", segCmd->segname); + } + if ( segCmd->initprot & VM_PROT_EXECUTE ) { + dyld::throwf("malformed mach-o image: segment %s has vmsize != filesize and is executable", segCmd->segname); + } + } + } + if ( inCache ) { + if ( (fileSize != 0) && (segCmd->initprot == (VM_PROT_READ | VM_PROT_EXECUTE)) ) { + if ( foundLoadCommandSegment ) + throw "load commands in multiple segments"; + foundLoadCommandSegment = true; + } + } + else if ( (fileStart < mh->sizeofcmds) && (fileSize != 0) ) { + // all load commands must be in an executable segment + if ( (fileStart != 0) || (fileSize < (mh->sizeofcmds+sizeof(macho_header))) ) + dyld::throwf("malformed mach-o image: segment %s does not span all load commands", segCmd->segname); + if ( segCmd->initprot != (VM_PROT_READ | VM_PROT_EXECUTE) ) + dyld::throwf("malformed mach-o image: load commands found in segment %s with wrong permissions", segCmd->segname); + if ( foundLoadCommandSegment ) + throw "load commands in multiple segments"; + foundLoadCommandSegment = true; + } + + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (!inCache && sect->offset != 0 && ((sect->offset + sect->size) > (segCmd->fileoff + segCmd->filesize))) + dyld::throwf("malformed mach-o image: section %s,%s of '%s' exceeds segment %s booundary", sect->segname, sect->sectname, path, segCmd->segname); + } + } + break; + case LC_SEGMENT_COMMAND_WRONG: + dyld::throwf("malformed mach-o image: wrong LC_SEGMENT[_64] for architecture"); + break; + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + *libCount += 1; + // fall thru + case LC_ID_DYLIB: + dylibCmd = (dylib_command*)cmd; + if ( dylibCmd->dylib.name.offset > cmdLength ) + dyld::throwf("malformed mach-o image: dylib load command #%d has offset (%u) outside its size (%u)", i, dylibCmd->dylib.name.offset, cmdLength); + if ( (dylibCmd->dylib.name.offset + strlen((char*)dylibCmd + dylibCmd->dylib.name.offset) + 1) > cmdLength ) + dyld::throwf("malformed mach-o image: dylib load command #%d string extends beyond end of load command", i); + break; + case LC_CODE_SIGNATURE: + if ( cmd->cmdsize != sizeof(linkedit_data_command) ) + throw "malformed mach-o image: LC_CODE_SIGNATURE size wrong"; + // only support one LC_CODE_SIGNATURE per image + if ( *codeSigCmd != NULL ) + throw "malformed mach-o image: multiple LC_CODE_SIGNATURE load commands"; + *codeSigCmd = (struct linkedit_data_command*)cmd; + break; + case LC_ENCRYPTION_INFO: + if ( cmd->cmdsize != sizeof(encryption_info_command) ) + throw "malformed mach-o image: LC_ENCRYPTION_INFO size wrong"; + // only support one LC_ENCRYPTION_INFO per image + if ( *encryptCmd != NULL ) + throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO load commands"; + *encryptCmd = (encryption_info_command*)cmd; + break; + case LC_ENCRYPTION_INFO_64: + if ( cmd->cmdsize != sizeof(encryption_info_command_64) ) + throw "malformed mach-o image: LC_ENCRYPTION_INFO_64 size wrong"; + // only support one LC_ENCRYPTION_INFO_64 per image + if ( *encryptCmd != NULL ) + throw "malformed mach-o image: multiple LC_ENCRYPTION_INFO_64 load commands"; + *encryptCmd = (encryption_info_command*)cmd; + break; + case LC_SYMTAB: + if ( cmd->cmdsize != sizeof(symtab_command) ) + throw "malformed mach-o image: LC_SYMTAB size wrong"; + symTabCmd = (symtab_command*)cmd; + break; + case LC_DYSYMTAB: + if ( cmd->cmdsize != sizeof(dysymtab_command) ) + throw "malformed mach-o image: LC_DYSYMTAB size wrong"; + dynSymbTabCmd = (dysymtab_command*)cmd; + break; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // error when loading iOS Simulator mach-o binary into macOS process + case LC_VERSION_MIN_WATCHOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_IPHONEOS: + throw "mach-o, but built for simulator (not macOS)"; + break; +#endif + } + cmd = nextCmd; + } + + if ( context.strictMachORequired && !foundLoadCommandSegment ) + throw "load commands not in a segment"; + if ( linkeditSegCmd == NULL ) + throw "malformed mach-o image: missing __LINKEDIT segment"; + if ( !inCache && (startOfFileSegCmd == NULL) ) + throw "malformed mach-o image: missing __TEXT segment that maps start of file"; + // verify every segment does not overlap another segment + if ( context.strictMachORequired ) { + uintptr_t lastFileStart = 0; + uintptr_t linkeditFileStart = 0; + const struct load_command* cmd1 = startCmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd1->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd1 = (struct macho_segment_command*)cmd1; + uintptr_t vmStart1 = segCmd1->vmaddr; + uintptr_t vmEnd1 = segCmd1->vmaddr + segCmd1->vmsize; + uintptr_t fileStart1 = segCmd1->fileoff; + uintptr_t fileEnd1 = segCmd1->fileoff + segCmd1->filesize; + + if (fileStart1 > lastFileStart) + lastFileStart = fileStart1; + + if ( strcmp(&segCmd1->segname[0], "__LINKEDIT") == 0 ) { + linkeditFileStart = fileStart1; + } + + const struct load_command* cmd2 = startCmds; + for (uint32_t j = 0; j < cmd_count; ++j) { + if ( cmd2 == cmd1 ) + continue; + if ( cmd2->cmd == LC_SEGMENT_COMMAND ) { + struct macho_segment_command* segCmd2 = (struct macho_segment_command*)cmd2; + uintptr_t vmStart2 = segCmd2->vmaddr; + uintptr_t vmEnd2 = segCmd2->vmaddr + segCmd2->vmsize; + uintptr_t fileStart2 = segCmd2->fileoff; + uintptr_t fileEnd2 = segCmd2->fileoff + segCmd2->filesize; + if ( ((vmStart2 <= vmStart1) && (vmEnd2 > vmStart1) && (vmEnd1 > vmStart1)) + || ((vmStart2 >= vmStart1) && (vmStart2 < vmEnd1) && (vmEnd2 > vmStart2)) ) + dyld::throwf("malformed mach-o image: segment %s vm overlaps segment %s", segCmd1->segname, segCmd2->segname); + if ( ((fileStart2 <= fileStart1) && (fileEnd2 > fileStart1) && (fileEnd1 > fileStart1)) + || ((fileStart2 >= fileStart1) && (fileStart2 < fileEnd1) && (fileEnd2 > fileStart2)) ) + dyld::throwf("malformed mach-o image: segment %s file content overlaps segment %s", segCmd1->segname, segCmd2->segname); + } + cmd2 = (const struct load_command*)(((char*)cmd2)+cmd2->cmdsize); + } + } + cmd1 = (const struct load_command*)(((char*)cmd1)+cmd1->cmdsize); + } + + if (lastFileStart != linkeditFileStart) + dyld::throwf("malformed mach-o image: __LINKEDIT must be last segment"); + } + + // validate linkedit content + if ( (dyldInfoCmd == NULL) && (symTabCmd == NULL) ) + throw "malformed mach-o image: missing LC_SYMTAB or LC_DYLD_INFO"; + if ( dynSymbTabCmd == NULL ) + throw "malformed mach-o image: missing LC_DYSYMTAB"; + + uint32_t linkeditFileOffsetStart = (uint32_t)linkeditSegCmd->fileoff; + uint32_t linkeditFileOffsetEnd = (uint32_t)linkeditSegCmd->fileoff + (uint32_t)linkeditSegCmd->filesize; + + if ( !inCache && (dyldInfoCmd != NULL) && context.strictMachORequired ) { + // validate all LC_DYLD_INFO chunks fit in LINKEDIT and don't overlap + uint32_t offset = linkeditFileOffsetStart; + if ( dyldInfoCmd->rebase_size != 0 ) { + if ( dyldInfoCmd->rebase_size & 0x80000000 ) + throw "malformed mach-o image: dyld rebase info size overflow"; + if ( dyldInfoCmd->rebase_off < offset ) + throw "malformed mach-o image: dyld rebase info underruns __LINKEDIT"; + offset = dyldInfoCmd->rebase_off + dyldInfoCmd->rebase_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld rebase info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->bind_size != 0 ) { + if ( dyldInfoCmd->bind_size & 0x80000000 ) + throw "malformed mach-o image: dyld bind info size overflow"; + if ( dyldInfoCmd->bind_off < offset ) + throw "malformed mach-o image: dyld bind info overlaps rebase info"; + offset = dyldInfoCmd->bind_off + dyldInfoCmd->bind_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld bind info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->weak_bind_size != 0 ) { + if ( dyldInfoCmd->weak_bind_size & 0x80000000 ) + throw "malformed mach-o image: dyld weak bind info size overflow"; + if ( dyldInfoCmd->weak_bind_off < offset ) + throw "malformed mach-o image: dyld weak bind info overlaps bind info"; + offset = dyldInfoCmd->weak_bind_off + dyldInfoCmd->weak_bind_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld weak bind info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->lazy_bind_size != 0 ) { + if ( dyldInfoCmd->lazy_bind_size & 0x80000000 ) + throw "malformed mach-o image: dyld lazy bind info size overflow"; + if ( dyldInfoCmd->lazy_bind_off < offset ) + throw "malformed mach-o image: dyld lazy bind info overlaps weak bind info"; + offset = dyldInfoCmd->lazy_bind_off + dyldInfoCmd->lazy_bind_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld lazy bind info overruns __LINKEDIT"; + } + if ( dyldInfoCmd->export_size != 0 ) { + if ( dyldInfoCmd->export_size & 0x80000000 ) + throw "malformed mach-o image: dyld export info size overflow"; + if ( dyldInfoCmd->export_off < offset ) + throw "malformed mach-o image: dyld export info overlaps lazy bind info"; + offset = dyldInfoCmd->export_off + dyldInfoCmd->export_size; + if ( offset > linkeditFileOffsetEnd ) + throw "malformed mach-o image: dyld export info overruns __LINKEDIT"; + } + } + + if ( symTabCmd != NULL ) { + // validate symbol table fits in LINKEDIT + if ( symTabCmd->symoff < linkeditFileOffsetStart ) + throw "malformed mach-o image: symbol table underruns __LINKEDIT"; + if ( symTabCmd->nsyms > 0x10000000 ) + throw "malformed mach-o image: symbol table too large"; + uint32_t symbolsSize = symTabCmd->nsyms * sizeof(macho_nlist); + if ( symbolsSize > linkeditSegCmd->filesize ) + throw "malformed mach-o image: symbol table overruns __LINKEDIT"; + if ( symTabCmd->symoff + symbolsSize < symTabCmd->symoff ) + throw "malformed mach-o image: symbol table size wraps"; + if ( symTabCmd->symoff + symbolsSize > symTabCmd->stroff ) + throw "malformed mach-o image: symbol table overlaps symbol strings"; + if ( symTabCmd->stroff + symTabCmd->strsize < symTabCmd->stroff ) + throw "malformed mach-o image: symbol string size wraps"; + if ( symTabCmd->stroff + symTabCmd->strsize > linkeditFileOffsetEnd ) { + // let old apps overflow as long as it stays within mapped page + if ( context.strictMachORequired || (symTabCmd->stroff + symTabCmd->strsize > ((linkeditFileOffsetEnd + 4095) & (-4096))) ) + throw "malformed mach-o image: symbol strings overrun __LINKEDIT"; + } + // validate indirect symbol table + if ( dynSymbTabCmd->nindirectsyms != 0 ) { + if ( dynSymbTabCmd->indirectsymoff < linkeditFileOffsetStart ) + throw "malformed mach-o image: indirect symbol table underruns __LINKEDIT"; + if ( dynSymbTabCmd->nindirectsyms > 0x10000000 ) + throw "malformed mach-o image: indirect symbol table too large"; + uint32_t indirectTableSize = dynSymbTabCmd->nindirectsyms * sizeof(uint32_t); + if ( indirectTableSize > linkeditSegCmd->filesize ) + throw "malformed mach-o image: indirect symbol table overruns __LINKEDIT"; + if ( dynSymbTabCmd->indirectsymoff + indirectTableSize < dynSymbTabCmd->indirectsymoff ) + throw "malformed mach-o image: indirect symbol table size wraps"; + if ( context.strictMachORequired && (dynSymbTabCmd->indirectsymoff + indirectTableSize > symTabCmd->stroff) ) + throw "malformed mach-o image: indirect symbol table overruns string pool"; + } + if ( (dynSymbTabCmd->nlocalsym > symTabCmd->nsyms) || (dynSymbTabCmd->ilocalsym > symTabCmd->nsyms) ) + throw "malformed mach-o image: indirect symbol table local symbol count exceeds total symbols"; + if ( dynSymbTabCmd->ilocalsym + dynSymbTabCmd->nlocalsym < dynSymbTabCmd->ilocalsym ) + throw "malformed mach-o image: indirect symbol table local symbol count wraps"; + if ( (dynSymbTabCmd->nextdefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iextdefsym > symTabCmd->nsyms) ) + throw "malformed mach-o image: indirect symbol table extern symbol count exceeds total symbols"; + if ( dynSymbTabCmd->iextdefsym + dynSymbTabCmd->nextdefsym < dynSymbTabCmd->iextdefsym ) + throw "malformed mach-o image: indirect symbol table extern symbol count wraps"; + if ( (dynSymbTabCmd->nundefsym > symTabCmd->nsyms) || (dynSymbTabCmd->iundefsym > symTabCmd->nsyms) ) + throw "malformed mach-o image: indirect symbol table undefined symbol count exceeds total symbols"; + if ( dynSymbTabCmd->iundefsym + dynSymbTabCmd->nundefsym < dynSymbTabCmd->iundefsym ) + throw "malformed mach-o image: indirect symbol table undefined symbol count wraps"; + } + + + // fSegmentsArrayCount is only 8-bits + if ( *segCount > 255 ) + dyld::throwf("malformed mach-o image: more than 255 segments in %s", path); + + // fSegmentsArrayCount is only 8-bits + if ( *libCount > 4095 ) + dyld::throwf("malformed mach-o image: more than 4095 dependent libraries in %s", path); + + if ( needsAddedLibSystemDepency(*libCount, mh) ) + *libCount = 1; +} + + + +// create image for main executable +ImageLoader* ImageLoaderMachO::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, const LinkContext& context) +{ + //dyld::log("ImageLoader=%ld, ImageLoaderMachO=%ld, ImageLoaderMachOClassic=%ld, ImageLoaderMachOCompressed=%ld\n", + // sizeof(ImageLoader), sizeof(ImageLoaderMachO), sizeof(ImageLoaderMachOClassic), sizeof(ImageLoaderMachOCompressed)); + bool compressed; + unsigned int segCount; + unsigned int libCount; + const linkedit_data_command* codeSigCmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateMainExecutable(mh, slide, path, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + + +// create image by mapping in a mach-o file +ImageLoader* ImageLoaderMachO::instantiateFromFile(const char* path, int fd, const uint8_t firstPages[], size_t firstPagesSize, uint64_t offsetInFat, + uint64_t lenInFat, const struct stat& info, const LinkContext& context) +{ + bool compressed; + unsigned int segCount; + unsigned int libCount; + const linkedit_data_command* codeSigCmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands((const macho_header*)firstPages, path, false, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, encryptCmd, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateFromFile(path, fd, firstPages, firstPagesSize, offsetInFat, lenInFat, info, segCount, libCount, codeSigCmd, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + +// create image by using cached mach-o file +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; + const linkedit_data_command* codeSigCmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, path, true, &compressed, &segCount, &libCount, context, &codeSigCmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateFromCache(mh, path, slide, info, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + +// create image by copying an in-memory mach-o file +ImageLoader* ImageLoaderMachO::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, const LinkContext& context) +{ + bool compressed; + unsigned int segCount; + unsigned int libCount; + const linkedit_data_command* sigcmd; + const encryption_info_command* encryptCmd; + sniffLoadCommands(mh, moduleName, false, &compressed, &segCount, &libCount, context, &sigcmd, &encryptCmd); + // instantiate concrete class based on content of load commands + if ( compressed ) + return ImageLoaderMachOCompressed::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); + else +#if SUPPORT_CLASSIC_MACHO + return ImageLoaderMachOClassic::instantiateFromMemory(moduleName, mh, len, segCount, libCount, context); +#else + throw "missing LC_DYLD_INFO load command"; +#endif +} + + +int ImageLoaderMachO::crashIfInvalidCodeSignature() +{ + // Now that segments are mapped in, try reading from first executable segment. + // If code signing is enabled the kernel will validate the code signature + // when paging in, and kill the process if invalid. + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + // return read value to ensure compiler does not optimize away load + int* p = (int*)segActualLoadAddress(i); + return *p; + } + } + return 0; +} + + +void ImageLoaderMachO::parseLoadCmds(const LinkContext& context) +{ + // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide + for(unsigned int i=0; i < fSegmentsCount; ++i) { + // set up pointer to __LINKEDIT segment + if ( strcmp(segName(i),"__LINKEDIT") == 0 ) { + if ( context.requireCodeSignature && (segFileOffset(i) > fCoveredCodeLength)) + dyld::throwf("cannot load '%s' (segment outside of code signature)", this->getShortName()); + fLinkEditBase = (uint8_t*)(segActualLoadAddress(i) - segFileOffset(i)); + } +#if TEXT_RELOC_SUPPORT + // __TEXT segment always starts at beginning of file and contains mach_header and load commands + if ( segExecutable(i) ) { + if ( segHasRebaseFixUps(i) && (fSlide != 0) ) + fTextSegmentRebases = true; + if ( segHasBindFixUps(i) ) + fTextSegmentBinds = true; + } +#endif +#if __i386__ + if ( segIsReadOnlyImport(i) ) + fReadOnlyImportSegment = true; +#endif + // some segment always starts at beginning of file and contains mach_header and load commands + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + fMachOData = (uint8_t*)(segActualLoadAddress(i)); + } + } + + // keep count of prebound images with weak exports + if ( this->participatesInCoalescing() ) { + ++fgImagesRequiringCoalescing; + fRegisteredAsRequiresCoalescing = true; + if ( this->hasCoalescedExports() ) + ++fgImagesHasWeakDefinitions; + } + + // keep count of images used in shared cache + if ( fInSharedCache ) + ++fgImagesUsedFromSharedCache; + + // walk load commands (mapped in at start of __TEXT segment) + const dyld_info_command* dyldInfo = NULL; + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const struct load_command* firstUnknownCmd = NULL; + const struct version_min_command* minOSVersionCmd = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + case LC_SUB_UMBRELLA: + fHasSubUmbrella = true; + break; + case LC_SUB_FRAMEWORK: + fInUmbrella = true; + break; + case LC_SUB_LIBRARY: + fHasSubLibraries = true; + break; + case LC_ROUTINES_COMMAND: + fHasDashInit = true; + break; + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + dyldInfo = (struct dyld_info_command*)cmd; + break; + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const bool isTextSeg = (strcmp(seg->segname, "__TEXT") == 0); + #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED + const bool isObjCSeg = (strcmp(seg->segname, "__OBJC") == 0); + if ( isObjCSeg ) + fNotifyObjC = true; + #else + const bool isDataSeg = (strncmp(seg->segname, "__DATA", 6) == 0); + #endif + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( type == S_MOD_INIT_FUNC_POINTERS ) + fHasInitializers = true; + else if ( type == S_MOD_TERM_FUNC_POINTERS ) + fHasTerminators = true; + else if ( type == S_DTRACE_DOF ) + fHasDOFSections = true; + else if ( isTextSeg && (strcmp(sect->sectname, "__eh_frame") == 0) ) + fEHFrameSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); + else if ( isTextSeg && (strcmp(sect->sectname, "__unwind_info") == 0) ) + fUnwindInfoSectionOffset = (uint32_t)((uint8_t*)sect - fMachOData); + + #if __i386__ && __MAC_OS_X_VERSION_MIN_REQUIRED + else if ( isObjCSeg ) { + if ( strcmp(sect->sectname, "__image_info") == 0 ) { + const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); + uint32_t flags = imageInfo[1]; + if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) ) + dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath()); + } + else if ( ((macho_header*)fMachOData)->filetype == MH_DYLIB ) { + fRetainForObjC = true; + } + } + #else + else if ( isDataSeg && (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) ) { + #if __MAC_OS_X_VERSION_MIN_REQUIRED + const uint32_t* imageInfo = (uint32_t*)(sect->addr + fSlide); + uint32_t flags = imageInfo[1]; + if ( (flags & 4) && (((macho_header*)fMachOData)->filetype != MH_EXECUTE) ) + dyld::throwf("cannot load '%s' because Objective-C garbage collection is not supported", getPath()); + #endif + fNotifyObjC = true; + } + else if ( isDataSeg && (strncmp(sect->sectname, "__objc_", 7) == 0) && (((macho_header*)fMachOData)->filetype == MH_DYLIB) ) + fRetainForObjC = true; + #endif + } + } + break; + case LC_TWOLEVEL_HINTS: + // no longer supported + break; + case LC_ID_DYLIB: + { + fDylibIDOffset = (uint32_t)((uint8_t*)cmd - fMachOData); + } + break; + case LC_RPATH: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + case LC_MAIN: + // do nothing, just prevent LC_REQ_DYLD exception from occuring + break; + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + minOSVersionCmd = (version_min_command*)cmd; + break; + default: + if ( (cmd->cmd & LC_REQ_DYLD) != 0 ) { + if ( firstUnknownCmd == NULL ) + firstUnknownCmd = cmd; + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( firstUnknownCmd != NULL ) { + if ( minOSVersionCmd != NULL ) { + dyld::throwf("cannot load '%s' because it was built for OS version %u.%u (load command 0x%08X is unknown)", + this->getShortName(), + minOSVersionCmd->version >> 16, ((minOSVersionCmd->version >> 8) & 0xff), + firstUnknownCmd->cmd); + } + else { + dyld::throwf("cannot load '%s' (load command 0x%08X is unknown)", this->getShortName(), firstUnknownCmd->cmd); + } + } + + + if ( dyldInfo != NULL ) + this->setDyldInfo(dyldInfo); + if ( symbolTable != NULL) + this->setSymbolTableInfo(symbolTable, symbolTableStrings, dynSymbolTable); + +} + +// don't do this work in destructor because we need object to be full subclass +// for UnmapSegments() to work +void ImageLoaderMachO::destroy() +{ + // update count of images with weak exports + if ( fRegisteredAsRequiresCoalescing ) { + --fgImagesRequiringCoalescing; + if ( this->hasCoalescedExports() ) + --fgImagesHasWeakDefinitions; + } + + // keep count of images used in shared cache + if ( fInSharedCache ) + --fgImagesUsedFromSharedCache; + + // unmap image when done + UnmapSegments(); +} + + +unsigned int ImageLoaderMachO::segmentCount() const +{ + return fSegmentsCount; +} + + +const macho_segment_command* ImageLoaderMachO::segLoadCommand(unsigned int segIndex) const +{ + uint32_t* lcOffsets = this->segmentCommandOffsets(); + uint32_t lcOffset = lcOffsets[segIndex]; + return (macho_segment_command*)(&fMachOData[lcOffset]); +} + +const char* ImageLoaderMachO::segName(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->segname; +} + + +uintptr_t ImageLoaderMachO::segSize(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmsize; +} + + +uintptr_t ImageLoaderMachO::segFileSize(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->filesize; +} + + +bool ImageLoaderMachO::segHasTrailingZeroFill(unsigned int segIndex) +{ + return ( segWriteable(segIndex) && (segSize(segIndex) > segFileSize(segIndex)) ); +} + + +uintptr_t ImageLoaderMachO::segFileOffset(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->fileoff; +} + + +bool ImageLoaderMachO::segReadable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_READ) != 0); +} + + +bool ImageLoaderMachO::segWriteable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_WRITE) != 0); +} + + +bool ImageLoaderMachO::segExecutable(unsigned int segIndex) const +{ + return ( (segLoadCommand(segIndex)->initprot & VM_PROT_EXECUTE) != 0); +} + + +bool ImageLoaderMachO::segUnaccessible(unsigned int segIndex) const +{ + return (segLoadCommand(segIndex)->initprot == 0); +} + +bool ImageLoaderMachO::segHasPreferredLoadAddress(unsigned int segIndex) const +{ + return (segLoadCommand(segIndex)->vmaddr != 0); +} + +uintptr_t ImageLoaderMachO::segPreferredLoadAddress(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmaddr; +} + +uintptr_t ImageLoaderMachO::segActualLoadAddress(unsigned int segIndex) const +{ + return segLoadCommand(segIndex)->vmaddr + fSlide; +} + + +uintptr_t ImageLoaderMachO::segActualEndAddress(unsigned int segIndex) const +{ + return segActualLoadAddress(segIndex) + segSize(segIndex); +} + +bool ImageLoaderMachO::segHasRebaseFixUps(unsigned int segIndex) const +{ +#if TEXT_RELOC_SUPPORT + // scan sections for fix-up bit + const macho_segment_command* segCmd = segLoadCommand(segIndex); + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & S_ATTR_LOC_RELOC) != 0 ) + return true; + } +#endif + return false; +} + +bool ImageLoaderMachO::segHasBindFixUps(unsigned int segIndex) const +{ +#if TEXT_RELOC_SUPPORT + // scan sections for fix-up bit + const macho_segment_command* segCmd = segLoadCommand(segIndex); + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)segCmd + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[segCmd->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & S_ATTR_EXT_RELOC) != 0 ) + return true; + } +#endif + return false; +} + +#if __i386__ +bool ImageLoaderMachO::segIsReadOnlyImport(unsigned int segIndex) const +{ + const macho_segment_command* segCmd = segLoadCommand(segIndex); + return ( (segCmd->initprot & VM_PROT_EXECUTE) + && ((segCmd->initprot & VM_PROT_WRITE) == 0) + && (strcmp(segCmd->segname, "__IMPORT") == 0) ); +} +#endif + + +void ImageLoaderMachO::UnmapSegments() +{ + // usually unmap image when done + if ( ! this->leaveMapped() && (this->getState() >= dyld_image_state_mapped) ) { + // unmap TEXT segment last because it contains load command being inspected + unsigned int textSegmentIndex = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + //dyld::log("unmap %s at 0x%08lX\n", seg->getName(), seg->getActualLoadAddress(this)); + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + textSegmentIndex = i; + } + else { + // update stats + --ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped -= segSize(i); + munmap((void*)segActualLoadAddress(i), segSize(i)); + } + } + // now unmap TEXT + --ImageLoader::fgTotalSegmentsMapped; + ImageLoader::fgTotalBytesMapped -= segSize(textSegmentIndex); + munmap((void*)segActualLoadAddress(textSegmentIndex), segSize(textSegmentIndex)); + } +} + + +// prefetch __DATA/__OBJC pages during launch, but not for dynamically loaded code +void ImageLoaderMachO::preFetchDATA(int fd, uint64_t offsetInFat, const LinkContext& context) +{ + if ( context.linkingMainExecutable ) { + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + if ( segWriteable(i) && (segFileSize(i) > 0) ) { + // prefetch writable segment that have mmap'ed regions + radvisory advice; + advice.ra_offset = offsetInFat + segFileOffset(i); + advice.ra_count = (int)segFileSize(i); + // limit prefetch to 1MB (256 pages) + if ( advice.ra_count > 1024*1024 ) + advice.ra_count = 1024*1024; + // don't prefetch single pages, let them fault in + fgTotalBytesPreFetched += advice.ra_count; + fcntl(fd, F_RDADVISE, &advice); + if ( context.verboseMapping ) { + dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", + segName(i), segActualLoadAddress(i), segActualLoadAddress(i)+advice.ra_count-1); + } + } + } + } +} + + +bool ImageLoaderMachO::segmentsMustSlideTogether() const +{ + return true; +} + +bool ImageLoaderMachO::segmentsCanSlide() const +{ + return (this->isDylib() || this->isBundle() || this->isPositionIndependentExecutable()); +} + +bool ImageLoaderMachO::isBundle() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_BUNDLE ); +} + +bool ImageLoaderMachO::isDylib() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_DYLIB ); +} + +bool ImageLoaderMachO::isExecutable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( mh->filetype == MH_EXECUTE ); +} + +bool ImageLoaderMachO::isPositionIndependentExecutable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->filetype == MH_EXECUTE) && ((mh->flags & MH_PIE) != 0) ); +} + + +bool ImageLoaderMachO::forceFlat() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_FORCE_FLAT) != 0 ); +} + +bool ImageLoaderMachO::usesTwoLevelNameSpace() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_TWOLEVEL) != 0 ); +} + +bool ImageLoaderMachO::isPrebindable() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_PREBOUND) != 0 ); +} + +bool ImageLoaderMachO::hasCoalescedExports() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_WEAK_DEFINES) != 0 ); +} + +bool ImageLoaderMachO::hasReferencesToWeakSymbols() const +{ + const macho_header* mh = (macho_header*)fMachOData; + return ( (mh->flags & MH_BINDS_TO_WEAK) != 0 ); +} + +bool ImageLoaderMachO::participatesInCoalescing() const +{ + const macho_header* mh = (macho_header*)fMachOData; + // if image is loaded with RTLD_LOCAL, then its symbols' visibility + // is reduced and it can't coalesce with other images + if ( this->hasHiddenExports() ) + return false; + return ( (mh->flags & (MH_WEAK_DEFINES|MH_BINDS_TO_WEAK)) != 0 ); +} + + + +void ImageLoaderMachO::setSlide(intptr_t slide) +{ + fSlide = slide; +} + +void ImageLoaderMachO::loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context) +{ + // if dylib being loaded has no code signature load command + if ( codeSigCmd == NULL) { + if (context.requireCodeSignature ) { + // if we require dylibs to be codesigned there needs to be a signature. + dyld::throwf("required code signature missing for '%s'\n", this->getPath()); + } else { + disableCoverageCheck(); + } + } + else { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // ignore code signatures in binaries built with pre-10.9 tools + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } +#endif + fsignatures_t siginfo; + siginfo.fs_file_start=offsetInFatFile; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of CD in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of CD + int result = fcntl(fd, F_ADDFILESIGS_RETURN, &siginfo); + +#if TARGET_IPHONE_SIMULATOR + // rdar://problem/18759224> check range covered by the code directory after loading + // Attempt to fallback only if we are in the simulator + + if ( result == -1 ) { + result = fcntl(fd, F_ADDFILESIGS, &siginfo); + siginfo.fs_file_start = codeSigCmd->dataoff; + } +#endif + + if ( result == -1 ) { + if ( (errno == EPERM) || (errno == EBADEXEC) ) + dyld::throwf("code signature invalid for '%s'\n", this->getPath()); + if ( context.verboseCodeSignatures ) + dyld::log("dyld: Failed registering code signature for %s, errno=%d\n", this->getPath(), errno); + siginfo.fs_file_start = UINT64_MAX; + } else if ( context.verboseCodeSignatures ) { + dyld::log("dyld: Registered code signature for %s\n", this->getPath()); + } + fCoveredCodeLength = siginfo.fs_file_start; + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processUsingLibraryValidation ) { + fchecklv checkInfo; + char messageBuffer[512]; + messageBuffer[0] = '\0'; + checkInfo.lv_file_start = offsetInFatFile; + checkInfo.lv_error_message_size = sizeof(messageBuffer); + checkInfo.lv_error_message = messageBuffer; + int res = fcntl(fd, F_CHECK_LV, &checkInfo); + if ( res == -1 ) { + dyld::throwf("code signature in (%s) not valid for use in process using Library Validation: %s", this->getPath(), messageBuffer); + } + } +#endif + } +} + +void ImageLoaderMachO::validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // rdar://problem/21839703> 15A226d: dyld crashes in mageLoaderMachO::validateFirstPages during dlopen() after encountering an mmap failure + // We need to ignore older code signatures because they will be bad. + if ( this->sdkVersion() < DYLD_MACOSX_VERSION_10_9 ) { + return; + } +#endif + if (codeSigCmd != NULL) { + void *fdata = xmmap(NULL, lenFileData, PROT_READ | PROT_EXEC, MAP_SHARED, fd, offsetInFat); + if ( fdata == MAP_FAILED ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processUsingLibraryValidation ) { + dyld::throwf("cannot load image with wrong team ID in process using Library Validation"); + } + else +#endif + { + int errnoCopy = errno; + if ( errnoCopy == EPERM ) { + if ( dyld::sandboxBlockedMmap(getPath()) ) + dyld::throwf("file system sandbox blocked mmap() of '%s'", getPath()); + else + dyld::throwf("code signing blocked mmap() of '%s'", getPath()); + } + else + dyld::throwf("mmap() errno=%d validating first page of '%s'", errnoCopy, getPath()); + } + } + if ( memcmp(fdata, fileData, lenFileData) != 0 ) + dyld::throwf("mmap() page compare failed for '%s'", getPath()); + munmap(fdata, lenFileData); + } +} + + +const char* ImageLoaderMachO::getInstallPath() const +{ + if ( fDylibIDOffset != 0 ) { + const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); + return (char*)dylibID + dylibID->dylib.name.offset; + } + return NULL; +} + +void ImageLoaderMachO::registerInterposing() +{ + // mach-o files advertise interposing by having a __DATA __interpose section + 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)) ) { + // Ensure section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("interpose section has malformed address range for %s\n", this->getPath()); + const InterposeData* interposeArray = (InterposeData*)(sect->addr + fSlide); + const size_t count = sect->size / sizeof(InterposeData); + for (size_t j=0; j < count; ++j) { + ImageLoader::InterposeTuple tuple; + tuple.replacement = interposeArray[j].replacement; + tuple.neverImage = this; + tuple.onlyImage = NULL; + tuple.replacee = interposeArray[j].replacee; + // ignore interposing on a weak function that does not exist + if ( tuple.replacee == 0 ) + continue; + // verify that replacement is in this image + if ( this->containsAddress((void*)tuple.replacement) ) { + // chain to any existing interpositions + for (std::vector::iterator it=fgInterposingTuples.begin(); it != fgInterposingTuples.end(); it++) { + if ( it->replacee == tuple.replacee ) { + tuple.replacee = it->replacement; + } + } + ImageLoader::fgInterposingTuples.push_back(tuple); + } + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +uint32_t ImageLoaderMachO::sdkVersion(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct version_min_command* versCmd; + const struct build_version_command* buildVersCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + return versCmd->sdk; + case LC_BUILD_VERSION: + buildVersCmd = (build_version_command*)cmd; + return buildVersCmd->sdk; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +uint32_t ImageLoaderMachO::sdkVersion() const +{ + return ImageLoaderMachO::sdkVersion(machHeader()); +} + +uint32_t ImageLoaderMachO::minOSVersion(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh) + sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct version_min_command* versCmd; + const struct build_version_command* buildVersCmd; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_VERSION_MIN_MACOSX: + case LC_VERSION_MIN_IPHONEOS: + case LC_VERSION_MIN_TVOS: + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + return versCmd->version; + case LC_BUILD_VERSION: + buildVersCmd = (build_version_command*)cmd; + return buildVersCmd->minos; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +uint32_t ImageLoaderMachO::minOSVersion() const +{ + return ImageLoaderMachO::minOSVersion(machHeader()); +} + + +void* ImageLoaderMachO::getThreadPC() const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + 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_MAIN ) { + entry_point_command* mainCmd = (entry_point_command*)cmd; + void* entry = (void*)(mainCmd->entryoff + (char*)fMachOData); + // verify entry point is in image + if ( this->containsAddress(entry) ) + return entry; + else + throw "LC_MAIN entryoff is out of range"; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return NULL; +} + + +void* ImageLoaderMachO::getMain() const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_UNIXTHREAD: + { + #if __i386__ + const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->eip + fSlide); + #elif __x86_64__ + const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->rip + fSlide); + #elif __arm__ + const arm_thread_state_t* registers = (arm_thread_state_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->__pc + fSlide); + #elif __arm64__ + const arm_thread_state64_t* registers = (arm_thread_state64_t*)(((char*)cmd) + 16); + void* entry = (void*)(registers->__pc + fSlide); + #else + #warning need processor specific code + #endif + // verify entry point is in image + if ( this->containsAddress(entry) ) { + return entry; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + throw "no valid entry point"; +} + +bool ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh) +{ + // ensure that every image depends on something which depends on libSystem + if ( libCount > 1 ) + return false; + + // dyld implicit-libSystem breaks valgrind + if ( mh->filetype == MH_EXECUTE ) + return false; + + bool isNonOSdylib = false; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)((uint8_t*)mh+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + return false; + case LC_ID_DYLIB: + { + const dylib_command* dylibID = (dylib_command*)cmd; + const char* installPath = (char*)cmd + dylibID->dylib.name.offset; + // It is OK for OS dylibs (libSystem or libmath or Rosetta shims) to have no dependents + // but all other dylibs must depend on libSystem for initialization to initialize libSystem first + // rosetta circular dependency spew + isNonOSdylib = ( (strncmp(installPath, "/usr/lib/", 9) != 0) && (strncmp(installPath, "/usr/libexec/oah/Shims", 9) != 0) ); + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return isNonOSdylib; +} + + +void ImageLoaderMachO::doGetDependentLibraries(DependentLibraryInfo libs[]) +{ + if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) { + DependentLibraryInfo* lib = &libs[0]; + lib->name = LIBSYSTEM_DYLIB_PATH; + lib->info.checksum = 0; + lib->info.minVersion = 0; + lib->info.maxVersion = 0; + lib->required = false; + lib->reExported = false; + lib->upward = false; + } + else { + uint32_t index = 0; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + { + const struct dylib_command* dylib = (struct dylib_command*)cmd; + DependentLibraryInfo* lib = &libs[index++]; + lib->name = (char*)cmd + dylib->dylib.name.offset; + //lib->name = strdup((char*)cmd + dylib->dylib.name.offset); + lib->info.checksum = dylib->dylib.timestamp; + lib->info.minVersion = dylib->dylib.compatibility_version; + lib->info.maxVersion = dylib->dylib.current_version; + lib->required = (cmd->cmd != LC_LOAD_WEAK_DYLIB); + lib->reExported = (cmd->cmd == LC_REEXPORT_DYLIB); + lib->upward = (cmd->cmd == LC_LOAD_UPWARD_DYLIB); + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + +ImageLoader::LibraryInfo ImageLoaderMachO::doGetLibraryInfo(const LibraryInfo&) +{ + LibraryInfo info; + if ( fDylibIDOffset != 0 ) { + const dylib_command* dylibID = (dylib_command*)(&fMachOData[fDylibIDOffset]); + info.minVersion = dylibID->dylib.compatibility_version; + info.maxVersion = dylibID->dylib.current_version; + info.checksum = dylibID->dylib.timestamp; + } + else { + info.minVersion = 0; + info.maxVersion = 0; + info.checksum = 0; + } + return info; +} + +void ImageLoaderMachO::getRPaths(const LinkContext& context, std::vector& paths) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (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", 12) == 0) && ((path[12] == '/') || (path[12] == '\0')) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processIsRestricted && (context.mainExecutable == this) ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @loader_path\n", path, this->getPath()); + break; + } +#endif + char resolvedPath[PATH_MAX]; + if ( realpath(this->getPath(), resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + char* addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) { + strcpy(addPoint, &path[12]); + pathToAdd = strdup(newRealPath); + } + } + } + else if ( (strncmp(path, "@executable_path", 16) == 0) && ((path[16] == '/') || (path[16] == '\0')) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( context.processIsRestricted ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because of @executable_path\n", path, this->getPath()); + break; + } +#endif + char resolvedPath[PATH_MAX]; + if ( realpath(context.mainExecutable->getPath(), resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + char* addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) { + strcpy(addPoint, &path[16]); + pathToAdd = strdup(newRealPath); + } + } + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + else if ( (path[0] != '/') && context.processIsRestricted ) { + dyld::warn("LC_RPATH %s in %s being ignored in restricted program because it is a relative path\n", path, this->getPath()); + break; + } +#endif +#if SUPPORT_ROOT_PATH + else if ( (path[0] == '/') && (context.rootPaths != NULL) ) { + // DYLD_ROOT_PATH should apply to LC_RPATH rpaths + // DYLD_ROOT_PATH can be a list of paths, but at this point we can only support one, so use first combination that exists + bool found = false; + for(const char** rp = context.rootPaths; *rp != NULL; ++rp) { + char newPath[PATH_MAX]; + strlcpy(newPath, *rp, PATH_MAX); + strlcat(newPath, path, PATH_MAX); + struct stat stat_buf; + if ( stat(newPath, &stat_buf) != -1 ) { + //dyld::log("combined DYLD_ROOT_PATH and LC_RPATH: %s\n", newPath); + pathToAdd = strdup(newPath); + found = true; + break; + } + } + if ( ! found ) { + // make copy so that all elements of 'paths' can be freed + pathToAdd = strdup(path); + } + } +#endif + else { + // make copy so that all elements of 'paths' can be freed + pathToAdd = strdup(path); + } + if ( pathToAdd != NULL ) + paths.push_back(pathToAdd); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + + +bool ImageLoaderMachO::getUUID(uuid_t uuid) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_UUID: + uuid_command* uc = (uuid_command*)cmd; + memcpy(uuid, uc->uuid, 16); + return true; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + bzero(uuid, 16); + return false; +} + +void ImageLoaderMachO::doRebase(const LinkContext& context) +{ + // Delay calling setNeverUnload() until we know this is not for dlopen_preflight() + if ( fRetainForObjC ) + this->setNeverUnload(); + + // if prebound and loaded at prebound address, then no need to rebase + if ( this->usablePrebinding(context) ) { + // skip rebasing because prebinding is valid + ++fgImagesWithUsedPrebinding; // bump totals for statistics + return; + } + + // print why prebinding was not used + if ( context.verbosePrebinding ) { + if ( !this->isPrebindable() ) { + dyld::log("dyld: image not prebound, so could not use prebinding in %s\n", this->getPath()); + } + else if ( fSlide != 0 ) { + dyld::log("dyld: image slid, so could not use prebinding in %s\n", this->getPath()); + } + else if ( !this->allDependentLibrariesAsWhenPreBound() ) { + dyld::log("dyld: dependent libraries changed, so could not use prebinding in %s\n", this->getPath()); + } + else if ( !this->usesTwoLevelNameSpace() ){ + dyld::log("dyld: image uses flat-namespace so, parts of prebinding ignored %s\n", this->getPath()); + } + else { + dyld::log("dyld: environment variable disabled use of prebinding in %s\n", this->getPath()); + } + } + + //dyld::log("slide=0x%08lX for %s\n", slide, this->getPath()); + +#if PREBOUND_IMAGE_SUPPORT + // if prebound and we got here, then prebinding is not valid, so reset all lazy pointers + // if this image is in the shared cache, do not reset, they will be bound in doBind() + if ( this->isPrebindable() && !fInSharedCache ) + this->resetPreboundLazyPointers(context); +#endif + + // if loaded at preferred address, no rebasing necessary + if ( this->fSlide == 0 ) + return; + +#if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentRebases ) + this->makeTextSegmentWritable(context, true); +#endif + + // do actual rebasing + this->rebase(context, fSlide); + +#if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentRebases ) + this->makeTextSegmentWritable(context, false); + +#endif +} + +#if TEXT_RELOC_SUPPORT +void ImageLoaderMachO::makeTextSegmentWritable(const LinkContext& context, bool writeable) +{ + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segExecutable(i) ) { + if ( writeable ) { + segMakeWritable(i, context); + } + else { + #if !__i386__ && !__x86_64__ + // some processors require range to be invalidated before it is made executable + sys_icache_invalidate((void*)segActualLoadAddress(i), segSize(textSegmentIndex)); + #endif + segProtect(i, context); + } + } + } + +} +#endif + +const ImageLoader::Symbol* ImageLoaderMachO::findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const +{ + // look in this image first + const ImageLoader::Symbol* result = this->findShallowExportedSymbol(name, foundIn); + if ( result != NULL ) + return result; + + if ( searchReExports ) { + for(unsigned int i=0; i < libraryCount(); ++i){ + if ( libReExported(i) ) { + ImageLoader* image = libImage(i); + if ( image != NULL ) { + const char* reExPath = libPath(i); + result = image->findExportedSymbol(name, searchReExports, reExPath, foundIn); + if ( result != NULL ) + return result; + } + } + } + } + + + return NULL; +} + + + +uintptr_t ImageLoaderMachO::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char* symbolName) const +{ + return this->getSymbolAddress(sym, requestor, context, runResolver); +} + +uintptr_t ImageLoaderMachO::getSymbolAddress(const Symbol* sym, const ImageLoader* requestor, + const LinkContext& context, bool runResolver) const +{ + uintptr_t result = exportedSymbolAddress(context, sym, requestor, runResolver); + // check for interposing overrides + result = interposedAddress(context, result, requestor); + return result; +} + +ImageLoader::DefinitionFlags ImageLoaderMachO::getExportedSymbolInfo(const Symbol* sym) const +{ + if ( exportedSymbolIsWeakDefintion(sym) ) + return kWeakDefinition; + else + return kNoDefinitionOptions; +} + +const char* ImageLoaderMachO::getExportedSymbolName(const Symbol* sym) const +{ + return exportedSymbolName(sym); +} + +uint32_t ImageLoaderMachO::getExportedSymbolCount() const +{ + return exportedSymbolCount(); +} + + +const ImageLoader::Symbol* ImageLoaderMachO::getIndexedExportedSymbol(uint32_t index) const +{ + return exportedSymbolIndexed(index); +} + + +uint32_t ImageLoaderMachO::getImportedSymbolCount() const +{ + return importedSymbolCount(); +} + + +const ImageLoader::Symbol* ImageLoaderMachO::getIndexedImportedSymbol(uint32_t index) const +{ + return importedSymbolIndexed(index); +} + + +ImageLoader::ReferenceFlags ImageLoaderMachO::getImportedSymbolInfo(const ImageLoader::Symbol* sym) const +{ + ImageLoader::ReferenceFlags flags = kNoReferenceOptions; + return flags; +} + + +const char* ImageLoaderMachO::getImportedSymbolName(const ImageLoader::Symbol* sym) const +{ + return importedSymbolName(sym); +} + + +bool ImageLoaderMachO::getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) +{ + 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 ( (strcmp(sect->segname, segmentName) == 0) && (strcmp(sect->sectname, sectionName) == 0) ) { + *start = (uintptr_t*)(sect->addr + fSlide); + *length = sect->size; + return true; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + *start = NULL; + *length = 0; + return false; +} + +void ImageLoaderMachO::getUnwindInfo(dyld_unwind_sections* info) +{ + info->mh = this->machHeader(); + info->dwarf_section = 0; + info->dwarf_section_length = 0; + info->compact_unwind_section = 0; + info->compact_unwind_section_length = 0; + if ( fEHFrameSectionOffset != 0 ) { + const macho_section* sect = (macho_section*)&fMachOData[fEHFrameSectionOffset]; + info->dwarf_section = (void*)(sect->addr + fSlide); + info->dwarf_section_length = sect->size; + } + if ( fUnwindInfoSectionOffset != 0 ) { + const macho_section* sect = (macho_section*)&fMachOData[fUnwindInfoSectionOffset]; + info->compact_unwind_section = (void*)(sect->addr + fSlide); + info->compact_unwind_section_length = sect->size; + } +} + +intptr_t ImageLoaderMachO::computeSlide(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const 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 ( strcmp(seg->segname, "__TEXT") == 0 ) + return (char*)mh - (char*)(seg->vmaddr); + } + cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + +bool ImageLoaderMachO::findSection(const mach_header* mh, const char* segmentName, const char* sectionName, void** sectAddress, size_t* sectSize) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const macho_segment_command* seg = (macho_segment_command*)cmd; + 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 ( (strcmp(sect->segname, segmentName) == 0) && (strcmp(sect->sectname, sectionName) == 0) ) { + *sectAddress = (void*)(sect->addr + computeSlide(mh)); + *sectSize = sect->size; + return true; + } + } + } + break; + } + cmd = (const load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} + + +bool ImageLoaderMachO::findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uintptr_t unslidInteriorAddress = (uintptr_t)imageInterior - this->getSlide(); + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( (unslidInteriorAddress >= seg->vmaddr) && (unslidInteriorAddress < (seg->vmaddr+seg->vmsize)) ) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ((sect->addr <= unslidInteriorAddress) && (unslidInteriorAddress < (sect->addr+sect->size))) { + if ( segmentName != NULL ) + *segmentName = sect->segname; + if ( sectionName != NULL ) + *sectionName = sect->sectname; + if ( sectionOffset != NULL ) + *sectionOffset = unslidInteriorAddress - sect->addr; + return true; + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} + +const char* ImageLoaderMachO::libPath(unsigned int index) const +{ + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + unsigned count = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch ( cmd->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + if ( index == count ) { + const struct dylib_command* dylibCmd = (struct dylib_command*)cmd; + return (char*)cmd + dylibCmd->dylib.name.offset; + } + ++count; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // if image linked with nothing and we implicitly added libSystem.dylib, return that + if ( needsAddedLibSystemDepency(libraryCount(), (macho_header*)fMachOData) ) { + return LIBSYSTEM_DYLIB_PATH; + } + + return NULL; +} + + +void __attribute__((noreturn)) ImageLoaderMachO::throwSymbolNotFound(const LinkContext& context, const char* symbol, + const char* referencedFrom, const char* fromVersMismatch, + const char* expectedIn) +{ + // record values for possible use by CrashReporter or Finder + (*context.setErrorStrings)(DYLD_EXIT_REASON_SYMBOL_MISSING, referencedFrom, expectedIn, symbol); + dyld::throwf("Symbol not found: %s\n Referenced from: %s%s\n Expected in: %s\n", + symbol, referencedFrom, fromVersMismatch, expectedIn); +} + +const mach_header* ImageLoaderMachO::machHeader() const +{ + return (mach_header*)fMachOData; +} + +uintptr_t ImageLoaderMachO::getSlide() const +{ + return fSlide; +} + +// hmm. maybe this should be up in ImageLoader?? +const void* ImageLoaderMachO::getEnd() const +{ + uintptr_t lastAddress = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + uintptr_t segEnd = segActualEndAddress(i); + if ( strcmp(segName(i), "__UNIXSTACK") != 0 ) { + if ( segEnd > lastAddress ) + lastAddress = segEnd; + } + } + return (const void*)lastAddress; +} + + +uintptr_t ImageLoaderMachO::bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, + uint8_t type, const char* symbolName, + intptr_t addend, const char* inPath, const char* toPath, const char* msg) +{ + // log + if ( context.verboseBind ) { + if ( addend != 0 ) + dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX + %ld\n", + msg, shortName(inPath), (uintptr_t)location, + ((toPath != NULL) ? shortName(toPath) : ""), + symbolName, (uintptr_t)location, value, addend); + else + dyld::log("dyld: %sbind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX\n", + msg, shortName(inPath), (uintptr_t)location, + ((toPath != NULL) ? shortName(toPath) : ""), + 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; + uint32_t* loc32; + uintptr_t newValue = value+addend; + uint32_t value32; + switch (type) { + case BIND_TYPE_POINTER: + // test first so we don't needless dirty pages + if ( *locationToFix != newValue ) + *locationToFix = newValue; + break; + case BIND_TYPE_TEXT_ABSOLUTE32: + loc32 = (uint32_t*)locationToFix; + value32 = (uint32_t)newValue; + if ( *loc32 != value32 ) + *loc32 = value32; + break; + case BIND_TYPE_TEXT_PCREL32: + loc32 = (uint32_t*)locationToFix; + value32 = (uint32_t)(newValue - (((uintptr_t)locationToFix) + 4)); + if ( *loc32 != value32 ) + *loc32 = value32; + break; + default: + dyld::throwf("bad bind type %d", type); + } + + // update statistics + ++fgTotalBindFixups; + + return newValue; +} + + + + + +#if SUPPORT_OLD_CRT_INITIALIZATION +// first 16 bytes of "start" in crt1.o +#if __i386__ + static uint8_t sStandardEntryPointInstructions[16] = { 0x6a, 0x00, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0x8b, 0x5d, 0x04, 0x89, 0x5c, 0x24 }; +#endif +#endif + +struct DATAdyld { + void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper + void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup + // the following only exist in main executables built for 10.5 or later + ProgramVars vars; +}; + +// These are defined in dyldStartup.s +extern "C" void stub_binding_helper(); +extern "C" int _dyld_func_lookup(const char* name, void** address); + +void ImageLoaderMachO::setupLazyPointerHandler(const LinkContext& context) +{ + 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; + // There used to be some optimizations to skip this section scan, but we need to handle the + // __dyld section in libdyld.dylib, so everything needs to be scanned for now. + // CrashTracer: 1,295 crashes in bash at bash: getenv + if ( true ) { + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( strncmp(seg->segname, "__DATA", 6) == 0 ) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( strcmp(sect->sectname, "__dyld" ) == 0 ) { + struct DATAdyld* dd = (struct DATAdyld*)(sect->addr + fSlide); + #if !__arm64__ && !__ARM_ARCH_7K__ + if ( sect->size > offsetof(DATAdyld, dyldLazyBinder) ) { + if ( dd->dyldLazyBinder != (void*)&stub_binding_helper ) + dd->dyldLazyBinder = (void*)&stub_binding_helper; + } + #endif // !__arm64__ + if ( sect->size > offsetof(DATAdyld, dyldFuncLookup) ) { + if ( dd->dyldFuncLookup != (void*)&_dyld_func_lookup ) + dd->dyldFuncLookup = (void*)&_dyld_func_lookup; + } + if ( mh->filetype == MH_EXECUTE ) { + // there are two ways to get the program variables + if ( (sect->size > offsetof(DATAdyld, vars)) && (dd->vars.mh == mh) ) { + // some really old binaries have space for vars, but it is zero filled + // main executable has 10.5 style __dyld section that has program variable pointers + context.setNewProgramVars(dd->vars); + } + else { + // main executable is pre-10.5 and requires the symbols names to be looked up + this->lookupProgramVars(context); + #if SUPPORT_OLD_CRT_INITIALIZATION + // If the first 16 bytes of the entry point's instructions do not + // match what crt1.o supplies, then the program has a custom entry point. + // This means it might be doing something that needs to be executed before + // initializers are run. + if ( memcmp(this->getMain(), sStandardEntryPointInstructions, 16) != 0 ) { + if ( context.verboseInit ) + dyld::log("dyld: program uses non-standard entry point so delaying running of initializers\n"); + context.setRunInitialzersOldWay(); + } + #endif + } + } + else if ( mh->filetype == MH_DYLIB ) { + const char* installPath = this->getInstallPath(); + if ( (installPath != NULL) && (strncmp(installPath, "/usr/lib/", 9) == 0) ) { + if ( sect->size > offsetof(DATAdyld, vars) ) { + // use ProgramVars from libdyld.dylib but tweak mh field to correct value + dd->vars.mh = context.mainExecutable->machHeader(); + context.setNewProgramVars(dd->vars); + } + } + } + } + else if ( (strcmp(sect->sectname, "__program_vars" ) == 0) && (mh->filetype == MH_EXECUTE) ) { + // this is a Mac OS X 10.6 or later main executable + struct ProgramVars* pv = (struct ProgramVars*)(sect->addr + fSlide); + context.setNewProgramVars(*pv); + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + +void ImageLoaderMachO::lookupProgramVars(const LinkContext& context) const +{ + ProgramVars vars = context.programVars; + const ImageLoader::Symbol* sym; + + // get mach header directly + vars.mh = (macho_header*)fMachOData; + + // lookup _NXArgc + sym = this->findShallowExportedSymbol("_NXArgc", NULL); + if ( sym != NULL ) + vars.NXArgcPtr = (int*)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + // lookup _NXArgv + sym = this->findShallowExportedSymbol("_NXArgv", NULL); + if ( sym != NULL ) + vars.NXArgvPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + // lookup _environ + sym = this->findShallowExportedSymbol("_environ", NULL); + if ( sym != NULL ) + vars.environPtr = (const char***)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + // lookup __progname + sym = this->findShallowExportedSymbol("___progname", NULL); + if ( sym != NULL ) + vars.__prognamePtr = (const char**)this->getExportedSymbolAddress(sym, context, this, false, NULL); + + 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() && (this->getSlide() == 0)) || fInSharedCache) + && this->usesTwoLevelNameSpace() + && this->allDependentLibrariesAsWhenPreBound() ) { + // allow environment variables to disable prebinding + if ( context.bindFlat ) + return false; + switch ( context.prebindUsage ) { + case kUseAllPrebinding: + return true; + case kUseSplitSegPrebinding: + return this->fIsSplitSeg; + case kUseAllButAppPredbinding: + return (this != context.mainExecutable); + case kUseNoPrebinding: + return false; + } + } + return false; +} + + +void ImageLoaderMachO::doImageInit(const LinkContext& context) +{ + if ( fHasDashInit ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_ROUTINES_COMMAND: + Initializer func = (Initializer)(((struct macho_routines_command*)cmd)->init_address + fSlide); + // verify initializers are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); + } + if ( ! dyld::gProcessInfo->libSystemInitialized ) { + // libSystem initializer must run first + dyld::throwf("-init function in image (%s) that does not link with libSystem.dylib\n", this->getPath()); + } + if ( context.verboseInit ) + dyld::log("dyld: calling -init function %p in %s\n", func, this->getPath()); + dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{ + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + }); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + +void ImageLoaderMachO::doModInitFunctions(const LinkContext& context) +{ + if ( fHasInitializers ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (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_MOD_INIT_FUNC_POINTERS ) { + Initializer* inits = (Initializer*)(sect->addr + fSlide); + const size_t count = sect->size / sizeof(uintptr_t); + // Ensure __mod_init_func section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("__mod_init_funcs section has malformed address range for %s\n", this->getPath()); + for (size_t j=0; j < count; ++j) { + Initializer func = inits[j]; + // verify initializers are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("initializer function %p not in mapped image for %s\n", func, this->getPath()); + } + if ( ! dyld::gProcessInfo->libSystemInitialized ) { + // libSystem initializer must run first + const char* installPath = getInstallPath(); + if ( (installPath == NULL) || (strcmp(installPath, LIBSYSTEM_DYLIB_PATH) != 0) ) + dyld::throwf("initializer in image (%s) that does not link with libSystem.dylib\n", this->getPath()); + } + if ( context.verboseInit ) + dyld::log("dyld: calling initializer function %p in %s\n", func, this->getPath()); + bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); + dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{ + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + }); + bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL); + if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) { + // now safe to use malloc() and other calls in libSystem.dylib + dyld::gProcessInfo->libSystemInitialized = true; + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + + +void ImageLoaderMachO::doGetDOFSections(const LinkContext& context, std::vector& dofs) +{ + if ( fHasDOFSections ) { + // walk load commands (mapped in at start of __TEXT segment) + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if ( (sect->flags & SECTION_TYPE) == S_DTRACE_DOF ) { + // Ensure section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("DOF section has malformed address range for %s\n", this->getPath()); + ImageLoader::DOFInfo info; + info.dof = (void*)(sect->addr + fSlide); + info.imageHeader = this->machHeader(); + info.imageShortName = this->getShortName(); + dofs.push_back(info); + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + +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() +{ + return ( fHasDashInit || fHasInitializers ); +} + + +bool ImageLoaderMachO::needsTermination() +{ + return fHasTerminators; +} + + +void ImageLoaderMachO::doTermination(const LinkContext& context) +{ + if ( fHasTerminators ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (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_MOD_TERM_FUNC_POINTERS ) { + // Ensure section is within segment + if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) + dyld::throwf("DOF section has malformed address range for %s\n", this->getPath()); + Terminator* terms = (Terminator*)(sect->addr + fSlide); + const size_t count = sect->size / sizeof(uintptr_t); + for (size_t j=count; j > 0; --j) { + Terminator func = terms[j-1]; + // verify terminators are in image + if ( ! this->containsAddress((void*)func) ) { + dyld::throwf("termination function %p not in mapped image for %s\n", func, this->getPath()); + } + if ( context.verboseInit ) + dyld::log("dyld: calling termination function %p in %s\n", func, this->getPath()); + func(); + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} + + +void ImageLoaderMachO::printStatisticsDetails(unsigned int imageCount, const InitializerTimingList& timingInfo) +{ + ImageLoader::printStatisticsDetails(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 weak symbols: %u\n", fgImagesHasWeakDefinitions); + dyld::log("total images using weak symbols: %u\n", fgImagesRequiringCoalescing); +} + + +intptr_t ImageLoaderMachO::assignSegmentAddresses(const LinkContext& context) +{ + // preflight and calculate slide if needed + const bool inPIE = (fgNextPIEDylibAddress != 0); + intptr_t slide = 0; + if ( this->segmentsCanSlide() && this->segmentsMustSlideTogether() ) { + bool needsToSlide = false; + bool imageHasPreferredLoadAddress = segHasPreferredLoadAddress(0); + uintptr_t lowAddr = (unsigned long)(-1); + uintptr_t highAddr = 0; + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + const uintptr_t segLow = segPreferredLoadAddress(i); + const uintptr_t segHigh = dyld_page_round(segLow + segSize(i)); + if ( segLow < highAddr ) { + if ( dyld_page_size > 4096 ) + dyld::throwf("can't map segments into 16KB pages"); + else + dyld::throwf("overlapping segments"); + } + if ( segLow < lowAddr ) + lowAddr = segLow; + if ( segHigh > highAddr ) + highAddr = segHigh; + + if ( needsToSlide || !imageHasPreferredLoadAddress || inPIE || !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) + needsToSlide = true; + } + if ( needsToSlide ) { + // find a chunk of address space to hold all segments + uintptr_t addr = reserveAnAddressRange(highAddr-lowAddr, context); + slide = addr - lowAddr; + } + } + else if ( ! this->segmentsCanSlide() ) { + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + if ( (strcmp(segName(i), "__PAGEZERO") == 0) && (segFileSize(i) == 0) && (segPreferredLoadAddress(i) == 0) ) + continue; + if ( !reserveAddressRange(segPreferredLoadAddress(i), segSize(i)) ) + dyld::throwf("can't map unslidable segment %s to 0x%lX with size 0x%lX", segName(i), segPreferredLoadAddress(i), segSize(i)); + } + } + else { + throw "mach-o does not support independently sliding segments"; + } + return slide; +} + + +uintptr_t ImageLoaderMachO::reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context) +{ + vm_address_t addr = 0; + vm_size_t size = length; + // in PIE programs, load initial dylibs after main executable so they don't have fixed addresses either + if ( fgNextPIEDylibAddress != 0 ) { + // add small (0-3 pages) random padding between dylibs + addr = fgNextPIEDylibAddress + (__stack_chk_guard/fgNextPIEDylibAddress & (sizeof(long)-1))*dyld_page_size; + //dyld::log("padding 0x%08llX, guard=0x%08llX\n", (long long)(addr - fgNextPIEDylibAddress), (long long)(__stack_chk_guard)); + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); + if ( r == KERN_SUCCESS ) { + fgNextPIEDylibAddress = addr + size; + return addr; + } + fgNextPIEDylibAddress = 0; + } + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_DYLIB)); + if ( r != KERN_SUCCESS ) + throw "out of address space"; + + return addr; +} + +bool ImageLoaderMachO::reserveAddressRange(uintptr_t start, size_t length) +{ + vm_address_t addr = start; + vm_size_t size = length; + kern_return_t r = vm_alloc(&addr, size, VM_FLAGS_FIXED | VM_MAKE_TAG(VM_MEMORY_DYLIB)); + if ( r != KERN_SUCCESS ) + return false; + return true; +} + + + +void ImageLoaderMachO::mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) +{ + // find address range for image + intptr_t slide = this->assignSegmentAddresses(context); + if ( context.verboseMapping ) { + if ( offsetInFat != 0 ) + dyld::log("dyld: Mapping %s (slice offset=%llu)\n", this->getPath(), (unsigned long long)offsetInFat); + else + dyld::log("dyld: Mapping %s\n", this->getPath()); + } + // map in all segments + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + vm_offset_t fileOffset = (vm_offset_t)(segFileOffset(i) + offsetInFat); + vm_size_t size = segFileSize(i); + uintptr_t requestedLoadAddress = segPreferredLoadAddress(i) + slide; + int protection = 0; + if ( !segUnaccessible(i) ) { + // If has text-relocs, don't set x-bit initially. + // Instead set it later after text-relocs have been done. + if ( segExecutable(i) && !(segHasRebaseFixUps(i) && (slide != 0)) ) + protection |= PROT_EXEC; + if ( segReadable(i) ) + protection |= PROT_READ; + if ( segWriteable(i) ) { + protection |= PROT_WRITE; + // rdar://problem/22525618 force __LINKEDIT to always be mapped read-only + if ( strcmp(segName(i), "__LINKEDIT") == 0 ) + protection = PROT_READ; + } + } + #if __i386__ + // initially map __IMPORT segments R/W so dyld can update them + if ( segIsReadOnlyImport(i) ) + protection |= PROT_WRITE; + #endif + // wholly zero-fill segments have nothing to mmap() in + if ( size > 0 ) { + if ( (fileOffset+size) > fileLen ) { + dyld::throwf("truncated mach-o error: segment %s extends to %llu which is past end of file %llu", + segName(i), (uint64_t)(fileOffset+size), fileLen); + } + void* loadAddress = xmmap((void*)requestedLoadAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, fileOffset); + if ( loadAddress == ((void*)(-1)) ) { + int mmapErr = errno; + if ( mmapErr == EPERM ) { + if ( dyld::sandboxBlockedMmap(getPath()) ) + dyld::throwf("file system sandbox blocked mmap() of '%s'", this->getPath()); + else + dyld::throwf("code signing blocked mmap() of '%s'", this->getPath()); + } + else + dyld::throwf("mmap() errno=%d at address=0x%08lX, size=0x%08lX segment=%s in Segment::map() mapping %s", + mmapErr, 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), 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); +} + +void ImageLoaderMachO::mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context) +{ + // find address range for image + intptr_t slide = this->assignSegmentAddresses(context); + if ( context.verboseMapping ) + dyld::log("dyld: Mapping memory %p\n", memoryImage); + // map in all segments + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + vm_address_t loadAddress = segPreferredLoadAddress(i) + slide; + vm_address_t srcAddr = (uintptr_t)memoryImage + segFileOffset(i); + vm_size_t size = segFileSize(i); + kern_return_t r = vm_copy(mach_task_self(), srcAddr, size, loadAddress); + if ( r != KERN_SUCCESS ) + throw "can't map segment"; + if ( context.verboseMapping ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", segName(i), (uintptr_t)loadAddress, (uintptr_t)loadAddress+size-1); + } + // update slide to reflect load location + this->setSlide(slide); + // set R/W permissions on all segments at slide location + for(unsigned int i=0, e=segmentCount(); i < e; ++i) { + segProtect(i, context); + } +} + + +void ImageLoaderMachO::segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context) +{ + vm_prot_t protection = 0; + if ( !segUnaccessible(segIndex) ) { + if ( segExecutable(segIndex) ) + protection |= PROT_EXEC; + if ( segReadable(segIndex) ) + protection |= PROT_READ; + if ( segWriteable(segIndex) ) + protection |= PROT_WRITE; + } + vm_address_t addr = segActualLoadAddress(segIndex); + vm_size_t size = segSize(segIndex); + const bool setCurrentPermissions = false; + kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); + if ( r != KERN_SUCCESS ) { + 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' : '.' ); + } +} + +void ImageLoaderMachO::segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context) +{ + vm_address_t addr = segActualLoadAddress(segIndex); + vm_size_t size = segSize(segIndex); + const bool setCurrentPermissions = false; + vm_prot_t protection = VM_PROT_WRITE | VM_PROT_READ; + if ( segExecutable(segIndex) && !segHasRebaseFixUps(segIndex) ) + protection |= VM_PROT_EXECUTE; + kern_return_t r = vm_protect(mach_task_self(), addr, size, setCurrentPermissions, protection); + 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' : '.' ); + } +} + + +const char* ImageLoaderMachO::findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr) +{ + // called by dladdr() + // only works with compressed LINKEDIT if classic symbol table is also present + const dysymtab_command* dynSymbolTable = NULL; + const symtab_command* symtab = NULL; + const macho_segment_command* seg; + const uint8_t* unslidLinkEditBase = NULL; + bool linkEditBaseFound = false; + intptr_t slide = 0; + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + seg = (macho_segment_command*)cmd; + if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) { + unslidLinkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff); + linkEditBaseFound = true; + } + else if ( strcmp(seg->segname, "__TEXT") == 0 ) { + slide = (uintptr_t)mh - seg->vmaddr; + } + break; + case LC_SYMTAB: + symtab = (symtab_command*)cmd; + break; + case LC_DYSYMTAB: + dynSymbolTable = (dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + // no symbol table => no lookup by address + if ( (symtab == NULL) || (dynSymbolTable == NULL) || !linkEditBaseFound ) + return NULL; + + const uint8_t* linkEditBase = unslidLinkEditBase + slide; + const char* symbolTableStrings = (const char*)&linkEditBase[symtab->stroff]; + const macho_nlist* symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]); + + uintptr_t targetAddress = (uintptr_t)addr - slide; + const struct macho_nlist* bestSymbol = NULL; + // first walk all global symbols + const struct macho_nlist* const globalsStart = &symbolTable[dynSymbolTable->iextdefsym]; + const struct macho_nlist* const globalsEnd= &globalsStart[dynSymbolTable->nextdefsym]; + for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type & N_TYPE) == N_SECT ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + // next walk all local symbols + const struct macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; + const struct macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; + for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + if ( bestSymbol != NULL ) { +#if __arm__ + if (bestSymbol->n_desc & N_ARM_THUMB_DEF) + *closestAddr = (void*)((bestSymbol->n_value | 1) + slide); + else + *closestAddr = (void*)(bestSymbol->n_value + slide); +#else + *closestAddr = (void*)(bestSymbol->n_value + slide); +#endif + return &symbolTableStrings[bestSymbol->n_un.n_strx]; + } + return NULL; +} + +bool ImageLoaderMachO::getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd, + uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind) +{ + if ( lazyBindingInfoOffset > (lazyInfoEnd-lazyInfoStart) ) + return false; + uint8_t type = BIND_TYPE_POINTER; + uint8_t symboFlags = 0; + bool done = false; + const uint8_t* p = &lazyInfoStart[lazyBindingInfoOffset]; + while ( !done && (p < lazyInfoEnd) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + *doneAfterBind = false; + return true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + *ordinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + *ordinal = (int)read_uleb128(p, lazyInfoEnd); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + *ordinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + *ordinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + *symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + *segIndex = immediate; + *segOffset = read_uleb128(p, lazyInfoEnd); + break; + case BIND_OPCODE_DO_BIND: + *doneAfterBind = ((*p & BIND_OPCODE_MASK) == BIND_OPCODE_DONE); + lazyBindingInfoOffset += p - &lazyInfoStart[lazyBindingInfoOffset]; + return true; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + case BIND_OPCODE_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + return false; + } + } + return false; +} + +const dyld_info_command* ImageLoaderMachO::findDyldInfoLoadCommand(const mach_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_DYLD_INFO: + case LC_DYLD_INFO_ONLY: + return (dyld_info_command*)cmd; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return NULL; +} + + +uintptr_t ImageLoaderMachO::segPreferredAddress(const mach_header* mh, unsigned segIndex) +{ + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (load_command*)((char*)mh + sizeof(macho_header)); + const load_command* cmd = cmds; + unsigned curSegIndex = 0; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + if ( segIndex == curSegIndex ) { + const macho_segment_command* segCmd = (macho_segment_command*)cmd; + return segCmd->vmaddr; + } + ++curSegIndex; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return 0; +} + + + diff --git a/dyld/src/ImageLoaderMachO.h b/dyld/src/ImageLoaderMachO.h new file mode 100644 index 0000000..5c48589 --- /dev/null +++ b/dyld/src/ImageLoaderMachO.h @@ -0,0 +1,253 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-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@ + */ + + +#ifndef __IMAGELOADERMACHO__ +#define __IMAGELOADERMACHO__ + +#include +#include +#include + +#include "ImageLoader.h" +#include "mach-o/dyld_images.h" + + +// +// ImageLoaderMachO is a subclass of ImageLoader which loads mach-o format files. +// +// +class ImageLoaderMachO : public ImageLoader { +public: + 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 firstPages[], size_t firstPagesSize, uint64_t offsetInFat, + uint64_t lenInFat, 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; } + void disableCoverageCheck() { fCoveredCodeLength = UINT64_MAX; } + + const char* getInstallPath() const; + virtual void* getMain() const; + virtual void* getThreadPC() const; + virtual const struct mach_header* machHeader() const; + virtual uintptr_t getSlide() const; + virtual const void* getEnd() const; + virtual bool hasCoalescedExports() const; + virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const; + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char*) const; + virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const; + virtual const char* getExportedSymbolName(const Symbol* sym) const; + virtual uint32_t getExportedSymbolCount() const; + virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const; + virtual uint32_t getImportedSymbolCount() const; + virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const; + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const; + virtual const char* getImportedSymbolName(const Symbol* sym) const; + virtual bool isBundle() const; + virtual bool isDylib() const; + virtual bool isExecutable() const; + virtual bool isPositionIndependentExecutable() const; + virtual bool forceFlat() const; + virtual bool participatesInCoalescing() const; + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const = 0; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned) = 0; + virtual bool incrementCoalIterator(CoalIterator&) = 0; + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex) = 0; + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, 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, 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 void getUnwindInfo(dyld_unwind_sections* info); + virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset); + virtual bool usablePrebinding(const LinkContext& context) const; + virtual unsigned int segmentCount() const; + virtual const char* segName(unsigned int) const; + virtual uintptr_t segSize(unsigned int) const; + virtual uintptr_t segFileSize(unsigned int) const; + virtual bool segHasTrailingZeroFill(unsigned int); + virtual uintptr_t segFileOffset(unsigned int) const; + virtual bool segReadable(unsigned int) const; + virtual bool segWriteable(unsigned int) const; + virtual bool segExecutable(unsigned int) const; + virtual bool segUnaccessible(unsigned int) const; + virtual bool segHasPreferredLoadAddress(unsigned int) const; + virtual uintptr_t segActualLoadAddress(unsigned int) const; + virtual uintptr_t segPreferredLoadAddress(unsigned int) const; + virtual uintptr_t segActualEndAddress(unsigned int) const; + virtual void registerInterposing(); + virtual uint32_t sdkVersion() const; + virtual uint32_t minOSVersion() const; + virtual const char* libPath(unsigned int) const; + virtual bool notifyObjC() const { return fNotifyObjC; } + + static void printStatisticsDetails(unsigned int imageCount, const InitializerTimingList&); + static uint32_t minOSVersion(const mach_header*); + static uint32_t sdkVersion(const mach_header* mh); + static intptr_t computeSlide(const mach_header* mh); + static bool findSection(const mach_header* mh, const char* segmentName, const char* sectionName, void** sectAddress, uintptr_t* sectSize); + static const dyld_info_command* findDyldInfoLoadCommand(const mach_header* mh); + static const char* findClosestSymbol(const mach_header* mh, const void* addr, const void** closestAddr); + static bool getLazyBindingInfo(uint32_t& lazyBindingInfoOffset, const uint8_t* lazyInfoStart, const uint8_t* lazyInfoEnd, + uint8_t* segIndex, uintptr_t* segOffset, int* ordinal, const char** symbolName, bool* doneAfterBind); + static uintptr_t segPreferredAddress(const mach_header* mh, unsigned segIndex); + static uintptr_t bindLocation(const LinkContext& context, uintptr_t location, uintptr_t value, + uint8_t type, const char* symbolName, + intptr_t addend, const char* inPath, const char* toPath, const char* msg); + virtual void rebase(const LinkContext& context, uintptr_t slide) = 0; + + + +protected: + ImageLoaderMachO(const ImageLoaderMachO&); + ImageLoaderMachO(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + virtual ~ImageLoaderMachO() {} + + void operator=(const ImageLoaderMachO&); + + virtual 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 uint32_t* segmentCommandOffsets() const = 0; + virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const = 0; + virtual bool containsSymbol(const void* addr) const = 0; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const = 0; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const = 0; + virtual const char* exportedSymbolName(const Symbol* symbol) const = 0; + virtual unsigned int exportedSymbolCount() const = 0; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const = 0; + virtual unsigned int importedSymbolCount() const = 0; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const = 0; + virtual const char* importedSymbolName(const Symbol* symbol) const = 0; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context) = 0; +#endif + + + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]); + virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo); + virtual void getRPaths(const LinkContext& context, std::vector&) const; + virtual bool getUUID(uuid_t) const; + virtual void doRebase(const LinkContext& context); + virtual void doBind(const LinkContext& context, bool forceLazysBound) = 0; + virtual void doBindJustLazies(const LinkContext& context) = 0; + virtual bool doInitialization(const LinkContext& context); + virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs); + virtual bool needsTermination(); + virtual bool segmentsMustSlideTogether() const; + virtual bool segmentsCanSlide() const; + virtual void setSlide(intptr_t slide); + virtual bool usesTwoLevelNameSpace() const; + virtual bool isPrebindable() const; + +protected: + + void destroy(); + static void sniffLoadCommands(const macho_header* mh, const char* path, bool inCache, bool* compressed, + unsigned int* segCount, unsigned int* libCount, const LinkContext& context, + const linkedit_data_command** codeSigCmd, + const encryption_info_command** encryptCmd); + static bool needsAddedLibSystemDepency(unsigned int libCount, const macho_header* mh); + void loadCodeSignature(const struct linkedit_data_command* codeSigCmd, int fd, uint64_t offsetInFatFile, const LinkContext& context); + void validateFirstPages(const struct linkedit_data_command* codeSigCmd, int fd, const uint8_t *fileData, size_t lenFileData, off_t offsetInFat, const LinkContext& context); + const struct macho_segment_command* segLoadCommand(unsigned int segIndex) const; + void parseLoadCmds(const ImageLoader::LinkContext& context); + int crashIfInvalidCodeSignature(); + bool segHasRebaseFixUps(unsigned int) const; + bool segHasBindFixUps(unsigned int) const; + void segProtect(unsigned int segIndex, const ImageLoader::LinkContext& context); + void segMakeWritable(unsigned int segIndex, const ImageLoader::LinkContext& context); +#if __i386__ + bool segIsReadOnlyImport(unsigned int) const; +#endif + intptr_t assignSegmentAddresses(const LinkContext& context); + uintptr_t reserveAnAddressRange(size_t length, const ImageLoader::LinkContext& context); + bool reserveAddressRange(uintptr_t start, size_t length); + void mapSegments(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); + void mapSegments(const void* memoryImage, uint64_t imageLen, const LinkContext& context); + void UnmapSegments(); + void __attribute__((noreturn)) throwSymbolNotFound(const LinkContext& context, const char* symbol, + const char* referencedFrom, const char* fromVersMismatch, + const char* expectedIn); + void doImageInit(const LinkContext& context); + void doModInitFunctions(const LinkContext& context); + void setupLazyPointerHandler(const LinkContext& context); + void lookupProgramVars(const LinkContext& context) const; + + void makeTextSegmentWritable(const LinkContext& context, bool writeable); + 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, bool runResolver) const; + + static uintptr_t bindLazySymbol(const mach_header*, uintptr_t* lazyPointer); +protected: + uint64_t fCoveredCodeLength; + const uint8_t* fMachOData; + const uint8_t* fLinkEditBase; // add any internal "offset" to this to get mapped address + uintptr_t fSlide; + uint32_t fEHFrameSectionOffset; + uint32_t fUnwindInfoSectionOffset; + uint32_t fDylibIDOffset; + uint32_t fSegmentsCount : 8, + fIsSplitSeg : 1, + fInSharedCache : 1, +#if TEXT_RELOC_SUPPORT + fTextSegmentRebases : 1, + fTextSegmentBinds : 1, +#endif +#if __i386__ + fReadOnlyImportSegment : 1, +#endif + fHasSubLibraries : 1, + fHasSubUmbrella : 1, + fInUmbrella : 1, + fHasDOFSections : 1, + fHasDashInit : 1, + fHasInitializers : 1, + fHasTerminators : 1, + fNotifyObjC : 1, + fRetainForObjC : 1, + fRegisteredAsRequiresCoalescing : 1; // Loading MH_DYLIB_STUB causing coalescable miscount + + + static uint32_t fgSymbolTableBinarySearchs; +}; + + +#endif // __IMAGELOADERMACHO__ + + + + diff --git a/dyld/src/ImageLoaderMachOClassic.cpp b/dyld/src/ImageLoaderMachOClassic.cpp new file mode 100644 index 0000000..86723e7 --- /dev/null +++ b/dyld/src/ImageLoaderMachOClassic.cpp @@ -0,0 +1,2129 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-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@ + */ + +// work around until conformance work is complete rdar://problem/4508801 +#define __srr0 srr0 +#define __eip eip +#define __rip rip + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __x86_64__ + #include +#endif +#if __arm__ + #include +#endif + +#include "ImageLoaderMachOClassic.h" +#include "mach-o/dyld_images.h" + +// in dyldStartup.s +extern "C" void stub_binding_helper_i386_old(); + + +#if __x86_64__ + #define POINTER_RELOC X86_64_RELOC_UNSIGNED +#else + #define POINTER_RELOC GENERIC_RELOC_VANILLA +#endif + + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + + + + +// create image for main executable +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); + + // set slide for PIE programs + image->setSlide(slide); + + // for PIE record end of program, to know where to start loading dylibs + if ( slide != 0 ) + fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); + + image->disableCoverageCheck(); + 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 ) { + for(unsigned int i=0; i < image->fSegmentsCount; ++i) { + if ( image->segIsReadOnlyImport(i) ) + image->segMakeWritable(i, context); + } + } +#endif + + if ( context.verboseMapping ) { + dyld::log("dyld: Main executable mapped %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + const char* name = image->segName(i); + if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); + else + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + return image; +} + +// create image by mapping in a mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, + 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); + + // if this image is code signed, let kernel validate signature before mapping any pages from image + image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); + + // Validate that first data we read with pread actually matches with code signature + image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); + + // 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] == '/') ) + image->setPathUnowned(installName); + else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { + // rdar://problem/10733082 Fix up @path based paths during introspection + // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them + char realPath[MAXPATHLEN]; + if ( fcntl(fd, F_GETPATH, realPath) == 0 ) + image->setPaths(path, realPath); + else + image->setPath(path); + } + 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); + + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by using cached mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromCache(const macho_header* mh, const char* path, long slide, const struct stat& info, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, path, segCount, libCount); + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // remember this is from shared cache and cannot be unloaded + image->fInSharedCache = true; + image->setNeverUnload(); + image->disableCoverageCheck(); + + // segments already mapped in cache + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by copying an in-memory mach-o file +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOClassic* image = ImageLoaderMachOClassic::instantiateStart(mh, moduleName, segCount, libCount); + try { + // map segments + if ( mh->filetype == MH_EXECUTE ) + throw "can't load another MH_EXECUTE"; + + // vmcopy segments + image->ImageLoaderMachO::mapSegments((const void*)mh, len, context); + + // for compatibility, never unload dylibs loaded from memory + image->setNeverUnload(); + + image->disableCoverageCheck(); + + // bundle loads need path copied + if ( moduleName != NULL ) + image->setPath(moduleName); + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + + +ImageLoaderMachOClassic::ImageLoaderMachOClassic(const macho_header* mh, const char* path, + unsigned int segCount, uint32_t segOffsets[], unsigned int libCount) + : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fStrings(NULL), fSymbolTable(NULL), fDynamicInfo(NULL) +{ +} + +// construct ImageLoaderMachOClassic using "placement new" with SegmentMachO objects array at end +ImageLoaderMachOClassic* ImageLoaderMachOClassic::instantiateStart(const macho_header* mh, const char* path, + unsigned int segCount, unsigned int libCount) +{ + size_t size = sizeof(ImageLoaderMachOClassic) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); + ImageLoaderMachOClassic* allocatedSpace = static_cast(malloc(size)); + if ( allocatedSpace == NULL ) + throw "malloc failed"; + uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOClassic))); + bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array + return new (allocatedSpace) ImageLoaderMachOClassic(mh, path, segCount, segOffsets, libCount); +} + + + +// common code to finish initializing object +void ImageLoaderMachOClassic::instantiateFinish(const LinkContext& context) +{ + // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide + this->parseLoadCmds(context); +} + +ImageLoaderMachOClassic::~ImageLoaderMachOClassic() +{ + // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work + destroy(); +} + +uint32_t* ImageLoaderMachOClassic::segmentCommandOffsets() const +{ + return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic))); +} + + +ImageLoader* ImageLoaderMachOClassic::libImage(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + // mask off low bits + return (ImageLoader*)(images[libIndex] & (-4)); +} + +bool ImageLoaderMachOClassic::libReExported(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOClassic) + fSegmentsCount*sizeof(uint32_t))); + // re-export flag is low bit + return ((images[libIndex] & 1) != 0); +} + +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, 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; +} + + +void ImageLoaderMachOClassic::setSymbolTableInfo(const macho_nlist* symbols, const char* strings, const dysymtab_command* dynSym) +{ + fSymbolTable = symbols; + fStrings = strings; + fDynamicInfo = dynSym; +} + +void ImageLoaderMachOClassic::prefetchLINKEDIT(const LinkContext& context) +{ + // always prefetch a subrange of __LINKEDIT pages + uintptr_t symbolTableStart = (uintptr_t)fSymbolTable; + uintptr_t stringTableStart = (uintptr_t)fStrings; + uintptr_t start; + // if image did not load at preferred address + if ( segPreferredLoadAddress(0) != (uintptr_t)fMachOData ) { + // local relocations will be processed, so start pre-fetch at local symbols + start = (uintptr_t)fMachOData + fDynamicInfo->locreloff; + } + else { + // otherwise start pre-fetch at global symbols section of symbol table + start = symbolTableStart + fDynamicInfo->iextdefsym * sizeof(macho_nlist); + } + // prefetch ends at end of last undefined string in string pool + uintptr_t end = stringTableStart; + if ( fDynamicInfo->nundefsym != 0 ) + end += fSymbolTable[fDynamicInfo->iundefsym+fDynamicInfo->nundefsym-1].n_un.n_strx; + else if ( fDynamicInfo->nextdefsym != 0 ) + end += fSymbolTable[fDynamicInfo->iextdefsym+fDynamicInfo->nextdefsym-1].n_un.n_strx; + + // round to whole pages + start = dyld_page_trunc(start); + end = dyld_page_round(end); + + // skip if there is only one page + if ( (end-start) > dyld_page_size ) { + madvise((void*)start, end-start, MADV_WILLNEED); + fgTotalBytesPreFetched += (end-start); + if ( context.verboseMapping ) { + dyld::log("%18s prefetching 0x%0lX -> 0x%0lX\n", "__LINKEDIT", start, end-1); + } + } +} + + +#if SPLIT_SEG_DYLIB_SUPPORT +unsigned int +ImageLoaderMachOClassic::getExtraZeroFillEntriesCount() +{ + // calculate mapping entries + unsigned int extraZeroFillEntries = 0; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segHasTrailingZeroFill(i) ) + ++extraZeroFillEntries; + } + + return extraZeroFillEntries; +} + +void +ImageLoaderMachOClassic::initMappingTable(uint64_t offsetInFat, + shared_file_mapping_np *mappingTable) +{ + for(unsigned int i=0,entryIndex=0; i < fSegmentsCount; ++i, ++entryIndex) { + shared_file_mapping_np* entry = &mappingTable[entryIndex]; + entry->sfm_address = segActualLoadAddress(i); + entry->sfm_size = segFileSize(i); + entry->sfm_file_offset = segFileOffset(i) + offsetInFat; + entry->sfm_init_prot = VM_PROT_NONE; + if ( !segUnaccessible(i) ) { + if ( segExecutable(i) ) + entry->sfm_init_prot |= VM_PROT_EXECUTE; + if ( segReadable(i) ) + entry->sfm_init_prot |= VM_PROT_READ; + if ( segWriteable(i) ) + entry->sfm_init_prot |= VM_PROT_WRITE | VM_PROT_COW; + } + entry->sfm_max_prot = entry->sfm_init_prot; + if ( segHasTrailingZeroFill(i) ) { + shared_file_mapping_np* zfentry = &mappingTable[++entryIndex]; + zfentry->sfm_address = entry->sfm_address + segFileSize(i); + zfentry->sfm_size = segSize(i) - segFileSize(i); + zfentry->sfm_file_offset = 0; + zfentry->sfm_init_prot = entry->sfm_init_prot | VM_PROT_COW | VM_PROT_ZF; + zfentry->sfm_max_prot = zfentry->sfm_init_prot; + } + } +} + +int +ImageLoaderMachOClassic::mapSplitSegDylibOutsideSharedRegion(int fd, + uint64_t offsetInFat, + uint64_t lenInFat, + uint64_t fileLen, + const LinkContext& context) +{ + uintptr_t nextAltLoadAddress = 0; + const unsigned int segmentCount = fSegmentsCount; + const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); + const unsigned int regionCount = segmentCount+extraZeroFillEntries; + shared_file_mapping_np regions[regionCount]; + initMappingTable(offsetInFat, regions); + int r = -1; + // find space somewhere to allocate split seg + bool foundRoom = false; + while ( ! foundRoom ) { + foundRoom = true; + for(unsigned int i=0; i < regionCount; ++i) { + vm_address_t addr = (vm_address_t)(nextAltLoadAddress + regions[i].sfm_address - regions[0].sfm_address); + vm_size_t size = (vm_size_t)regions[i].sfm_size ; + r = vm_allocate(mach_task_self(), &addr, size, false /*only this range*/); + if ( 0 != r ) { + // no room here, deallocate what has succeeded so far + for(unsigned int j=0; j < i; ++j) { + addr = (vm_address_t)(nextAltLoadAddress + regions[j].sfm_address - regions[0].sfm_address); + size = (vm_size_t)(regions[j].sfm_size); + (void)vm_deallocate(mach_task_self(), addr, size); + } + nextAltLoadAddress += 0x00100000; // skip ahead 1MB and try again + // skip over shared region + if ( (SHARED_REGION_BASE <= nextAltLoadAddress) && (nextAltLoadAddress < (SHARED_REGION_BASE + SHARED_REGION_SIZE)) ) + nextAltLoadAddress = (SHARED_REGION_BASE + SHARED_REGION_SIZE); + if ( nextAltLoadAddress > 0xFF000000 ) + throw "can't map split seg anywhere"; + foundRoom = false; + break; + } + } + } + + // map in each region + uintptr_t slide = (uintptr_t)(nextAltLoadAddress - regions[0].sfm_address); + this->setSlide(slide); + for(unsigned int i=0; i < regionCount; ++i) { + if ( ((regions[i].sfm_init_prot & VM_PROT_ZF) != 0) || (regions[i].sfm_size == 0) ) { + // nothing to mmap for zero-fills areas, they are just vm_allocated + } + else { + void* mmapAddress = (void*)(uintptr_t)(regions[i].sfm_address + slide); + size_t size = (size_t)regions[i].sfm_size; + int protection = 0; + if ( regions[i].sfm_init_prot & VM_PROT_EXECUTE ) + protection |= PROT_EXEC; + if ( regions[i].sfm_init_prot & VM_PROT_READ ) + protection |= PROT_READ; + if ( regions[i].sfm_init_prot & VM_PROT_WRITE ) + protection |= PROT_WRITE; + off_t offset = regions[i].sfm_file_offset; + //dyld::log("mmap(%p, 0x%08lX, %s\n", mmapAddress, size, fPath); + mmapAddress = mmap(mmapAddress, size, protection, MAP_FIXED | MAP_PRIVATE, fd, offset); + if ( mmapAddress == ((void*)(-1)) ) + throw "mmap error"; + } + } + + // logging + if ( context.verboseMapping ) { + dyld::log("dyld: Mapping split-seg outside shared region, slid by 0x%08lX %s\n", this->fSlide, this->getPath()); + for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ + const shared_file_mapping_np* entry = ®ions[entryIndex]; + if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); + if ( entryIndex < (regionCount-1) ) { + const shared_file_mapping_np* nextEntry = ®ions[entryIndex+1]; + if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { + uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; + dyld::log("%18s at 0x%08lX->0x%08lX (zerofill)\n", + segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); + ++entryIndex; + } + } + } + } + + return r; +} +#endif // SPLIT_SEG_DYLIB_SUPPORT + + +void ImageLoaderMachOClassic::mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context) +{ + // non-split segment libraries handled by super class + if ( !fIsSplitSeg ) + return ImageLoaderMachO::mapSegments(fd, offsetInFat, lenInFat, fileLen, context); + +#if SPLIT_SEG_SHARED_REGION_SUPPORT + // 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 + +#if SPLIT_SEG_DYLIB_SUPPORT + // support old split-seg dylibs by mapping them where ever we find space + if ( mapSplitSegDylibOutsideSharedRegion(fd, offsetInFat, lenInFat, fileLen, context) != 0 ) +#endif + throw "mapping error"; +} + + +#if SPLIT_SEG_SHARED_REGION_SUPPORT +static int _shared_region_map_np(int fd, uint32_t count, const shared_file_mapping_np mappings[]) +{ + return syscall(295, fd, count, mappings); +} + +int +ImageLoaderMachOClassic::mapSplitSegDylibInfoSharedRegion(int fd, + uint64_t offsetInFat, + uint64_t lenInFat, + uint64_t fileLen, + const LinkContext& context) +{ + // build table of segments to map + const unsigned int segmentCount = fSegmentsCount; + const unsigned int extraZeroFillEntries = getExtraZeroFillEntriesCount(); + const unsigned int mappingTableCount = segmentCount+extraZeroFillEntries; + shared_file_mapping_np mappingTable[mappingTableCount]; + initMappingTable(offsetInFat, mappingTable); + + // try to map it in shared + int r = _shared_region_map_np(fd, mappingTableCount, mappingTable); + if ( 0 == r ) { + this->setNeverUnload(); + if ( context.verboseMapping ) { + dyld::log("dyld: Mapping split-seg shared %s\n", this->getPath()); + for(unsigned int segIndex=0,entryIndex=0; segIndex < segmentCount; ++segIndex, ++entryIndex){ + const shared_file_mapping_np* entry = &mappingTable[entryIndex]; + if ( (entry->sfm_init_prot & VM_PROT_ZF) == 0 ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), segActualLoadAddress(segIndex), segActualEndAddress(segIndex)-1); + if ( entryIndex < (mappingTableCount-1) ) { + const shared_file_mapping_np* nextEntry = &mappingTable[entryIndex+1]; + if ( (nextEntry->sfm_init_prot & VM_PROT_ZF) != 0 ) { + uint64_t segOffset = nextEntry->sfm_address - entry->sfm_address; + dyld::log("%18s at 0x%08lX->0x%08lX\n", + segName(segIndex), (uintptr_t)(segActualLoadAddress(segIndex) + segOffset), + (uintptr_t)(segActualLoadAddress(segIndex) + segOffset + nextEntry->sfm_size - 1)); + ++entryIndex; + } + } + } + } + } + return r; +} + +#endif // SPLIT_SEG_SHARED_REGION_SUPPORT + +// test if this image is re-exported through parent (the image that loaded this one) +bool ImageLoaderMachOClassic::isSubframeworkOf(const LinkContext& context, const ImageLoader* parent) const +{ + if ( fInUmbrella ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_SUB_FRAMEWORK) { + const struct sub_framework_command* subf = (struct sub_framework_command*)cmd; + const char* exportThruName = (char*)cmd + subf->umbrella.offset; + // need to match LC_SUB_FRAMEWORK string against the leaf name of the install location of parent... + const char* parentInstallPath = parent->getInstallPath(); + if ( parentInstallPath != NULL ) { + const char* lastSlash = strrchr(parentInstallPath, '/'); + if ( lastSlash != NULL ) { + if ( strcmp(&lastSlash[1], exportThruName) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end + char reexportAndSuffix[strlen(context.imageSuffix)+strlen(exportThruName)+1]; + strcpy(reexportAndSuffix, exportThruName); + strcat(reexportAndSuffix, context.imageSuffix); + if ( strcmp(&lastSlash[1], reexportAndSuffix) == 0 ) + return true; + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + return false; +} + +// test if child is re-exported +bool ImageLoaderMachOClassic::hasSubLibrary(const LinkContext& context, const ImageLoader* child) const +{ + if ( fHasSubLibraries ) { + // need to match LC_SUB_LIBRARY string against the leaf name (without extension) of the install location of child... + const char* childInstallPath = child->getInstallPath(); + if ( childInstallPath != NULL ) { + const char* lastSlash = strrchr(childInstallPath, '/'); + if ( lastSlash != NULL ) { + const char* firstDot = strchr(lastSlash, '.'); + size_t len; + if ( firstDot == NULL ) + len = strlen(lastSlash); + else + len = firstDot-lastSlash-1; + char childLeafName[len+1]; + strncpy(childLeafName, &lastSlash[1], len); + childLeafName[len] = '\0'; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SUB_LIBRARY: + { + const struct sub_library_command* lib = (struct sub_library_command*)cmd; + const char* aSubLibName = (char*)cmd + lib->sub_library.offset; + if ( strcmp(aSubLibName, childLeafName) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, childLeafName string needs imageSuffix removed from end + char aSubLibNameAndSuffix[strlen(context.imageSuffix)+strlen(aSubLibName)+1]; + strcpy(aSubLibNameAndSuffix, aSubLibName); + strcat(aSubLibNameAndSuffix, context.imageSuffix); + if ( strcmp(aSubLibNameAndSuffix, childLeafName) == 0 ) + return true; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + } + } + if ( fHasSubUmbrella ) { + // need to match LC_SUB_UMBRELLA string against the leaf name of install location of child... + const char* childInstallPath = child->getInstallPath(); + if ( childInstallPath != NULL ) { + const char* lastSlash = strrchr(childInstallPath, '/'); + if ( lastSlash != NULL ) { + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SUB_UMBRELLA: + { + const struct sub_umbrella_command* um = (struct sub_umbrella_command*)cmd; + const char* aSubUmbrellaName = (char*)cmd + um->sub_umbrella.offset; + if ( strcmp(aSubUmbrellaName, &lastSlash[1]) == 0 ) + return true; + if ( context.imageSuffix != NULL ) { + // when DYLD_IMAGE_SUFFIX is used, lastSlash string needs imageSuffix removed from end + char umbrellaAndSuffix[strlen(context.imageSuffix)+strlen(aSubUmbrellaName)+1]; + strcpy(umbrellaAndSuffix, aSubUmbrellaName); + strcat(umbrellaAndSuffix, context.imageSuffix); + if ( strcmp(umbrellaAndSuffix, &lastSlash[1]) == 0 ) + return true; + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } + } + } + return false; +} + + +uintptr_t ImageLoaderMachOClassic::getFirstWritableSegmentAddress() +{ + // in split segment libraries r_address is offset from first writable segment + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( segWriteable(i) ) + return segActualLoadAddress(i); + } + throw "no writable segment"; +} + +uintptr_t ImageLoaderMachOClassic::getRelocBase() +{ + // r_address is either an offset from the first segment address + // or from the first writable segment address +#if __x86_64__ + return getFirstWritableSegmentAddress(); +#else + if ( fIsSplitSeg ) + return getFirstWritableSegmentAddress(); + else + return segActualLoadAddress(0); +#endif +} + + +#if PREBOUND_IMAGE_SUPPORT +void ImageLoaderMachOClassic::resetPreboundLazyPointers(const LinkContext& context) +{ + // loop through all local (internal) relocation records looking for pre-bound-lazy-pointer values + const uintptr_t relocBase = this->getRelocBase(); + const uintptr_t slide = this->fSlide; + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( (reloc->r_address & R_SCATTERED) != 0 ) { + const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; + if (sreloc->r_length == RELOC_SIZE) { + uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); + switch(sreloc->r_type) { + #if __i386__ + case GENERIC_RELOC_PB_LA_PTR: + *locationToFix = sreloc->r_value + slide; + break; + #endif + #if __arm__ + case ARM_RELOC_PB_LA_PTR: + *locationToFix = sreloc->r_value + slide; + break; + #endif + } + } + } + } +} +#endif + + + + +void ImageLoaderMachOClassic::rebase(const LinkContext& context, uintptr_t slide) +{ + CRSetCrashLogMessage2(this->getPath()); + const uintptr_t relocBase = this->getRelocBase(); + + // prefetch any LINKEDIT pages needed + if ( !context.preFetchDisabled && !this->isPrebindable()) + this->prefetchLINKEDIT(context); + + // loop through all local (internal) relocation records + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->locreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + uintptr_t rebaseAddr; + try { + #if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(reloc); + #endif + #if __x86_64__ + // only one kind of local relocation supported for x86_64 + if ( reloc->r_length != 3 ) + throw "bad local relocation length"; + if ( reloc->r_type != X86_64_RELOC_UNSIGNED ) + throw "unknown local relocation type"; + if ( reloc->r_pcrel != 0 ) + throw "bad local relocation pc_rel"; + if ( reloc->r_extern != 0 ) + throw "extern relocation found with local relocations"; + rebaseAddr = reloc->r_address + relocBase; + if ( ! this->containsAddress((void*)rebaseAddr) ) + dyld::throwf("local reloc %p not in mapped image\n", (void*)rebaseAddr); + *((uintptr_t*)rebaseAddr) += slide; + if ( context.verboseRebase ) + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), rebaseAddr, slide); + #else + if ( (reloc->r_address & R_SCATTERED) == 0 ) { + if ( reloc->r_symbolnum == R_ABS ) { + // ignore absolute relocations + } + else if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case GENERIC_RELOC_VANILLA: + rebaseAddr = reloc->r_address + relocBase; + if ( ! this->containsAddress((void*)rebaseAddr) ) + dyld::throwf("local reloc %p not in mapped image\n", (void*)rebaseAddr); + *((uintptr_t*)rebaseAddr) += slide; + if ( context.verboseRebase ) + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), rebaseAddr, slide); + break; + default: + throw "unknown local relocation type"; + } + } + else { + throw "bad local relocation length"; + } + } + else { + const struct scattered_relocation_info* sreloc = (struct scattered_relocation_info*)reloc; + if (sreloc->r_length == RELOC_SIZE) { + uintptr_t* locationToFix = (uintptr_t*)(sreloc->r_address + relocBase); + switch(sreloc->r_type) { + case GENERIC_RELOC_VANILLA: + if ( ! this->containsAddress((void*)locationToFix) ) + dyld::throwf("local scattered reloc %p not in mapped image\n", locationToFix); + *locationToFix += slide; + if ( context.verboseRebase ) + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)locationToFix, slide); + break; + #if __i386__ + case GENERIC_RELOC_PB_LA_PTR: + // do nothing + break; + #elif __arm__ + case ARM_RELOC_PB_LA_PTR: + // do nothing + break; + #endif + default: + throw "unknown local scattered relocation type"; + } + } + else { + throw "bad local scattered relocation length"; + } + } + #endif // x86_64 + } + catch (const char* msg) { + const uint8_t* r = (uint8_t*)reloc; + dyld::throwf("%s in %s. reloc record at %p: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", + msg, this->getPath(), reloc, r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7]); + } + } + + // update stats + fgTotalRebaseFixups += fDynamicInfo->nlocrel; + CRSetCrashLogMessage2(NULL); +} + + + +const struct macho_nlist* ImageLoaderMachOClassic::binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], + const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const +{ + int32_t high = symbolCount-1; + int32_t mid = hintIndex; + + // handle out of range hint + if ( mid >= (int32_t)symbolCount ) + mid = symbolCount/2; + ++ImageLoaderMachO::fgSymbolTableBinarySearchs; + ++fgTotalBindImageSearches; + + //dyld::log("dyld: binarySearchWithToc for %s in %s\n", key, this->getShortName()); + + for (int32_t low = 0; low <= high; mid = (low+high)/2) { + const uint32_t index = toc[mid].symbol_index; + const struct macho_nlist* pivot = &symbols[index]; + const char* pivotStr = &stringPool[pivot->n_un.n_strx]; +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(&toc[mid]); + noteAccessedLinkEditAddress(pivot); + noteAccessedLinkEditAddress(pivotStr); +#endif + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + low = mid + 1; + } + else { + // key < pivot + high = mid - 1; + } + } + return NULL; +} + +const struct macho_nlist* ImageLoaderMachOClassic::binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const +{ + // update stats + ++fgTotalBindImageSearches; + ++ImageLoaderMachO::fgSymbolTableBinarySearchs; + + //dyld::log("dyld: binarySearch for %s in %s, stringpool=%p, symbols=%p, symbolCount=%u\n", + // key, this->getShortName(), stringPool, symbols, symbolCount); + + const struct macho_nlist* base = symbols; + for (uint32_t n = symbolCount; n > 0; n /= 2) { + const struct macho_nlist* pivot = &base[n/2]; + const char* pivotStr = &stringPool[pivot->n_un.n_strx]; +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(pivot); + noteAccessedLinkEditAddress(pivotStr); +#endif + int cmp = strcmp(key, pivotStr); + if ( cmp == 0 ) + return pivot; + if ( cmp > 0 ) { + // key > pivot + // move base to symbol after pivot + base = &pivot[1]; + --n; + } + else { + // key < pivot + // keep same base + } + } + return NULL; +} + + +const ImageLoader::Symbol* ImageLoaderMachOClassic::findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const +{ + const struct macho_nlist* sym = NULL; + if ( fDynamicInfo->tocoff == 0 ) + sym = binarySearch(name, fStrings, &fSymbolTable[fDynamicInfo->iextdefsym], fDynamicInfo->nextdefsym); + else + sym = binarySearchWithToc(name, fStrings, fSymbolTable, (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff], + fDynamicInfo->ntoc, fDynamicInfo->nextdefsym); + if ( sym != NULL ) { + if ( foundIn != NULL ) + *foundIn = (ImageLoader*)this; + return (const Symbol*)sym; + } + return NULL; +} + + + +bool ImageLoaderMachOClassic::containsSymbol(const void* addr) const +{ + return ( (fSymbolTable <= addr) && (addr < fStrings) ); +} + + +uintptr_t ImageLoaderMachOClassic::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const +{ + const struct macho_nlist* sym = (macho_nlist*)symbol; + uintptr_t result = sym->n_value + fSlide; + #if __arm__ + // processor assumes code address with low bit set is thumb + if (sym->n_desc & N_ARM_THUMB_DEF) + result |= 1; + #endif + return result; +} + +bool ImageLoaderMachOClassic::exportedSymbolIsWeakDefintion(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return ( (nlistSym->n_desc & N_WEAK_DEF) != 0 ); +} + +const char* ImageLoaderMachOClassic::exportedSymbolName(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return &fStrings[nlistSym->n_un.n_strx]; +} + +unsigned int ImageLoaderMachOClassic::exportedSymbolCount() const +{ + return fDynamicInfo->nextdefsym; +} + +const ImageLoader::Symbol* ImageLoaderMachOClassic::exportedSymbolIndexed(unsigned int index) const +{ + if ( index < fDynamicInfo->nextdefsym ) { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym + index]; + return (const ImageLoader::Symbol*)sym; + } + return NULL; +} + +unsigned int ImageLoaderMachOClassic::importedSymbolCount() const +{ + return fDynamicInfo->nundefsym; +} + +const ImageLoader::Symbol* ImageLoaderMachOClassic::importedSymbolIndexed(unsigned int index) const +{ + if ( index < fDynamicInfo->nundefsym ) { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iundefsym + index]; + return (const ImageLoader::Symbol*)sym; + } + return NULL; +} + +const char* ImageLoaderMachOClassic::importedSymbolName(const Symbol* symbol) const +{ + const struct macho_nlist* nlistSym = (const struct macho_nlist*)symbol; + return &fStrings[nlistSym->n_un.n_strx]; +} + + + +bool ImageLoaderMachOClassic::symbolIsWeakDefinition(const struct macho_nlist* symbol) +{ + // if a define and weak ==> coalesced + if ( ((symbol->n_type & N_TYPE) == N_SECT) && ((symbol->n_desc & N_WEAK_DEF) != 0) ) + return true; + + // regular symbol + return false; +} + +bool ImageLoaderMachOClassic::symbolIsWeakReference(const struct macho_nlist* symbol) +{ + // if an undefine and not referencing a weak symbol ==> coalesced + if ( ((symbol->n_type & N_TYPE) != N_SECT) && ((symbol->n_desc & N_REF_TO_WEAK) != 0) ) + return true; + + // regular symbol + return false; +} + +uintptr_t ImageLoaderMachOClassic::getSymbolAddress(const macho_nlist* sym, const LinkContext& context, bool runResolver) const +{ + return ImageLoaderMachO::getSymbolAddress((Symbol*)sym, this, context, runResolver); +} + +uintptr_t ImageLoaderMachOClassic::resolveUndefined(const LinkContext& context, const struct macho_nlist* undefinedSymbol, + bool twoLevel, bool dontCoalesce, bool runResolver, const ImageLoader** foundIn) +{ + ++fgTotalBindSymbolsResolved; + const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; + +#if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(undefinedSymbol); + noteAccessedLinkEditAddress(symbolName); +#endif + if ( context.bindFlat || !twoLevel ) { + // flat lookup + if ( ((undefinedSymbol->n_type & N_PEXT) != 0) && ((undefinedSymbol->n_type & N_TYPE) == N_SECT) ) { + // is a multi-module private_extern internal reference that the linker did not optimize away + uintptr_t addr = this->getSymbolAddress(undefinedSymbol, context, false); + *foundIn = this; + return addr; + } + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + // if a bundle is loaded privately the above will not find its exports + if ( this->isBundle() && this->hasHiddenExports() ) { + // look in self for needed symbol + sym = this->findShallowExportedSymbol(symbolName, foundIn); + if ( sym != NULL ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { + // definition can't be found anywhere + // if reference is weak_import, then it is ok, just return 0 + return 0; + } + throwSymbolNotFound(context, symbolName, this->getPath(), "", "flat namespace"); + } + else { + // symbol requires searching images with coalesced symbols (not done during prebinding) + if ( !context.prebinding && !dontCoalesce && (symbolIsWeakReference(undefinedSymbol) || symbolIsWeakDefinition(undefinedSymbol)) ) { + const Symbol* sym; + if ( context.coalescedExportFinder(symbolName, &sym, foundIn) ) { + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + } + //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, false); + *foundIn = this; + return addr; + } + + // two level lookup + ImageLoader* target = NULL; + uint8_t ord = GET_LIBRARY_ORDINAL(undefinedSymbol->n_desc); + if ( ord == EXECUTABLE_ORDINAL ) { + target = context.mainExecutable; + } + else if ( ord == SELF_LIBRARY_ORDINAL ) { + target = this; + } + else if ( ord == DYNAMIC_LOOKUP_ORDINAL ) { + // rnielsen: HACKHACK + // flat lookup + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + // no image has exports this symbol + // report error + context.undefinedHandler(symbolName); + // try looking again + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) + return (*foundIn)->getExportedSymbolAddress(sym, context, this); + + throwSymbolNotFound(context, symbolName, this->getPath(), "", "dynamic lookup"); + } + else if ( ord <= libraryCount() ) { + target = libImage(ord-1); + if ( target == NULL ) { + // if target library not loaded and reference is weak or library is weak return 0 + return 0; + } + } + else { + dyld::throwf("bad mach-o binary, library ordinal (%u) too big (max %u) for symbol %s in %s", + ord, libraryCount(), symbolName, this->getPath()); + } + + if ( target == NULL ) { + //dyld::log("resolveUndefined(%s) in %s\n", symbolName, this->getPath()); + throw "symbol not found"; + } + + uintptr_t address; + if ( target->findExportedSymbolAddress(context, symbolName, this, ord, runResolver, foundIn, &address) ) + return address; + + 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, false); + } + else if ( (undefinedSymbol->n_desc & N_WEAK_REF) != 0 ) { + // if definition not found and reference is weak return 0 + return 0; + } + + // nowhere to be found + throwSymbolNotFound(context, symbolName, this->getPath(), "", target->getPath()); + } +} + + + +// returns if 'addr' is within the address range of section 'sectionIndex' +// fSlide is not used. 'addr' is assumed to be a prebound address in this image +bool ImageLoaderMachOClassic::isAddrInSection(uintptr_t addr, uint8_t sectionIndex) +{ + uint8_t currentSectionIndex = 1; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( (currentSectionIndex <= sectionIndex) && (sectionIndex < currentSectionIndex+seg->nsects) ) { + // 'sectionIndex' is in this segment, get section info + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const section = §ionsStart[sectionIndex-currentSectionIndex]; + return ( (section->addr <= addr) && (addr < section->addr+section->size) ); + } + else { + // 'sectionIndex' not in this segment, skip to next segment + currentSectionIndex += seg->nsects; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} + +void ImageLoaderMachOClassic::doBindExternalRelocations(const LinkContext& context) +{ + const uintptr_t relocBase = this->getRelocBase(); + const bool twoLevel = this->usesTwoLevelNameSpace(); + const bool prebound = this->isPrebindable(); + +#if TEXT_RELOC_SUPPORT + // if there are __TEXT fixups, temporarily make __TEXT writable + if ( fTextSegmentBinds ) + this->makeTextSegmentWritable(context, true); +#endif + // cache last lookup + const struct macho_nlist* lastUndefinedSymbol = NULL; + uintptr_t symbolAddr = 0; + const ImageLoader* image = NULL; + + // loop through all external relocation records and bind each + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case POINTER_RELOC: + { + const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + if ( ! this->containsAddress((void*)location) ) + dyld::throwf("external reloc %p not in mapped image %s\n", (void*)location, this->getPath()); + uintptr_t value = *location; + bool symbolAddrCached = true; + #if __i386__ + if ( reloc->r_pcrel ) { + value += (uintptr_t)location + 4 - fSlide; + } + #endif + if ( prebound ) { + // we are doing relocations, so prebinding was not usable + // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound + // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address + // if mach-o relocation structs had an "addend" field this complication would not be necessary. + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // weak symbols need special casing, since *location may have been prebound to a definition in another image. + // If *location is currently prebound to somewhere in the same section as the weak definition, we assume + // that we can subtract off the weak symbol address to get the addend. + // If prebound elsewhere, we've lost the addend and have to assume it is zero. + // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs + if ( (value == undefinedSymbol->n_value) || this->isAddrInSection(value, undefinedSymbol->n_sect) ) { + value -= undefinedSymbol->n_value; + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + value -= 1; + #endif + } + else + value = 0; + } + #if __arm__ + else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { + // it was prebound to a defined symbol for thumb code in the same linkage unit + // we need to subtract off one to get real addend + value -= (undefinedSymbol->n_value+1); + } + #endif + else { + // is undefined or non-weak symbol, so do subtraction to get addend + value -= undefinedSymbol->n_value; + } + } + // if undefinedSymbol is same as last time, then symbolAddr and image will resolve to the same too + if ( undefinedSymbol != lastUndefinedSymbol ) { + bool dontCoalesce = true; + if ( symbolIsWeakReference(undefinedSymbol) ) { + // when weakbind() is run on a classic mach-o encoding, it won't try + // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted + // range of global symbols. To handle that case we do the coalesing now. + dontCoalesce = false; + } + symbolAddr = this->resolveUndefined(context, undefinedSymbol, twoLevel, dontCoalesce, false, &image); + lastUndefinedSymbol = undefinedSymbol; + symbolAddrCached = false; + } + if ( context.verboseBind ) { + const char *path = NULL; + if ( image != NULL ) { + path = image->getShortName(); + } + const char* cachedString = "(cached)"; + if ( !symbolAddrCached ) + cachedString = ""; + if ( value == 0 ) { + dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s\n", + this->getShortName(), (uintptr_t)location, + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString); + } + else { + dyld::log("dyld: bind: %s:0x%08lX = %s:%s, *0x%08lX = 0x%08lX%s + %ld\n", + this->getShortName(), (uintptr_t)location, + path, &fStrings[undefinedSymbol->n_un.n_strx], (uintptr_t)location, symbolAddr, cachedString, value); + } + } + value += symbolAddr; + #if __i386__ + if ( reloc->r_pcrel ) { + *location = value - ((uintptr_t)location + 4); + } + else { + // don't dirty page if prebound value was correct + if ( !prebound || (*location != value) ) + *location = value; + } + #else + // don't dirty page if prebound value was correct + if ( !prebound || (*location != value) ) + *location = value; + #endif + // update stats + ++fgTotalBindFixups; + } + break; + default: + throw "unknown external relocation type"; + } + } + else { + throw "bad external relocation length"; + } + } + +#if TEXT_RELOC_SUPPORT + // if there were __TEXT fixups, restore write protection + if ( fTextSegmentBinds ) { + this->makeTextSegmentWritable(context, true); + } +#endif +} + + + +uintptr_t ImageLoaderMachOClassic::bindIndirectSymbol(uintptr_t* ptrToBind, const struct macho_section* sect, const char* symbolName, uintptr_t targetAddr, const ImageLoader* targetImage, const LinkContext& context) +{ + if ( context.verboseBind ) { + const char* path = NULL; + if ( targetImage != NULL ) + path = targetImage->getShortName(); + dyld::log("dyld: bind indirect sym: %s:%s$%s = %s:%s, *0x%08lx = 0x%08lx\n", + this->getShortName(), symbolName, (((sect->flags & SECTION_TYPE)==S_NON_LAZY_SYMBOL_POINTERS) ? "non_lazy_ptr" : "lazy_ptr"), + ((path != NULL) ? path : ""), symbolName, (uintptr_t)ptrToBind, targetAddr); + } + if ( context.bindingHandler != NULL ) { + const char* path = NULL; + if ( targetImage != NULL ) + path = targetImage->getShortName(); + targetAddr = (uintptr_t)context.bindingHandler(path, symbolName, (void *)targetAddr); + } +#if __i386__ + // i386 has special self-modifying stubs that change from "CALL rel32" to "JMP rel32" + if ( ((sect->flags & SECTION_TYPE) == S_SYMBOL_STUBS) && ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) != 0) && (sect->reserved2 == 5) ) { + uint32_t rel32 = targetAddr - (((uint32_t)ptrToBind)+5); + // re-write instruction in a thread-safe manner + // use 8-byte compare-and-swap to alter 5-byte jump table entries + // loop is required in case the extra three bytes that cover the next entry are altered by another thread + bool done = false; + while ( !done ) { + volatile int64_t* jumpPtr = (int64_t*)ptrToBind; + int pad = 0; + // By default the three extra bytes swapped follow the 5-byte JMP. + // But, if the 5-byte jump is up against the end of the __IMPORT segment + // We don't want to access bytes off the end of the segment, so we shift + // the extra bytes to precede the 5-byte JMP. + if ( (((uint32_t)ptrToBind + 8) & 0x00000FFC) == 0x00000000 ) { + jumpPtr = (int64_t*)((uint32_t)ptrToBind - 3); + pad = 3; + } + int64_t oldEntry = *jumpPtr; + union { + int64_t int64; + uint8_t bytes[8]; + } newEntry; + newEntry.int64 = oldEntry; + newEntry.bytes[pad+0] = 0xE9; // JMP rel32 + newEntry.bytes[pad+1] = rel32 & 0xFF; + newEntry.bytes[pad+2] = (rel32 >> 8) & 0xFF; + newEntry.bytes[pad+3] = (rel32 >> 16) & 0xFF; + newEntry.bytes[pad+4] = (rel32 >> 24) & 0xFF; + done = OSAtomicCompareAndSwap64Barrier(oldEntry, newEntry.int64, (int64_t*)jumpPtr); + } + } + else +#endif + *ptrToBind = targetAddr; + return targetAddr; +} + +uintptr_t ImageLoaderMachOClassic::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, void (*lock)(), void (*unlock)()) +{ + throw "compressed LINKEDIT lazy binder called with classic LINKEDIT"; +} + +uintptr_t ImageLoaderMachOClassic::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +{ + // scan for all lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( type == S_LAZY_SYMBOL_POINTERS ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const size_t lazyIndex = lazyPointer - symbolPointers; + symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; + } + } + #if __i386__ + else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // 5 bytes stubs on i386 are new "fast stubs" + uint8_t* const jmpTableBase = (uint8_t*)(sect->addr + fSlide); + uint8_t* const jmpTableEnd = jmpTableBase + sect->size; + // initial CALL instruction in jump table leaves pointer to next entry, so back up + uint8_t* const jmpTableEntryToPatch = ((uint8_t*)lazyPointer) - 5; + lazyPointer = (uintptr_t*)jmpTableEntryToPatch; + if ( (jmpTableEntryToPatch >= jmpTableBase) && (jmpTableEntryToPatch < jmpTableEnd) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t entryIndex = (jmpTableEntryToPatch - jmpTableBase)/5; + symbolIndex = indirectTable[indirectTableOffset + entryIndex]; + } + } + #endif + if ( symbolIndex != INDIRECT_SYMBOL_ABS && symbolIndex != INDIRECT_SYMBOL_LOCAL ) { + const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; + const ImageLoader* image = NULL; + uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], twoLevel, false, true, &image); + symbolAddr = this->bindIndirectSymbol(lazyPointer, sect, symbolName, symbolAddr, image, context); + ++fgTotalLazyBindFixups; + return symbolAddr; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); +} + + + +void ImageLoaderMachOClassic::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.type = 0; + if ( fDynamicInfo->tocoff != 0 ) { + it.curIndex = 0; + it.endIndex = fDynamicInfo->ntoc; + } + else { + it.curIndex = 0; + it.endIndex = fDynamicInfo->nextdefsym; + } +} + + +bool ImageLoaderMachOClassic::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( fDynamicInfo->tocoff != 0 ) { + if ( it.curIndex >= fDynamicInfo->ntoc ) { + it.done = true; + it.symbolName = "~~~"; + return true; + } + else { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + const uint32_t index = toc[it.curIndex].symbol_index; + const struct macho_nlist* sym = &fSymbolTable[index]; + const char* symStr = &fStrings[sym->n_un.n_strx]; + it.symbolName = symStr; + it.weakSymbol = (sym->n_desc & N_WEAK_DEF); + it.symbolMatches = false; + it.type = 0; // clear flag that says we applied updates for this symbol + //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); + it.curIndex++; + return false; + } + } + else { + if ( it.curIndex >= fDynamicInfo->nextdefsym ) { + it.done = true; + it.symbolName = "~~~"; + return true; + } + else { + const struct macho_nlist* sym = &fSymbolTable[fDynamicInfo->iextdefsym+it.curIndex]; + const char* symStr = &fStrings[sym->n_un.n_strx]; + it.symbolName = symStr; + it.weakSymbol = (sym->n_desc & N_WEAK_DEF); + it.symbolMatches = false; + it.type = 0; // clear flag that says we applied updates for this symbol + //dyld::log("incrementCoalIterator() curIndex=%ld, symbolName=%s in %s\n", it.curIndex, symStr, this->getPath()); + it.curIndex++; + return false; + } + } + + return false; +} + +uintptr_t ImageLoaderMachOClassic::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + uint32_t symbol_index = 0; + if ( fDynamicInfo->tocoff != 0 ) { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + symbol_index = toc[it.curIndex-1].symbol_index; + } + else { + symbol_index = fDynamicInfo->iextdefsym + (uint32_t)it.curIndex - 1; + } + const struct macho_nlist* sym = &fSymbolTable[symbol_index]; + //dyld::log("getAddressCoalIterator() => 0x%llX, %s symbol_index=%d, in %s\n", (uint64_t)(sym->n_value + fSlide), &fStrings[sym->n_un.n_strx], symbol_index, this->getPath()); +#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 +} + + +void ImageLoaderMachOClassic::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context) +{ + // flat_namespace images with classic LINKEDIT do not need late coalescing. + // They still need to be iterated becuase they may implement + // something needed by other coalescing images. + // But they need no updating because during the bind phase every symbol lookup is a full scan. + if ( !this->usesTwoLevelNameSpace() ) + return; + + // weak binding done too early with inserted libraries + if ( this->getState() < dyld_image_state_bound ) + return; + + uint32_t symbol_index = 0; + if ( fDynamicInfo->tocoff != 0 ) { + const dylib_table_of_contents* toc = (dylib_table_of_contents*)&fLinkEditBase[fDynamicInfo->tocoff]; + symbol_index = toc[it.curIndex-1].symbol_index; + } + else { + symbol_index = fDynamicInfo->iextdefsym + (uint32_t)it.curIndex - 1; + } + + // if this image's copy of the symbol is not a weak definition nor a weak reference then nothing to coalesce here + if ( !symbolIsWeakReference(&fSymbolTable[symbol_index]) && !symbolIsWeakDefinition(&fSymbolTable[symbol_index]) ) { + return; + } + + // malformed dylib with duplicate weak symbols causes re-binding + if ( it.type ) + return; + + bool boundSomething = false; + // scan external relocations for uses of symbol_index + const uintptr_t relocBase = this->getRelocBase(); + const bool prebound = this->isPrebindable(); + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( reloc->r_symbolnum == symbol_index ) { + //dyld::log("found external reloc using symbol_index=%d in %s\n",symbol_index, this->getPath()); + const struct macho_nlist* undefinedSymbol = &fSymbolTable[reloc->r_symbolnum]; + const char* symbolName = &fStrings[undefinedSymbol->n_un.n_strx]; + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + const uintptr_t initialValue = *location; + uintptr_t addend = 0; + if ( prebound ) { + // we are doing relocations, so prebinding was not usable + // in a prebound executable, the n_value field of an undefined symbol is set to the address where the symbol was found when prebound + // so, subtracting that gives the initial displacement which we need to add to the newly found symbol address + // if mach-o relocation structs had an "addend" field this complication would not be necessary. + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // weak symbols need special casing, since *location may have been prebound to a definition in another image. + // If *location is currently prebound to somewhere in the same section as the weak definition, we assume + // that we can subtract off the weak symbol address to get the addend. + // If prebound elsewhere, we've lost the addend and have to assume it is zero. + // The prebinding to elsewhere only happens with 10.4+ update_prebinding which only operates on a small set of Apple dylibs + if ( (initialValue == undefinedSymbol->n_value) || this->isAddrInSection(initialValue, undefinedSymbol->n_sect) ) { + addend = initialValue - undefinedSymbol->n_value; + #if __arm__ + // if weak and thumb subtract off extra thumb bit + if ( (undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0 ) + addend &= -2; + #endif + } + } + #if __arm__ + else if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_ARM_THUMB_DEF) != 0) ) { + // it was prebound to a defined symbol for thumb code in the same linkage unit + // we need to subtract off one to get real addend + addend = initialValue - (undefinedSymbol->n_value+1); + } + #endif + else { + // is undefined or non-weak symbol, so do subtraction to get addend + addend = initialValue - undefinedSymbol->n_value; + } + } + else { + // non-prebound case + if ( ((undefinedSymbol->n_type & N_TYPE) == N_SECT) && ((undefinedSymbol->n_desc & N_WEAK_DEF) != 0) ) { + // if target is weak-def in same linkage unit, then bind phase has already set initialValue + // to be definition address plus addend + //dyld::log("weak def, initialValue=0x%lX, undefAddr=0x%lX\n", initialValue, undefinedSymbol->n_value+fSlide); + addend = initialValue - (undefinedSymbol->n_value + fSlide); + #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 + //dyld::log("addend=0x%lX\n", initialValue); + addend = initialValue; + } + } + + uint8_t type = BIND_TYPE_POINTER; + #if __i386__ + if ( reloc->r_pcrel ) + type = BIND_TYPE_TEXT_PCREL32; + #endif + this->bindLocation(context, (uintptr_t)location, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + } + } + + // scan lazy and non-lazy pointers for uses of symbol_index + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_SEGMENT_COMMAND ) { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + uint32_t elementSize = sizeof(uintptr_t); + switch ( sect->flags & SECTION_TYPE ) { + #if __i386__ + case S_SYMBOL_STUBS: + if ( ((sect->flags & S_ATTR_SELF_MODIFYING_CODE) ==0) || (sect->reserved2 != 5) ) + continue; + elementSize = 5; + #endif + case S_NON_LAZY_SYMBOL_POINTERS: + case S_LAZY_SYMBOL_POINTERS: + { + size_t elementCount = sect->size / elementSize; + const uint32_t indirectTableOffset = sect->reserved1; + uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); + //dyld::log(" scanning section %s of %s starting at %p\n", sect->sectname, this->getShortName(), ptrToBind); + for (size_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + if ( indirectTable[indirectTableOffset + j] == symbol_index ) { + //dyld::log(" found symbol index match at %d/%d, ptrToBind=%p\n", j, elementCount, ptrToBind); + // update pointer + this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, it.symbolName, value, targetImage, context); + boundSomething = true; + } + } + } + break; + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( boundSomething && (targetImage != this) ) { + context.addDynamicReference(this, targetImage); + } + + // mark that this symbol has already been bound, so we don't try to bind again + it.type = 1; +} + + +void ImageLoaderMachOClassic::bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys) +{ + // scan for all non-lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + bool isLazySymbol = false; + const uint8_t type = sect->flags & SECTION_TYPE; + uint32_t elementSize = sizeof(uintptr_t); + size_t elementCount = sect->size / elementSize; + if ( type == S_NON_LAZY_SYMBOL_POINTERS ) { + if ( ! bindNonLazys ) + continue; + } + else if ( type == S_LAZY_SYMBOL_POINTERS ) { + // process each symbol pointer in this section + fgTotalPossibleLazyBindFixups += elementCount; + isLazySymbol = true; + if ( ! bindLazys ) + continue; + } + #if __i386__ + else if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // process each jmp entry in this section + elementCount = sect->size / 5; + elementSize = 5; + fgTotalPossibleLazyBindFixups += elementCount; + isLazySymbol = true; + if ( ! bindLazys ) + continue; + } + #endif + else { + continue; + } + const uint32_t indirectTableOffset = sect->reserved1; + uint8_t* ptrToBind = (uint8_t*)(sect->addr + fSlide); + for (size_t j=0; j < elementCount; ++j, ptrToBind += elementSize) { + #if LINKEDIT_USAGE_DEBUG + noteAccessedLinkEditAddress(&indirectTable[indirectTableOffset + j]); + #endif + uint32_t symbolIndex = indirectTable[indirectTableOffset + j]; + if ( symbolIndex == INDIRECT_SYMBOL_LOCAL) { + *((uintptr_t*)ptrToBind) += this->fSlide; + } + else if ( symbolIndex == INDIRECT_SYMBOL_ABS) { + // do nothing since already has absolute address + } + else { + const struct macho_nlist* sym = &fSymbolTable[symbolIndex]; + if ( symbolIndex == 0 ) { + // This could be rdar://problem/3534709 + if ( ((const macho_header*)fMachOData)->filetype == MH_EXECUTE ) { + static bool alreadyWarned = false; + if ( (sym->n_type & N_TYPE) != N_UNDF ) { + // The indirect table parallels the (non)lazy pointer sections. For + // instance, to find info about the fifth lazy pointer you look at the + // fifth entry in the indirect table. (try otool -Iv on a file). + // The entry in the indirect table contains an index into the symbol table. + + // The bug in ld caused the entry in the indirect table to be zero + // (instead of a magic value that means a local symbol). So, if the + // symbolIndex == 0, we may be encountering the bug, or 0 may be a valid + // symbol table index. The check I put in place is to see if the zero'th + // symbol table entry is an import entry (usually it is a local symbol + // definition). + if ( context.verboseWarnings && !alreadyWarned ) { + dyld::log("dyld: malformed executable '%s', skipping indirect symbol to %s\n", + this->getPath(), &fStrings[sym->n_un.n_strx]); + alreadyWarned = true; + } + continue; + } + } + } + const ImageLoader* image = NULL; + // let weak definitions resolve to themselves, later coalescing may overwrite them + bool dontCoalesce = true; + if ( bindLazys && isLazySymbol ) { + // if this is something normally lazy bound, but we are forcing + // it to be bound now, do coalescing + dontCoalesce = false; + } + if ( symbolIsWeakReference(sym) ) { + // when weakbind() is run on a classic mach-o encoding, it won't try + // to coalesce N_REF_TO_WEAK symbols because they are not in the sorted + // range of global symbols. To handle that case we do the coalesing now. + dontCoalesce = false; + } + uintptr_t symbolAddr = resolveUndefined(context, sym, twoLevel, dontCoalesce, false, &image); + // update pointer + symbolAddr = this->bindIndirectSymbol((uintptr_t*)ptrToBind, sect, &fStrings[sym->n_un.n_strx], symbolAddr, image, context); + // update stats + ++fgTotalBindFixups; + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + + + +#if __i386__ +void ImageLoaderMachOClassic::initializeLazyStubs(const LinkContext& context) +{ + if ( ! this->usablePrebinding(context) ) { + // reset all "fast" stubs + const macho_header* mh = (macho_header*)fMachOData; + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( (type == S_SYMBOL_STUBS) && (sect->flags & S_ATTR_SELF_MODIFYING_CODE) && (sect->reserved2 == 5) ) { + // reset each jmp entry in this section + const uint32_t indirectTableOffset = sect->reserved1; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[fDynamicInfo->indirectsymoff]; + uint8_t* start = (uint8_t*)(sect->addr + this->fSlide); + uint8_t* end = start + sect->size; + uintptr_t dyldHandler = (uintptr_t)&stub_binding_helper_i386_old; + uint32_t entryIndex = 0; + for (uint8_t* entry = start; entry < end; entry += 5, ++entryIndex) { + bool installLazyHandler = true; + // jump table entries that cross a (64-byte) cache line boundary have the potential to cause crashes + // if the instruction is updated by one thread while being executed by another + if ( ((uint32_t)entry & 0xFFFFFFC0) != ((uint32_t)entry+4 & 0xFFFFFFC0) ) { + // need to bind this now to avoid a potential problem if bound lazily + uint32_t symbolIndex = indirectTable[indirectTableOffset + entryIndex]; + // the latest linker marks 64-byte crossing stubs with INDIRECT_SYMBOL_ABS so they are not used + if ( symbolIndex != INDIRECT_SYMBOL_ABS ) { + const char* symbolName = &fStrings[fSymbolTable[symbolIndex].n_un.n_strx]; + const ImageLoader* image = NULL; + try { + uintptr_t symbolAddr = this->resolveUndefined(context, &fSymbolTable[symbolIndex], this->usesTwoLevelNameSpace(), false, false, &image); + symbolAddr = this->bindIndirectSymbol((uintptr_t*)entry, sect, symbolName, symbolAddr, image, context); + ++fgTotalBindFixups; + uint32_t rel32 = symbolAddr - (((uint32_t)entry)+5); + entry[0] = 0xE9; // JMP rel32 + entry[1] = rel32 & 0xFF; + entry[2] = (rel32 >> 8) & 0xFF; + entry[3] = (rel32 >> 16) & 0xFF; + entry[4] = (rel32 >> 24) & 0xFF; + installLazyHandler = false; + } + catch (const char* msg) { + // ignore errors when binding symbols early + // maybe the function is never called, and therefore erroring out now would be a regression + } + } + } + if ( installLazyHandler ) { + uint32_t rel32 = dyldHandler - (((uint32_t)entry)+5); + entry[0] = 0xE8; // CALL rel32 + entry[1] = rel32 & 0xFF; + entry[2] = (rel32 >> 8) & 0xFF; + entry[3] = (rel32 >> 16) & 0xFF; + entry[4] = (rel32 >> 24) & 0xFF; + } + } + } + } + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + } +} +#endif // __i386__ + + +void ImageLoaderMachOClassic::doBind(const LinkContext& context, bool forceLazysBound) +{ + CRSetCrashLogMessage2(this->getPath()); +#if __i386__ + this->initializeLazyStubs(context); +#endif + + // if prebound and loaded at prebound address, and all libraries are same as when this was prebound, then no need to bind + // note: flat-namespace binaries need to have imports rebound (even if correctly prebound) + if ( this->usablePrebinding(context) ) { + // binding already up to date + } + else { + // no valid prebinding, so bind symbols. + // values bound by name are stored two different ways in classic mach-o: + + #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); + + // 2) "indirect symbols" are used for code references to external symbols + // if this image is in the shared cache, there is no way to reset the lazy pointers, so bind them now + this->bindIndirectSymbolPointers(context, true, forceLazysBound || fInSharedCache); + + #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) +{ + // some API called requested that all lazy pointers in this image be force bound + 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 size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + for (size_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { + uintptr_t newValue = interposedAddress(context, symbolPointers[pointerIndex], this); + if ( newValue != symbolPointers[pointerIndex] ) + symbolPointers[pointerIndex] = newValue; + } + } + #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; + uint32_t newTarget = interposedAddress(context, target, this); + if ( newTarget != target ) { + uint32_t newRel32 = newTarget - (uint32_t)&entry[5]; + *((uint32_t*)&entry[1]) = newRel32; // assume unaligned store of uint32_t is ok + } + } + } + } + #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)); + uintptr_t value = *location; + uintptr_t newValue = interposedAddress(context, value, this); + if ( newValue != value ) + *location = newValue; + } + break; + } + } + } +} + +void ImageLoaderMachOClassic::dynamicInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); + + // scan indirect symbols + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + if ( (type == S_NON_LAZY_SYMBOL_POINTERS) || (type == S_LAZY_SYMBOL_POINTERS) ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + for (size_t pointerIndex=0; pointerIndex < pointerCount; ++pointerIndex) { + for(size_t j=0; j < context.dynamicInterposeCount; ++j) { + // replace all references to 'replacee' with 'replacement' + if ( symbolPointers[pointerIndex] == (uintptr_t)context.dynamicInterposeArray[j].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + &symbolPointers[pointerIndex], context.dynamicInterposeArray[j].replacee, context.dynamicInterposeArray[j].replacement, this->getPath()); + } + symbolPointers[pointerIndex] = (uintptr_t)context.dynamicInterposeArray[j].replacement; + } + } + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // scan external relocations + const uintptr_t relocBase = this->getRelocBase(); + const relocation_info* const relocsStart = (struct relocation_info*)(&fLinkEditBase[fDynamicInfo->extreloff]); + const relocation_info* const relocsEnd = &relocsStart[fDynamicInfo->nextrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if (reloc->r_length == RELOC_SIZE) { + switch(reloc->r_type) { + case POINTER_RELOC: + { + uintptr_t* location = ((uintptr_t*)(reloc->r_address + relocBase)); + for(size_t i=0; i < context.dynamicInterposeCount; ++i) { + // replace all references to 'replacee' with 'replacement' + if ( *location == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + location, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); + } + *location = (uintptr_t)context.dynamicInterposeArray[i].replacement; + } + } + } + break; + } + } + } +} + + +const char* ImageLoaderMachOClassic::findClosestSymbol(const void* addr, const void** closestAddr) const +{ + uintptr_t targetAddress = (uintptr_t)addr - fSlide; + const struct macho_nlist* bestSymbol = NULL; + // first walk all global symbols + const struct macho_nlist* const globalsStart = &fSymbolTable[fDynamicInfo->iextdefsym]; + const struct macho_nlist* const globalsEnd= &globalsStart[fDynamicInfo->nextdefsym]; + for (const struct macho_nlist* s = globalsStart; s < globalsEnd; ++s) { + if ( (s->n_type & N_TYPE) == N_SECT ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + // next walk all local symbols + const struct macho_nlist* const localsStart = &fSymbolTable[fDynamicInfo->ilocalsym]; + const struct macho_nlist* const localsEnd= &localsStart[fDynamicInfo->nlocalsym]; + for (const struct macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + if ( bestSymbol == NULL ) { + if ( s->n_value <= targetAddress ) + bestSymbol = s; + } + else if ( (s->n_value <= targetAddress) && (bestSymbol->n_value < s->n_value) ) { + bestSymbol = s; + } + } + } + if ( bestSymbol != NULL ) { +#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; +} + + diff --git a/dyld/src/ImageLoaderMachOClassic.h b/dyld/src/ImageLoaderMachOClassic.h new file mode 100644 index 0000000..1f63ae0 --- /dev/null +++ b/dyld/src/ImageLoaderMachOClassic.h @@ -0,0 +1,138 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __IMAGELOADER_MACHO_CLASSIC__ +#define __IMAGELOADER_MACHO_CLASSIC__ + +#include + +#include "ImageLoaderMachO.h" + + +// +// ImageLoaderMachOClassic is the concrete subclass of ImageLoader which loads mach-o files +// that use the traditional LINKEDIT format. +// +class ImageLoaderMachOClassic : public ImageLoaderMachO { +public: + static ImageLoaderMachOClassic* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + static ImageLoaderMachOClassic* instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, 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 ~ImageLoaderMachOClassic(); + + virtual ImageLoader* libImage(unsigned int) const; + virtual bool libReExported(unsigned int) const; + 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, void (*lock)(), void (*unlock)()); + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context); + +protected: + virtual void doInterpose(const LinkContext& context); + virtual void dynamicInterpose(const LinkContext& context); + virtual void setDyldInfo(const dyld_info_command*) {} + virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*); + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const; + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const; + virtual uint32_t* segmentCommandOffsets() const; + virtual void rebase(const LinkContext& context, uintptr_t slide); + virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const; + virtual bool containsSymbol(const void* addr) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; + virtual const char* exportedSymbolName(const Symbol* symbol) const; + virtual unsigned int exportedSymbolCount() const; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const; + virtual unsigned int importedSymbolCount() const; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const; + virtual const char* importedSymbolName(const Symbol* symbol) const; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context); +#endif + + +private: + ImageLoaderMachOClassic(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + static ImageLoaderMachOClassic* instantiateStart(const macho_header* mh, const char* path, unsigned int segCount, unsigned int libCount); + void instantiateFinish(const LinkContext& context); + uintptr_t getRelocBase(); + void mapSegmentsClassic(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); + uintptr_t getFirstWritableSegmentAddress(); + const struct macho_nlist* binarySearch(const char* key, const char stringPool[], const struct macho_nlist symbols[], uint32_t symbolCount) const; + const struct macho_nlist* binarySearchWithToc(const char* key, const char stringPool[], const struct macho_nlist symbols[], + const struct dylib_table_of_contents toc[], uint32_t symbolCount, uint32_t hintIndex) const; + static bool symbolIsWeakReference(const struct macho_nlist* symbol); + static bool symbolIsWeakDefinition(const struct macho_nlist* symbol); + uintptr_t resolveUndefined(const LinkContext& context, const struct macho_nlist* symbol, bool twoLevel, + bool dontCoalesce, bool runResolver, const ImageLoader **foundIn); + 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, + const char* symbolName, uintptr_t targetAddr, + const ImageLoader* targetImage, const LinkContext& context); + void bindIndirectSymbolPointers(const LinkContext& context, bool bindNonLazys, bool bindLazys); + void initializeLazyStubs(const LinkContext& context); + void prefetchLINKEDIT(const LinkContext& context); +#if SPLIT_SEG_DYLIB_SUPPORT + unsigned int getExtraZeroFillEntriesCount(); + void initMappingTable(uint64_t offsetInFat, shared_file_mapping_np *mappingTable); +#endif + int mapSplitSegDylibOutsideSharedRegion(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); +#if SPLIT_SEG_SHARED_REGION_SUPPORT + int mapSplitSegDylibInfoSharedRegion(int fd, uint64_t offsetInFat, uint64_t lenInFat, uint64_t fileLen, const LinkContext& context); +#endif + + + const char* fStrings; + const struct macho_nlist* fSymbolTable; + const struct dysymtab_command* fDynamicInfo; + +}; + + + + +#endif // __IMAGELOADER_MACHO_CLASSIC__ + + + + diff --git a/dyld/src/ImageLoaderMachOCompressed.cpp b/dyld/src/ImageLoaderMachOCompressed.cpp new file mode 100644 index 0000000..020ec51 --- /dev/null +++ b/dyld/src/ImageLoaderMachOCompressed.cpp @@ -0,0 +1,1673 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#if __arm__ || __arm64__ + #include +#else + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ImageLoaderMachOCompressed.h" +#include "mach-o/dyld_images.h" + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + +#if __arm__ || __arm64__ +bool ImageLoaderMachOCompressed::sVmAccountingDisabled = false; +bool ImageLoaderMachOCompressed::sVmAccountingSuspended = false; +#endif + + + +// create image for main executable +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); + + // set slide for PIE programs + image->setSlide(slide); + + // for PIE record end of program, to know where to start loading dylibs + if ( slide != 0 ) + fgNextPIEDylibAddress = (uintptr_t)image->getEnd(); + + image->disableCoverageCheck(); + image->instantiateFinish(context); + image->setMapped(context); + + if ( context.verboseMapping ) { + dyld::log("dyld: Main executable mapped %s\n", path); + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + const char* name = image->segName(i); + if ( (strcmp(name, "__PAGEZERO") == 0) || (strcmp(name, "__UNIXSTACK") == 0) ) + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segPreferredLoadAddress(i), image->segPreferredLoadAddress(i)+image->segSize(i)); + else + dyld::log("%18s at 0x%08lX->0x%08lX\n", name, image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + return image; +} + +// create image by mapping in a mach-o file +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromFile(const char* path, int fd, const uint8_t* fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, + const struct encryption_info_command* encryptCmd, + const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart((macho_header*)fileData, path, segCount, libCount); + + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // if this image is code signed, let kernel validate signature before mapping any pages from image + image->loadCodeSignature(codeSigCmd, fd, offsetInFat, context); + + // Validate that first data we read with pread actually matches with code signature + image->validateFirstPages(codeSigCmd, fd, fileData, lenFileData, offsetInFat, context); + + // mmap segments + image->mapSegments(fd, offsetInFat, lenInFat, info.st_size, context); + + // if framework is FairPlay encrypted, register with kernel + image->registerEncryption(encryptCmd, context); + + // probe to see if code signed correctly + image->crashIfInvalidCodeSignature(); + + // finish construction + image->instantiateFinish(context); + + // if path happens to be same as in LC_DYLIB_ID load command use that, otherwise malloc a copy of the path + const char* installName = image->getInstallPath(); + if ( (installName != NULL) && (strcmp(installName, path) == 0) && (path[0] == '/') ) + image->setPathUnowned(installName); +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // app crashes when libSystem cannot be found + else if ( (installName != NULL) && (strcmp(path, "/usr/lib/libgcc_s.1.dylib") == 0) && (strcmp(installName, "/usr/lib/libSystem.B.dylib") == 0) ) + image->setPathUnowned("/usr/lib/libSystem.B.dylib"); +#endif + else if ( (path[0] != '/') || (strstr(path, "../") != NULL) ) { + // rdar://problem/10733082 Fix up @rpath based paths during introspection + // rdar://problem/5135363 turn relative paths into absolute paths so gdb, Symbolication can later find them + char realPath[MAXPATHLEN]; + if ( fcntl(fd, F_GETPATH, realPath) == 0 ) + image->setPaths(path, realPath); + else + image->setPath(path); + } + else + image->setPath(path); + + // make sure path is stable before recording in dyld_all_image_infos + image->setMapped(context); + + // dylibs with thread local variables cannot be unloaded because there is no way to clean up all threads + if ( image->machHeader()->flags & MH_HAS_TLV_DESCRIPTORS ) + image->setNeverUnload(); + + // pre-fetch content of __DATA and __LINKEDIT segment for faster launches + // don't do this on prebound images or if prefetching is disabled + if ( !context.preFetchDisabled && !image->isPrebindable()) { + image->preFetchDATA(fd, offsetInFat, context); + image->markSequentialLINKEDIT(context); + } + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by using cached mach-o file +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromCache(const macho_header* mh, const char* path, long slide, + const struct stat& info, unsigned int segCount, + unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, path, segCount, libCount); + try { + // record info about file + image->setFileInfo(info.st_dev, info.st_ino, info.st_mtime); + + // remember this is from shared cache and cannot be unloaded + image->fInSharedCache = true; + image->setNeverUnload(); + image->setSlide(slide); + image->disableCoverageCheck(); + + // segments already mapped in cache + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + for(unsigned int i=0; i < image->fSegmentsCount; ++i) { + dyld::log("%18s at 0x%08lX->0x%08lX\n", image->segName(i), image->segActualLoadAddress(i), image->segActualEndAddress(i)); + } + } + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + +// create image by copying an in-memory mach-o file +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateFromMemory(const char* moduleName, const macho_header* mh, uint64_t len, + unsigned int segCount, unsigned int libCount, const LinkContext& context) +{ + ImageLoaderMachOCompressed* image = ImageLoaderMachOCompressed::instantiateStart(mh, moduleName, segCount, libCount); + try { + // map segments + if ( mh->filetype == MH_EXECUTE ) + throw "can't load another MH_EXECUTE"; + + // vmcopy segments + image->mapSegments((const void*)mh, len, context); + + // for compatibility, never unload dylibs loaded from memory + image->setNeverUnload(); + + image->disableCoverageCheck(); + + // bundle loads need path copied + if ( moduleName != NULL ) + image->setPath(moduleName); + + image->instantiateFinish(context); + image->setMapped(context); + } + catch (...) { + // ImageLoader::setMapped() can throw an exception to block loading of image + // Leaked fSegmentsArray and image segments during failed dlopen_preflight + delete image; + throw; + } + + return image; +} + + +ImageLoaderMachOCompressed::ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount) + : ImageLoaderMachO(mh, path, segCount, segOffsets, libCount), fDyldInfo(NULL) +{ +} + +ImageLoaderMachOCompressed::~ImageLoaderMachOCompressed() +{ + // don't do clean up in ~ImageLoaderMachO() because virtual call to segmentCommandOffsets() won't work + destroy(); +} + + + +// construct ImageLoaderMachOCompressed using "placement new" with SegmentMachO objects array at end +ImageLoaderMachOCompressed* ImageLoaderMachOCompressed::instantiateStart(const macho_header* mh, const char* path, + unsigned int segCount, unsigned int libCount) +{ + size_t size = sizeof(ImageLoaderMachOCompressed) + segCount * sizeof(uint32_t) + libCount * sizeof(ImageLoader*); + ImageLoaderMachOCompressed* allocatedSpace = static_cast(malloc(size)); + if ( allocatedSpace == NULL ) + throw "malloc failed"; + uint32_t* segOffsets = ((uint32_t*)(((uint8_t*)allocatedSpace) + sizeof(ImageLoaderMachOCompressed))); + bzero(&segOffsets[segCount], libCount*sizeof(void*)); // zero out lib array + return new (allocatedSpace) ImageLoaderMachOCompressed(mh, path, segCount, segOffsets, libCount); +} + + +// common code to finish initializing object +void ImageLoaderMachOCompressed::instantiateFinish(const LinkContext& context) +{ + // now that segments are mapped in, get real fMachOData, fLinkEditBase, and fSlide + this->parseLoadCmds(context); +} + +uint32_t* ImageLoaderMachOCompressed::segmentCommandOffsets() const +{ + return ((uint32_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed))); +} + + +ImageLoader* ImageLoaderMachOCompressed::libImage(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + // mask off low bits + return (ImageLoader*)(images[libIndex] & (-4)); +} + +bool ImageLoaderMachOCompressed::libReExported(unsigned int libIndex) const +{ + const uintptr_t* images = ((uintptr_t*)(((uint8_t*)this) + sizeof(ImageLoaderMachOCompressed) + fSegmentsCount*sizeof(uint32_t))); + // re-export flag is low bit + return ((images[libIndex] & 1) != 0); +} + +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, 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; +} + + +void ImageLoaderMachOCompressed::markFreeLINKEDIT(const LinkContext& context) +{ + // mark that we are done with rebase and bind info + markLINKEDIT(context, MADV_FREE); +} + +void ImageLoaderMachOCompressed::markSequentialLINKEDIT(const LinkContext& context) +{ + // mark the rebase and bind info and using sequential access + markLINKEDIT(context, MADV_SEQUENTIAL); +} + +void ImageLoaderMachOCompressed::markLINKEDIT(const LinkContext& context, int advise) +{ + // if not loaded at preferred address, mark rebase info + uintptr_t start = 0; + if ( (fSlide != 0) && (fDyldInfo->rebase_size != 0) ) + start = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off; + else if ( fDyldInfo->bind_off != 0 ) + start = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off; + else + return; // no binding info to prefetch + + // end is at end of bind info + uintptr_t end = 0; + if ( fDyldInfo->bind_off != 0 ) + end = (uintptr_t)fLinkEditBase + fDyldInfo->bind_off + fDyldInfo->bind_size; + else if ( fDyldInfo->rebase_off != 0 ) + end = (uintptr_t)fLinkEditBase + fDyldInfo->rebase_off + fDyldInfo->rebase_size; + else + return; + + + // round to whole pages + start = dyld_page_trunc(start); + end = dyld_page_round(end); + + // do nothing if only one page of rebase/bind info + if ( (end-start) <= dyld_page_size ) + return; + + // tell kernel about our access to these pages + madvise((void*)start, end-start, advise); + if ( context.verboseMapping ) { + const char* adstr = "sequential"; + if ( advise == MADV_FREE ) + adstr = "free"; + dyld::log("%18s %s 0x%0lX -> 0x%0lX for %s\n", "__LINKEDIT", adstr, start, end-1, this->getPath()); + } +} + + + +void ImageLoaderMachOCompressed::rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type) +{ + if ( context.verboseRebase ) { + dyld::log("dyld: rebase: %s:*0x%08lX += 0x%08lX\n", this->getShortName(), (uintptr_t)addr, slide); + } + //dyld::log("0x%08lX type=%d\n", addr, type); + uintptr_t* locationToFix = (uintptr_t*)addr; + switch (type) { + case REBASE_TYPE_POINTER: + *locationToFix += slide; + break; + case REBASE_TYPE_TEXT_ABSOLUTE32: + *locationToFix += slide; + break; + default: + dyld::throwf("bad rebase type %d", type); + } +} + +void ImageLoaderMachOCompressed::throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) +{ + dyld::throwf("malformed rebase opcodes (%ld/%ld): address 0x%08lX is outside of segment %s (0x%08lX -> 0x%08lX)", + (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), + segActualLoadAddress(segmentIndex), segmentEndAddress); +} + +void ImageLoaderMachOCompressed::rebase(const LinkContext& context, uintptr_t slide) +{ + CRSetCrashLogMessage2(this->getPath()); + const uint8_t* const start = fLinkEditBase + fDyldInfo->rebase_off; + const uint8_t* const end = &start[fDyldInfo->rebase_size]; + const uint8_t* p = start; + + try { + uint8_t type = 0; + int segmentIndex = 0; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentStartAddress = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + uintptr_t count; + uintptr_t skip; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & REBASE_IMMEDIATE_MASK; + uint8_t opcode = *p & REBASE_OPCODE_MASK; + ++p; + switch (opcode) { + case REBASE_OPCODE_DONE: + done = true; + break; + case REBASE_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( segmentIndex >= fSegmentsCount ) + dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + segmentIndex, fSegmentsCount-1); + #if TEXT_RELOC_SUPPORT + if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) ) + #else + if ( !segWriteable(segmentIndex) ) + #endif + dyld::throwf("REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not a writable segment (%s)", + segmentIndex, segName(segmentIndex)); + segmentStartAddress = segActualLoadAddress(segmentIndex); + segmentEndAddress = segActualEndAddress(segmentIndex); + address = segmentStartAddress + read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case REBASE_OPCODE_ADD_ADDR_IMM_SCALED: + address += immediate*sizeof(uintptr_t); + break; + case REBASE_OPCODE_DO_REBASE_IMM_TIMES: + for (int i=0; i < immediate; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += sizeof(uintptr_t); + } + fgTotalRebaseFixups += immediate; + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: + count = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += sizeof(uintptr_t); + } + fgTotalRebaseFixups += count; + break; + case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB: + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += read_uleb128(p, end) + sizeof(uintptr_t); + ++fgTotalRebaseFixups; + break; + case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadRebaseAddress(address, segmentEndAddress, segmentIndex, start, end, p); + rebaseAt(context, address, slide, type); + address += skip + sizeof(uintptr_t); + } + fgTotalRebaseFixups += count; + break; + default: + dyld::throwf("bad rebase opcode %d", *p); + } + } + } + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } + CRSetCrashLogMessage2(NULL); +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::findShallowExportedSymbol(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 uintptr_t flags = read_uleb128(p, end); + // found match, return pointer to terminal part of node + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + // re-export from another dylib, lookup there + const uintptr_t ordinal = read_uleb128(p, end); + const char* importedName = (char*)p; + if ( importedName[0] == '\0' ) + importedName = symbol; + if ( (ordinal > 0) && (ordinal <= libraryCount()) ) { + const ImageLoader* reexportedFrom = libImage((unsigned int)ordinal-1); + //dyld::log("Compressed::findExportedSymbol(), %s -> %s/%s\n", symbol, reexportedFrom->getShortName(), importedName); + const char* reExportLibPath = libPath((unsigned int)ordinal-1); + return reexportedFrom->findExportedSymbol(importedName, true, reExportLibPath, 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 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; + } + } + return NULL; +} + + +bool ImageLoaderMachOCompressed::containsSymbol(const void* addr) const +{ + const uint8_t* start = &fLinkEditBase[fDyldInfo->export_off]; + const uint8_t* end = &start[fDyldInfo->export_size]; + return ( (start <= addr) && (addr < end) ); +} + + +uintptr_t ImageLoaderMachOCompressed::exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const +{ + const uint8_t* exportNode = (uint8_t*)symbol; + const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; + 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()); + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); + switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + // this node has a stub and resolver, run the resolver to get target address + uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; // skip over stub + // interposing dylibs have the stub address as their replacee + uintptr_t interposedStub = interposedAddress(context, stub, requestor); + if ( interposedStub != stub ) + return interposedStub; + // stub was not interposed, so run resolver + typedef uintptr_t (*ResolverProc)(void); + ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData); + uintptr_t result = (*resolver)(); + 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; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); + return read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)fMachOData; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); + return read_uleb128(exportNode, exportTrieEnd); + default: + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, symbol); + } +} + +bool ImageLoaderMachOCompressed::exportedSymbolIsWeakDefintion(const Symbol* symbol) const +{ + const uint8_t* exportNode = (uint8_t*)symbol; + const uint8_t* exportTrieStart = fLinkEditBase + fDyldInfo->export_off; + const uint8_t* exportTrieEnd = exportTrieStart + fDyldInfo->export_size; + if ( (exportNode < exportTrieStart) || (exportNode > exportTrieEnd) ) + throw "symbol is not in trie"; + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); + return ( flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION ); +} + + +const char* ImageLoaderMachOCompressed::exportedSymbolName(const Symbol* symbol) const +{ + throw "NSNameOfSymbol() not supported with compressed LINKEDIT"; +} + +unsigned int ImageLoaderMachOCompressed::exportedSymbolCount() const +{ + throw "NSSymbolDefinitionCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::exportedSymbolIndexed(unsigned int index) const +{ + throw "NSSymbolDefinitionNameInObjectFileImage() not supported with compressed LINKEDIT"; +} + +unsigned int ImageLoaderMachOCompressed::importedSymbolCount() const +{ + throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const ImageLoader::Symbol* ImageLoaderMachOCompressed::importedSymbolIndexed(unsigned int index) const +{ + throw "NSSymbolReferenceCountInObjectFileImage() not supported with compressed LINKEDIT"; +} + +const char* ImageLoaderMachOCompressed::importedSymbolName(const Symbol* symbol) const +{ + throw "NSSymbolReferenceNameInObjectFileImage() not supported with compressed LINKEDIT"; +} + + + +uintptr_t ImageLoaderMachOCompressed::resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, + bool runResolver, const ImageLoader** foundIn) +{ + const Symbol* sym; + if ( context.flatExportFinder(symbolName, &sym, foundIn) ) { + if ( *foundIn != this ) + context.addDynamicReference(this, const_cast(*foundIn)); + return (*foundIn)->getExportedSymbolAddress(sym, context, this, runResolver); + } + // if a bundle is loaded privately the above will not find its exports + if ( this->isBundle() && this->hasHiddenExports() ) { + // look in self for needed symbol + sym = this->findShallowExportedSymbol(symbolName, foundIn); + if ( sym != NULL ) + 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(context, symbolName, this->getPath(), "", "flat namespace"); +} + + +uintptr_t ImageLoaderMachOCompressed::resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage, + const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver, + const ImageLoader** foundIn) +{ + // two level lookup + uintptr_t address; + if ( definedInImage->findExportedSymbolAddress(context, symbolName, requestorImage, requestorOrdinalOfDef, runResolver, foundIn, &address) ) + return address; + + if ( weak_import ) { + // definition can't be found anywhere, ok because it is weak, just return 0 + return 0; + } + + // nowhere to be found, check if maybe this image is too new for this OS + char versMismatch[256]; + versMismatch[0] = '\0'; + uint32_t imageMinOS = this->minOSVersion(); + // dyld is always built for the current OS, so we can get the current OS version + // from the load command in dyld itself. + extern const mach_header __dso_handle; + uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion(&__dso_handle); + if ( imageMinOS > dyldMinOS ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + const char* msg = dyld::mkstringf(" (which was built for Mac OS X %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); +#else + const char* msg = dyld::mkstringf(" (which was built for iOS %d.%d)", imageMinOS >> 16, (imageMinOS >> 8) & 0xFF); +#endif + strcpy(versMismatch, msg); + ::free((void*)msg); + } + throwSymbolNotFound(context, symbolName, this->getPath(), versMismatch, definedInImage->getPath()); +} + + +uintptr_t ImageLoaderMachOCompressed::resolve(const LinkContext& context, const char* symbolName, + uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, + LastLookup* last, bool runResolver) +{ + *targetImage = NULL; + + // only clients that benefit from caching last lookup pass in a LastLookup struct + if ( last != NULL ) { + if ( (last->ordinal == libraryOrdinal) + && (last->flags == symboFlags) + && (last->name == symbolName) ) { + *targetImage = last->foundIn; + return last->result; + } + } + + bool weak_import = (symboFlags & BIND_SYMBOL_FLAGS_WEAK_IMPORT); + uintptr_t symbolAddress; + if ( context.bindFlat || (libraryOrdinal == BIND_SPECIAL_DYLIB_FLAT_LOOKUP) ) { + symbolAddress = this->resolveFlat(context, symbolName, weak_import, runResolver, targetImage); + } + else { + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE ) { + *targetImage = context.mainExecutable; + } + else if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) { + *targetImage = this; + } + else if ( libraryOrdinal <= 0 ) { + dyld::throwf("bad mach-o binary, unknown special library ordinal (%ld) too big for symbol %s in %s", + libraryOrdinal, symbolName, this->getPath()); + } + else if ( (unsigned)libraryOrdinal <= libraryCount() ) { + *targetImage = libImage((unsigned int)libraryOrdinal-1); + } + else { + dyld::throwf("bad mach-o binary, library ordinal (%ld) too big (max %u) for symbol %s in %s", + libraryOrdinal, libraryCount(), symbolName, this->getPath()); + } + if ( *targetImage == NULL ) { + if ( weak_import ) { + // if target library not loaded and reference is weak or library is weak return 0 + symbolAddress = 0; + } + else { + dyld::throwf("can't resolve symbol %s in %s because dependent dylib #%ld could not be loaded", + symbolName, this->getPath(), libraryOrdinal); + } + } + else { + symbolAddress = resolveTwolevel(context, symbolName, *targetImage, this, (unsigned)libraryOrdinal, weak_import, runResolver, targetImage); + } + } + + // save off lookup results if client wants + if ( last != NULL ) { + last->ordinal = libraryOrdinal; + last->flags = symboFlags; + last->name = symbolName; + last->foundIn = *targetImage; + last->result = symbolAddress; + } + + return symbolAddress; +} + +uintptr_t ImageLoaderMachOCompressed::bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t symbolFlags, intptr_t addend, long libraryOrdinal, const char* msg, + LastLookup* last, bool runResolver) +{ + const ImageLoader* targetImage; + uintptr_t symbolAddress; + + // resolve symbol + symbolAddress = this->resolve(context, symbolName, symbolFlags, libraryOrdinal, &targetImage, last, runResolver); + + // do actual update + return this->bindLocation(context, addr, symbolAddress, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, msg); +} + + +void ImageLoaderMachOCompressed::throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos) +{ + dyld::throwf("malformed binding opcodes (%ld/%ld): address 0x%08lX is outside segment %s (0x%08lX -> 0x%08lX)", + (intptr_t)(pos-startOpcodes), (intptr_t)(endOpcodes-startOpcodes), address, segName(segmentIndex), + segActualLoadAddress(segmentIndex), segmentEndAddress); +} + + +void ImageLoaderMachOCompressed::doBind(const LinkContext& context, bool forceLazysBound) +{ + 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 { + uint64_t t0 = mach_absolute_time(); + + #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 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 ) + 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); + + uint64_t t1 = mach_absolute_time(); + ImageLoader::fgTotalRebindCacheTime += (t1-t0); + } + + // set up dyld entry points in image + // do last so flat main executables will have __dyld or __program_vars set up + this->setupLazyPointerHandler(context); + CRSetCrashLogMessage2(NULL); +} + + +void ImageLoaderMachOCompressed::doBindJustLazies(const LinkContext& context) +{ + eachLazyBind(context, &ImageLoaderMachOCompressed::bindAt); +} + +#if __arm__ || __arm64__ +int ImageLoaderMachOCompressed::vmAccountingSetSuspended(bool suspend, const LinkContext& context) +{ + if ( context.verboseBind ) + dyld::log("vm.footprint_suspend=%d\n", suspend); + int newValue = suspend ? 1 : 0; + int oldValue = 0; + size_t newlen = sizeof(newValue); + size_t oldlen = sizeof(oldValue); + return sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen); +} +#endif + +void ImageLoaderMachOCompressed::eachBind(const LinkContext& context, bind_handler handler) +{ +#if __arm__ || __arm64__ + // dyld should tell the kernel when it is doing root fix-ups + if ( !sVmAccountingDisabled ) { + if ( fInSharedCache ) { + if ( !sVmAccountingSuspended ) { + int ret = vmAccountingSetSuspended(true, context); + if ( context.verboseBind && (ret != 0) ) + dyld::log("vm.footprint_suspend => %d, errno=%d\n", ret, errno); + if ( ret == 0 ) + sVmAccountingSuspended = true; + else + sVmAccountingDisabled = true; + } + } + else if ( sVmAccountingSuspended ) { + int ret = vmAccountingSetSuspended(false, context); + if ( ret == 0 ) + sVmAccountingSuspended = false; + else if ( errno == ENOENT ) + sVmAccountingDisabled = true; + } + } +#endif + + try { + uint8_t type = 0; + int segmentIndex = -1; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentStartAddress = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + const char* symbolName = NULL; + uint8_t symboFlags = 0; + bool libraryOrdinalSet = false; + long libraryOrdinal = 0; + intptr_t addend = 0; + uintptr_t count; + uintptr_t skip; + uintptr_t segOffset; + LastLookup last = { 0, 0, NULL, 0, NULL }; + const uint8_t* const start = fLinkEditBase + fDyldInfo->bind_off; + const uint8_t* const end = &start[fDyldInfo->bind_size]; + const uint8_t* p = start; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + libraryOrdinalSet = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)", + segmentIndex, fSegmentsCount-1); + #if TEXT_RELOC_SUPPORT + if ( !segWriteable(segmentIndex) && !segHasRebaseFixUps(segmentIndex) && !segHasBindFixUps(segmentIndex) ) + #else + if ( !segWriteable(segmentIndex) ) + #endif + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(segmentIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex)); + segmentStartAddress = segActualLoadAddress(segmentIndex); + address = segmentStartAddress + segOffset; + segmentEndAddress = segActualEndAddress(segmentIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + (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 < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + (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 < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + (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: + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + count = read_uleb128(p, end); + if ( !libraryOrdinalSet ) + dyld::throwf("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB missing preceding BIND_OPCODE_SET_DYLIB_ORDINAL*"); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last, false); + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in bind info", *p); + } + } + } + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } +} + +void ImageLoaderMachOCompressed::eachLazyBind(const LinkContext& context, bind_handler handler) +{ + try { + uint8_t type = BIND_TYPE_POINTER; + int segmentIndex = -1; + uintptr_t address = segActualLoadAddress(0); + uintptr_t segmentStartAddress = segActualLoadAddress(0); + uintptr_t segmentEndAddress = segActualEndAddress(0); + uintptr_t segOffset; + const char* symbolName = NULL; + uint8_t symboFlags = 0; + long libraryOrdinal = 0; + intptr_t addend = 0; + const uint8_t* const start = fLinkEditBase + fDyldInfo->lazy_bind_off; + const uint8_t* const end = &start[fDyldInfo->lazy_bind_size]; + const uint8_t* p = start; + bool done = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + // there is BIND_OPCODE_DONE at end of each lazy bind, don't stop until end of whole sequence + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: + libraryOrdinal = immediate; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: + libraryOrdinal = read_uleb128(p, end); + break; + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: + // the special ordinals are negative numbers + if ( immediate == 0 ) + libraryOrdinal = 0; + else { + int8_t signExtended = BIND_OPCODE_MASK | immediate; + libraryOrdinal = signExtended; + } + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + symbolName = (char*)p; + symboFlags = immediate; + while (*p != '\0') + ++p; + ++p; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segmentIndex = immediate; + if ( (segmentIndex >= fSegmentsCount) || (segmentIndex < 0) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is out of range (0..%d)", + segmentIndex, fSegmentsCount-1); + if ( !segWriteable(segmentIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is not writable", segmentIndex); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(segmentIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segmentIndex)); + segmentStartAddress = segActualLoadAddress(segmentIndex); + segmentEndAddress = segActualEndAddress(segmentIndex); + address = segmentStartAddress + segOffset; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + if ( segmentIndex == -1 ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB"); + if ( (address < segmentStartAddress) || (address >= segmentEndAddress) ) + throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p); + if ( symbolName == NULL ) + dyld::throwf("BIND_OPCODE_DO_BIND missing preceding BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM"); + (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "forced lazy ", NULL, false); + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + default: + dyld::throwf("bad lazy bind opcode %d", *p); + } + } + } + + catch (const char* msg) { + const char* newMsg = dyld::mkstringf("%s in %s", msg, this->getPath()); + free((void*)msg); + throw newMsg; + } +} + +// A program built targeting 10.5 will have hybrid stubs. When used with weak symbols +// the classic lazy loader is used even when running on 10.6 +uintptr_t ImageLoaderMachOCompressed::doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) +{ + // only works with compressed LINKEDIT if classic symbol table is also present + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; + const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + symbolTableStrings = (const char*)&fLinkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&fLinkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + // no symbol table => no lookup by address + if ( (symbolTable == NULL) || (dynSymbolTable == NULL) ) + dyld::throwf("classic lazy binding used with compressed LINKEDIT at %p in image %s", lazyPointer, this->getPath()); + + // scan for all lazy-pointer sections + const bool twoLevel = this->usesTwoLevelNameSpace(); + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; + cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + const uint8_t type = sect->flags & SECTION_TYPE; + uint32_t symbolIndex = INDIRECT_SYMBOL_LOCAL; + if ( type == S_LAZY_SYMBOL_POINTERS ) { + const size_t pointerCount = sect->size / sizeof(uintptr_t); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + fSlide); + if ( (lazyPointer >= symbolPointers) && (lazyPointer < &symbolPointers[pointerCount]) ) { + const uint32_t indirectTableOffset = sect->reserved1; + const size_t lazyIndex = lazyPointer - symbolPointers; + symbolIndex = indirectTable[indirectTableOffset + lazyIndex]; + } + } + if ( (symbolIndex != INDIRECT_SYMBOL_ABS) && (symbolIndex != INDIRECT_SYMBOL_LOCAL) ) { + const macho_nlist* symbol = &symbolTable[symbolIndex]; + const char* symbolName = &symbolTableStrings[symbol->n_un.n_strx]; + int libraryOrdinal = GET_LIBRARY_ORDINAL(symbol->n_desc); + if ( !twoLevel || context.bindFlat ) + libraryOrdinal = BIND_SPECIAL_DYLIB_FLAT_LOOKUP; + uintptr_t ptrToBind = (uintptr_t)lazyPointer; + uintptr_t symbolAddr = bindAt(context, ptrToBind, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL); + ++fgTotalLazyBindFixups; + return symbolAddr; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + dyld::throwf("lazy pointer not found at address %p in image %s", lazyPointer, this->getPath()); +} + + + +uintptr_t ImageLoaderMachOCompressed::doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) +{ + // 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]; + uint8_t segIndex; + uintptr_t segOffset; + int libraryOrdinal; + const char* symbolName; + bool doneAfterBind; + uintptr_t result; + do { + if ( ! getLazyBindingInfo(lazyBindingInfoOffset, start, end, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) ) + dyld::throwf("bad lazy bind info"); + + if ( segIndex >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + segIndex, fSegmentsCount-1); + if ( segOffset > segSize(segIndex) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(segIndex)); + uintptr_t address = segActualLoadAddress(segIndex) + segOffset; + result = this->bindAt(context, address, BIND_TYPE_POINTER, symbolName, 0, 0, libraryOrdinal, "lazy ", NULL, true); + // Some old apps had multiple lazy symbols bound at once + } while (!doneAfterBind && !context.strictMachORequired); + + if ( !this->usesTwoLevelNameSpace() ) { + // release dyld global lock + if ( unlock != NULL ) + unlock(); + } + return result; +} + +void ImageLoaderMachOCompressed::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.curIndex = 0; + it.endIndex = this->fDyldInfo->weak_bind_size; + it.address = 0; + it.type = 0; + it.addend = 0; +} + + +bool ImageLoaderMachOCompressed::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( this->fDyldInfo->weak_bind_size == 0 ) { + /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info + it.done = true; + it.symbolName = "~~~"; + return true; + } + const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; + const uint8_t* p = start + it.curIndex; + const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; + uintptr_t count; + uintptr_t skip; + uintptr_t segOffset; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + it.done = true; + it.curIndex = p - start; + it.symbolName = "~~~"; // sorts to end + return true; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + it.symbolName = (char*)p; + it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); + it.symbolMatches = false; + while (*p != '\0') + ++p; + ++p; + it.curIndex = p - start; + return false; + case BIND_OPCODE_SET_TYPE_IMM: + it.type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + it.addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( immediate >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + immediate, fSegmentsCount-1); + #if __arm__ + // iOS app compatibility + if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() ) + #elif TEXT_RELOC_SUPPORT + // i386 OS X app compatibility + if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate) + && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) ) + #else + if ( !segWriteable(immediate) ) + #endif + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate)); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(immediate) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate)); + it.address = segActualLoadAddress(immediate) + segOffset; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + it.address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + it.address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); + } + } + /// hmmm, BIND_OPCODE_DONE is missing... + it.done = true; + it.symbolName = "~~~"; + //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); + return true; +} + +uintptr_t ImageLoaderMachOCompressed::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); + const ImageLoader* foundIn = NULL; + const ImageLoader::Symbol* sym = this->findShallowExportedSymbol(it.symbolName, &foundIn); + if ( sym != NULL ) { + //dyld::log("sym=%p, foundIn=%p\n", sym, foundIn); + return foundIn->getExportedSymbolAddress(sym, context, this); + } + return 0; +} + + +void ImageLoaderMachOCompressed::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context) +{ + // weak binding done too early with inserted libraries + if ( this->getState() < dyld_image_state_bound ) + return; + + const uint8_t* start = fLinkEditBase + fDyldInfo->weak_bind_off; + const uint8_t* p = start + it.curIndex; + const uint8_t* end = fLinkEditBase + fDyldInfo->weak_bind_off + this->fDyldInfo->weak_bind_size; + + uint8_t type = it.type; + uintptr_t address = it.address; + const char* symbolName = it.symbolName; + intptr_t addend = it.addend; + uintptr_t count; + uintptr_t skip; + uintptr_t segOffset; + bool done = false; + bool boundSomething = false; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + done = true; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + if ( immediate >= fSegmentsCount ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large (0..%d)", + immediate, fSegmentsCount-1); + #if __arm__ + // iOS app compatibility + if ( !segWriteable(immediate) && it.image->isPositionIndependentExecutable() ) + #elif TEXT_RELOC_SUPPORT + // i386 OS X app compatibility + if ( !segWriteable(immediate) && !segHasRebaseFixUps(immediate) && !segHasBindFixUps(immediate) + && (!it.image->isExecutable() || it.image->isPositionIndependentExecutable()) ) + #else + if ( !segWriteable(immediate) ) + #endif + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB targets segment %s which is not writable", segName(immediate)); + segOffset = read_uleb128(p, end); + if ( segOffset > segSize(immediate) ) + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has offset 0x%08lX beyond segment size (0x%08lX)", segOffset, segSize(immediate)); + address = segActualLoadAddress(immediate) + segOffset; + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + bindLocation(context, address, value, type, symbolName, addend, this->getPath(), targetImage ? targetImage->getPath() : NULL, "weak "); + boundSomething = true; + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in weak binding info", *p); + } + } + // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. + if ( boundSomething && (targetImage != this) ) + context.addDynamicReference(this, targetImage); +} + +uintptr_t ImageLoaderMachOCompressed::interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) +{ + if ( type == BIND_TYPE_POINTER ) { + uintptr_t* fixupLocation = (uintptr_t*)addr; + uintptr_t curValue = *fixupLocation; + uintptr_t newValue = interposedAddress(context, curValue, this); + if ( newValue != curValue) { + *fixupLocation = newValue; + } + } + 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); +} + + +uintptr_t ImageLoaderMachOCompressed::dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver) +{ + if ( type == BIND_TYPE_POINTER ) { + uintptr_t* fixupLocation = (uintptr_t*)addr; + uintptr_t value = *fixupLocation; + // don't apply interposing to table entries. + if ( (context.dynamicInterposeArray <= (void*)addr) && ((void*)addr < &context.dynamicInterposeArray[context.dynamicInterposeCount]) ) + return 0; + for(size_t i=0; i < context.dynamicInterposeCount; ++i) { + if ( value == (uintptr_t)context.dynamicInterposeArray[i].replacee ) { + if ( context.verboseInterposing ) { + dyld::log("dyld: dynamic interposing: at %p replace %p with %p in %s\n", + fixupLocation, context.dynamicInterposeArray[i].replacee, context.dynamicInterposeArray[i].replacement, this->getPath()); + } + *fixupLocation = (uintptr_t)context.dynamicInterposeArray[i].replacement; + } + } + } + return 0; +} + +void ImageLoaderMachOCompressed::dynamicInterpose(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: dynamic interposing %lu tuples onto image: %s\n", context.dynamicInterposeCount, this->getPath()); + + // update already bound references to symbols + eachBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); + eachLazyBind(context, &ImageLoaderMachOCompressed::dynamicInterposeAt); +} + +const char* ImageLoaderMachOCompressed::findClosestSymbol(const void* addr, const void** closestAddr) const +{ + return ImageLoaderMachO::findClosestSymbol((mach_header*)fMachOData, addr, closestAddr); +} + + +#if PREBOUND_IMAGE_SUPPORT +void ImageLoaderMachOCompressed::resetPreboundLazyPointers(const LinkContext& context) +{ + // no way to back off a prebound compress image +} +#endif + + +#if __arm__ || __x86_64__ +void ImageLoaderMachOCompressed::updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context) +{ +#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 ) { + // only de-optimization lazy pointers if they are part of shared cache not loaded (because overridden) + const ImageLoader* lazyPointerImage = context.findImageContainingAddress(lazyPointerAddr); + if ( lazyPointerImage != NULL ) + return; + + // copy newly re-bound lazy pointer value to shared lazy pointer + *lazyPointerAddr = *originalLazyPointerAddr; + + if ( context.verboseBind ) + dyld::log("dyld: alter bind: %s: *0x%08lX = 0x%08lX \n", + this->getShortName(), (long)lazyPointerAddr, (long)*originalLazyPointerAddr); + } +} +#endif + + +// overriding shared cache dylibs with resolvers fails +void ImageLoaderMachOCompressed::updateOptimizedLazyPointers(const LinkContext& context) +{ +#if __arm__ || __x86_64__ + // find stubs and indirect symbol table + const struct macho_section* stubsSection = 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 ( cmd->cmd == LC_DYSYMTAB ) { + dynSymbolTable = (struct dysymtab_command*)cmd; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( dynSymbolTable == NULL ) + return; + const uint32_t* const indirectTable = (uint32_t*)&fLinkEditBase[dynSymbolTable->indirectsymoff]; + if ( stubsSection == NULL ) + return; + const uint32_t stubsSize = stubsSection->reserved2; + const uint32_t stubsCount = (uint32_t)(stubsSection->size / stubsSize); + const uint32_t stubsIndirectTableOffset = stubsSection->reserved1; + if ( (stubsIndirectTableOffset+stubsCount) > dynSymbolTable->nindirectsyms ) + return; + uint8_t* const stubsAddr = (uint8_t*)(stubsSection->addr + this->fSlide); + + // for each lazy pointer section + 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* lazyPointerSection=sectionsStart; lazyPointerSection < sectionsEnd; ++lazyPointerSection) { + const uint8_t type = lazyPointerSection->flags & SECTION_TYPE; + if ( type != S_LAZY_SYMBOL_POINTERS ) + continue; + const uint32_t lazyPointersCount = (uint32_t)(lazyPointerSection->size / sizeof(void*)); + const uint32_t lazyPointersIndirectTableOffset = lazyPointerSection->reserved1; + if ( (lazyPointersIndirectTableOffset+lazyPointersCount) > dynSymbolTable->nindirectsyms ) + continue; + void** const lazyPointersAddr = (void**)(lazyPointerSection->addr + this->fSlide); + // for each lazy pointer + for(uint32_t lpIndex=0; lpIndex < lazyPointersCount; ++lpIndex) { + const uint32_t lpSymbolIndex = indirectTable[lazyPointersIndirectTableOffset+lpIndex]; + // find matching stub and validate it uses this lazy pointer + for(uint32_t stubIndex=0; stubIndex < stubsCount; ++stubIndex) { + if ( indirectTable[stubsIndirectTableOffset+stubIndex] == lpSymbolIndex ) { + this->updateAlternateLazyPointer(stubsAddr+stubIndex*stubsSize, &lazyPointersAddr[lpIndex], context); + break; + } + } + } + + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + +#endif +} + + +void ImageLoaderMachOCompressed::registerEncryption(const encryption_info_command* encryptCmd, const LinkContext& context) +{ +#if __arm__ || __arm64__ + if ( encryptCmd == NULL ) + return; + const mach_header* mh = NULL; + for(unsigned int i=0; i < fSegmentsCount; ++i) { + if ( (segFileOffset(i) == 0) && (segFileSize(i) != 0) ) { + mh = (mach_header*)segActualLoadAddress(i); + break; + } + } + void* start = ((uint8_t*)mh) + encryptCmd->cryptoff; + size_t len = encryptCmd->cryptsize; + uint32_t cputype = mh->cputype; + uint32_t cpusubtype = mh->cpusubtype; + uint32_t cryptid = encryptCmd->cryptid; + if (context.verboseMapping) { + dyld::log(" 0x%08lX->0x%08lX configured for FairPlay decryption\n", (long)start, (long)start+len); + } + int result = mremap_encrypted(start, len, cryptid, cputype, cpusubtype); + if ( result != 0 ) { + dyld::throwf("mremap_encrypted() => %d, errno=%d for %s\n", result, errno, this->getPath()); + } +#endif +} + + + diff --git a/dyld/src/ImageLoaderMachOCompressed.h b/dyld/src/ImageLoaderMachOCompressed.h new file mode 100644 index 0000000..d042387 --- /dev/null +++ b/dyld/src/ImageLoaderMachOCompressed.h @@ -0,0 +1,155 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __IMAGELOADER_MACHO_COMPRESSED__ +#define __IMAGELOADER_MACHO_COMPRESSED__ + +#include + +#include "ImageLoaderMachO.h" + + +// +// ImageLoaderMachOCompressed is the concrete subclass of ImageLoader which loads mach-o files +// that use the compressed LINKEDIT format. +// +class ImageLoaderMachOCompressed : public ImageLoaderMachO { +public: + static ImageLoaderMachOCompressed* instantiateMainExecutable(const macho_header* mh, uintptr_t slide, const char* path, + unsigned int segCount, unsigned int libCount, const LinkContext& context); + static ImageLoaderMachOCompressed* instantiateFromFile(const char* path, int fd, const uint8_t *fileData, size_t lenFileData, + uint64_t offsetInFat, uint64_t lenInFat, const struct stat& info, + unsigned int segCount, unsigned int libCount, + const struct linkedit_data_command* codeSigCmd, + const struct encryption_info_command* encryptCmd, + 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 ~ImageLoaderMachOCompressed(); + + virtual ImageLoader* libImage(unsigned int) const; + virtual bool libReExported(unsigned int) const; + 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, void (*lock)(), void (*unlock)()); + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const; + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned targetIndex, const LinkContext& context); + +protected: + virtual void doInterpose(const LinkContext& context); + virtual void dynamicInterpose(const LinkContext& context); + virtual void setDyldInfo(const dyld_info_command* dyldInfo) { fDyldInfo = dyldInfo; } + virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) {} + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; } + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; } + virtual uint32_t* segmentCommandOffsets() const; + virtual void rebase(const LinkContext& context, uintptr_t slide); + virtual const ImageLoader::Symbol* findShallowExportedSymbol(const char* name, const ImageLoader** foundIn) const; + virtual bool containsSymbol(const void* addr) const; + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const; + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const; + virtual const char* exportedSymbolName(const Symbol* symbol) const; + virtual unsigned int exportedSymbolCount() const; + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const; + virtual unsigned int importedSymbolCount() const; + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const; + virtual const char* importedSymbolName(const Symbol* symbol) const; +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context); +#endif + + +private: + struct LastLookup { long ordinal; uint8_t flags; const char* name; uintptr_t result; const ImageLoader* foundIn; }; + + + typedef uintptr_t (ImageLoaderMachOCompressed::*bind_handler)(const LinkContext& context, uintptr_t addr, uint8_t type, + const char* symbolName, uint8_t symboFlags, intptr_t addend, long libraryOrdinal, + const char* msg, LastLookup* last, bool runResolver); + + void eachLazyBind(const LinkContext& context, bind_handler); + void eachBind(const LinkContext& context, bind_handler); + + + ImageLoaderMachOCompressed(const macho_header* mh, const char* path, unsigned int segCount, + uint32_t segOffsets[], unsigned int libCount); + static ImageLoaderMachOCompressed* instantiateStart(const macho_header* mh, const char* path, unsigned int segCount, unsigned int libCount); + void instantiateFinish(const LinkContext& context); + void markSequentialLINKEDIT(const LinkContext& context); + void markFreeLINKEDIT(const LinkContext& context); + void markLINKEDIT(const LinkContext& context, int advise); + + void rebaseAt(const LinkContext& context, uintptr_t addr, uintptr_t slide, uint8_t type); + void throwBadRebaseAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); + uintptr_t bindAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char* symbolName, + uint8_t symboFlags, intptr_t addend, long libraryOrdinal, const char* msg, + LastLookup* last, bool runResolver=false); + void bindCompressed(const LinkContext& context); + void throwBadBindingAddress(uintptr_t address, uintptr_t segmentEndAddress, int segmentIndex, + const uint8_t* startOpcodes, const uint8_t* endOpcodes, const uint8_t* pos); + uintptr_t resolve(const LinkContext& context, const char* symbolName, + uint8_t symboFlags, long libraryOrdinal, const ImageLoader** targetImage, + LastLookup* last = NULL, bool runResolver=false); + uintptr_t resolveFlat(const LinkContext& context, const char* symbolName, bool weak_import, bool runResolver, + const ImageLoader** foundIn); + uintptr_t resolveCoalesced(const LinkContext& context, const char* symbolName, const ImageLoader** foundIn); + uintptr_t resolveTwolevel(const LinkContext& context, const char* symbolName, const ImageLoader* definedInImage, + const ImageLoader* requestorImage, unsigned requestorOrdinalOfDef, bool weak_import, bool runResolver, + const ImageLoader** foundInn); + uintptr_t interposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver); + uintptr_t dynamicInterposeAt(const LinkContext& context, uintptr_t addr, uint8_t type, const char*, + uint8_t, intptr_t, long, const char*, LastLookup*, bool runResolver); + void updateOptimizedLazyPointers(const LinkContext& context); + void updateAlternateLazyPointer(uint8_t* stub, void** originalLazyPointerAddr, const LinkContext& context); + void registerEncryption(const struct encryption_info_command* encryptCmd, const LinkContext& context); + + const struct dyld_info_command* fDyldInfo; + +#if __arm__ || __arm64__ + static int vmAccountingSetSuspended(bool suspend, const LinkContext& context); + static bool sVmAccountingDisabled; // sysctl not availble + static bool sVmAccountingSuspended; // kernel is currently ignoring COWs +#endif +}; + + + +#endif // __IMAGELOADER_MACHO_COMPRESSED__ + + + + diff --git a/dyld/src/ImageLoaderMegaDylib.cpp b/dyld/src/ImageLoaderMegaDylib.cpp new file mode 100644 index 0000000..a54625d --- /dev/null +++ b/dyld/src/ImageLoaderMegaDylib.cpp @@ -0,0 +1,1182 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 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 __arm__ || __arm64__ + #include +#else + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ImageLoaderMegaDylib.h" +#include "ImageLoaderMachO.h" +#include "mach-o/dyld_images.h" +#include "dyldLibSystemInterface.h" +#include "Tracing.h" +#include "dyld.h" + +// from dyld_gdb.cpp +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); + +extern "C" int _dyld_func_lookup(const char* name, void** address); + + +#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE + #define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02 +#endif + +// relocation_info.r_length field has value 3 for 64-bit executables and value 2 for 32-bit executables +#if __LP64__ + #define RELOC_SIZE 3 + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_ROUTINES_COMMAND LC_ROUTINES_64 + struct macho_segment_command : public segment_command_64 {}; + struct macho_section : public section_64 {}; + struct macho_routines_command : public routines_command_64 {}; +#else + #define RELOC_SIZE 2 + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_ROUTINES_COMMAND LC_ROUTINES + struct macho_segment_command : public segment_command {}; + struct macho_section : public section {}; + struct macho_routines_command : public routines_command {}; +#endif + + + +#if SUPPORT_ACCELERATE_TABLES + + +ImageLoaderMegaDylib* ImageLoaderMegaDylib::makeImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const macho_header* mainMH, const LinkContext& context) +{ + return new ImageLoaderMegaDylib(header, slide, mainMH, context); +} + +struct DATAdyld { + void* dyldLazyBinder; // filled in at launch by dyld to point into dyld to &stub_binding_helper + void* dyldFuncLookup; // filled in at launch by dyld to point into dyld to &_dyld_func_lookup + ProgramVars vars; +}; + + + + +ImageLoaderMegaDylib::ImageLoaderMegaDylib(const dyld_cache_header* header, long slide, const macho_header* mainMH, const LinkContext& context) + : ImageLoader("dyld shared cache", 0), _header(header), _linkEditBias(NULL), _slide(slide), + _lockArray(NULL), _lockArrayInUseCount(0) +{ + pthread_mutex_init(&_lockArrayGuard, NULL); + const dyld_cache_mapping_info* mappings = (const dyld_cache_mapping_info*)((uint8_t*)_header + _header->mappingOffset); + const dyld_cache_mapping_info* lastMapping = &mappings[_header->mappingCount-1]; + const dyld_cache_accelerator_info* accHeader = (dyld_cache_accelerator_info*)(_header->accelerateInfoAddr + slide); + for (const dyld_cache_mapping_info* m=mappings; m <= lastMapping; ++m) { + if ( m->initProt == VM_PROT_READ ) { + _linkEditBias = (uint8_t*)(m->address - m->fileOffset) + _slide; + } + } + + _endOfCacheInMemory = (void*)(lastMapping->address + lastMapping->size + slide); + _images = (const dyld_cache_image_info*)((uint8_t*)_header + _header->imagesOffset); + _imageExtras = (dyld_cache_image_info_extra*)((char*)accHeader + accHeader->imagesExtrasOffset); + _initializers = (dyld_cache_accelerator_initializer*)((char*)accHeader + accHeader->initializersOffset); + _reExportsArray = (uint16_t*)((char*)accHeader + accHeader->reExportListOffset); + _dependenciesArray = (uint16_t*)((char*)accHeader + accHeader->depListOffset); + _bottomUpArray = (uint16_t*)((char*)accHeader + accHeader->bottomUpListOffset); + _rangeTable = (dyld_cache_range_entry*)((char*)accHeader + accHeader->rangeTableOffset); + _rangeTableCount = accHeader->rangeTableCount; + _imageCount = accHeader->imageExtrasCount; + _stateFlags = (uint8_t*)calloc(_imageCount, 1); + _initializerCount = accHeader->initializersCount; + _dylibsTrieStart = (uint8_t*)accHeader + accHeader->dylibTrieOffset; + _dylibsTrieEnd = _dylibsTrieStart + accHeader->dylibTrieSize; + _imageTextInfo = (dyld_cache_image_text_info*)((uint8_t*)_header + _header->imagesTextOffset); + DATAdyld* dyldSection = (DATAdyld*)(accHeader->dyldSectionAddr + slide); + dyldSection->dyldLazyBinder = NULL; // not used by libdyld.dylib + dyldSection->dyldFuncLookup = (void*)&_dyld_func_lookup; + dyldSection->vars.mh = mainMH; + context.setNewProgramVars(dyldSection->vars); +} + + +void ImageLoaderMegaDylib::unreachable() const +{ + abort(); +} + +ImageLoaderMegaDylib::~ImageLoaderMegaDylib() +{ +} + +const char* ImageLoaderMegaDylib::getInstallPath() const { + unreachable(); +} + +const macho_header* ImageLoaderMegaDylib::getIndexedMachHeader(unsigned index) const +{ + if ( index > _header->imagesCount ) + dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1); + return (const macho_header*)(_images[index].address + _slide); +} + +const char* ImageLoaderMegaDylib::getIndexedPath(unsigned index) const +{ + if ( index > _header->imagesCount ) + dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1); + return (char*)_header + _images[index].pathFileOffset; +} + +const char* ImageLoaderMegaDylib::getIndexedShortName(unsigned index) const +{ + const char* path = getIndexedPath(index); + const char* lastSlash = strrchr(path, '/'); + if ( lastSlash == NULL ) + return path; + else + return lastSlash+1; +} + +void ImageLoaderMegaDylib::getDylibUUID(unsigned int index, uuid_t uuid) const +{ + if ( index > _header->imagesCount ) + dyld::throwf("cache image index out of range (%u), max=%u", index, _header->imagesCount - 1); + memcpy(uuid, _imageTextInfo[index].uuid, 16); +} + +void ImageLoaderMegaDylib::printSegments(const macho_header* mh) const +{ + 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; + const macho_segment_command* seg; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + seg = (macho_segment_command*)cmd; + dyld::log("%18s at 0x%08lX->0x%08lX\n", seg->segname, (long)(seg->vmaddr + _slide), (long)(seg->vmaddr + seg->vmsize + _slide)); + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +bool ImageLoaderMegaDylib::hasDylib(const char* path, unsigned* index) const +{ + const uint8_t* imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, path); + if ( imageNode == NULL ) { + #if __MAC_OS_X_VERSION_MIN_REQUIRED + // not all symlinks are recorded as aliases in accelerator tables + if ( (strncmp(path, "/usr/lib/", 9) == 0) || (strncmp(path, "/System/Library/", 16) == 0) ) { + char resolvedPath[PATH_MAX]; + if ( realpath(path, resolvedPath) != NULL ) { + imageNode = ImageLoader::trieWalk(_dylibsTrieStart, _dylibsTrieEnd, resolvedPath); + } + } + if ( imageNode == NULL ) + return false; + #else + return false; + #endif + } + *index = (unsigned)read_uleb128(imageNode, _dylibsTrieEnd); + return true; +} + +bool ImageLoaderMegaDylib::addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index) +{ + // quick out of bounds check +#if __x86_64__ + if ( (uintptr_t)address < 0x7FFF70000000LL ) + return false; +#else + if ( address < (void*)_header ) + return false; +#endif + if ( address > _endOfCacheInMemory ) + return false; + + uint64_t unslidAddress = (uint64_t)address - _slide; + // linear search for now + const dyld_cache_range_entry* rangeTableEnd = &_rangeTable[_rangeTableCount]; + for (const dyld_cache_range_entry* r = _rangeTable; r < rangeTableEnd; ++r) { + if ( (r->startAddress <= unslidAddress) && (unslidAddress < r->startAddress+r->size) ) { + *index = r->imageIndex; + *mh = (mach_header*)getIndexedMachHeader(r->imageIndex); + *path = getIndexedPath(r->imageIndex); + return true; + } + } + + return false; +} + + +bool ImageLoaderMegaDylib::findUnwindSections(const void* address, dyld_unwind_sections* info) +{ + const char* path; + unsigned index; + if ( addressInCache(address, &info->mh, &path, &index) ) { + info->dwarf_section = NULL; + info->dwarf_section_length = 0; + ImageLoaderMachO::findSection(info->mh, "__TEXT", "__eh_frame", (void**)&info->dwarf_section, &info->dwarf_section_length); + + info->compact_unwind_section = NULL; + info->compact_unwind_section_length = 0; + ImageLoaderMachO::findSection(info->mh, "__TEXT", "__unwind_info", (void**)&info->compact_unwind_section, &info->compact_unwind_section_length); + + return true; + } + return false; +} + + +unsigned ImageLoaderMegaDylib::findImageIndex(const LinkContext& context, const char* path) const +{ + unsigned index; + if ( hasDylib(path, &index) ) + return index; + + // Somehow we found the dylib in the cache, but it is not this literal string, try simple expansions of @rpath + if ( strncmp(path, "@rpath/", 7) == 0 ) { + std::vector rpathsFromMainExecutable; + context.mainExecutable->getRPaths(context, rpathsFromMainExecutable); + rpathsFromMainExecutable.push_back("/System/Library/Frameworks/"); + const char* trailingPath = &path[7]; + for (const char* anRPath : rpathsFromMainExecutable) { + if ( anRPath[0] != '/' ) + continue; + char possiblePath[strlen(anRPath) + strlen(trailingPath)+2]; + strcpy(possiblePath, anRPath); + if ( possiblePath[strlen(possiblePath)-1] != '/' ) + strcat(possiblePath, "/"); + strcat(possiblePath, trailingPath); + if ( hasDylib(possiblePath, &index) ) { + return index; + } + } + } + else { + // handle symlinks embedded in load commands + char resolvedPath[PATH_MAX]; + realpath(path, resolvedPath); + int realpathErrno = errno; + // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT + if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) { + if ( strcmp(resolvedPath, path) != 0 ) + return findImageIndex(context, resolvedPath); + } + } + + dyld::throwf("no cache image with name (%s)", path); +} + +void ImageLoaderMegaDylib::initializeCoalIterator(CoalIterator& it, unsigned int loadOrder, unsigned imageIndex) +{ + it.image = this; + it.symbolName = " "; + it.loadOrder = loadOrder; + it.weakSymbol = false; + it.symbolMatches = false; + it.done = false; + it.curIndex = 0; + it.endIndex = _imageExtras[imageIndex].weakBindingsSize; + it.address = 0; + it.type = 0; + it.addend = 0; + it.imageIndex = imageIndex; +} + +bool ImageLoaderMegaDylib::incrementCoalIterator(CoalIterator& it) +{ + if ( it.done ) + return false; + + if ( _imageExtras[it.imageIndex].weakBindingsSize == 0 ) { + /// hmmm, ld set MH_WEAK_DEFINES or MH_BINDS_TO_WEAK, but there is no weak binding info + it.done = true; + it.symbolName = "~~~"; + return true; + } + const uint8_t* start = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide); + const uint8_t* end = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide + _imageExtras[it.imageIndex].weakBindingsSize); + const uint8_t* p = start + it.curIndex; + uintptr_t count; + uintptr_t skip; + uint64_t segOffset; + unsigned segIndex; + const mach_header* mh; + while ( p < end ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + it.done = true; + it.curIndex = p - start; + it.symbolName = "~~~"; // sorts to end + return true; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + it.symbolName = (char*)p; + it.weakSymbol = ((immediate & BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION) == 0); + it.symbolMatches = false; + while (*p != '\0') + ++p; + ++p; + it.curIndex = p - start; + return false; + case BIND_OPCODE_SET_TYPE_IMM: + it.type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + it.addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + mh = (mach_header*)getIndexedMachHeader((unsigned)it.imageIndex); + if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) ) + it.address = segPrefAddress + segOffset + _slide; + else + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large", segIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + it.address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + it.address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + it.address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + it.address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad weak bind opcode '%d' found after processing %d bytes in '%s'", *p, (int)(p-start), this->getPath()); + } + } + /// hmmm, BIND_OPCODE_DONE is missing... + it.done = true; + it.symbolName = "~~~"; + //dyld::log("missing BIND_OPCODE_DONE for image %s\n", this->getPath()); + return true; +} + +uintptr_t ImageLoaderMegaDylib::getAddressCoalIterator(CoalIterator& it, const LinkContext& context) +{ + //dyld::log("looking for %s in %s\n", it.symbolName, this->getPath()); + uintptr_t address; + if ( findInChainedTries(context, it.symbolName, (unsigned)it.imageIndex, NULL, false, &address) ) { + return address; + } + return 0; +} + +void ImageLoaderMegaDylib::updateUsesCoalIterator(CoalIterator& it, uintptr_t value, ImageLoader* targetImage, unsigned targetIndex, const LinkContext& context) +{ + + const uint8_t* start = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide); + const uint8_t* end = (uint8_t*)(_imageExtras[it.imageIndex].weakBindingsAddr + _slide + _imageExtras[it.imageIndex].weakBindingsSize); + const uint8_t* p = start + it.curIndex; + + uint8_t type = it.type; + uintptr_t address = it.address; + const char* symbolName = it.symbolName; + intptr_t addend = it.addend; + uint64_t segOffset; + unsigned segIndex; + const mach_header* mh; + uintptr_t count; + uintptr_t skip; + bool done = false; + bool boundSomething = false; + const char* targetImagePath = targetImage ? targetImage->getIndexedPath(targetIndex) : NULL; + while ( !done && (p < end) ) { + uint8_t immediate = *p & BIND_IMMEDIATE_MASK; + uint8_t opcode = *p & BIND_OPCODE_MASK; + ++p; + switch (opcode) { + case BIND_OPCODE_DONE: + done = true; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: + done = true; + break; + case BIND_OPCODE_SET_TYPE_IMM: + type = immediate; + break; + case BIND_OPCODE_SET_ADDEND_SLEB: + addend = read_sleb128(p, end); + break; + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: + segIndex = immediate; + segOffset = read_uleb128(p, end); + mh = (mach_header*)getIndexedMachHeader((unsigned)it.imageIndex); + if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) ) + address = segPrefAddress + segOffset + _slide; + else + dyld::throwf("BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB has segment %d which is too large", segIndex); + break; + case BIND_OPCODE_ADD_ADDR_ULEB: + address += read_uleb128(p, end); + break; + case BIND_OPCODE_DO_BIND: + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += read_uleb128(p, end) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += immediate*sizeof(intptr_t) + sizeof(intptr_t); + break; + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: + count = read_uleb128(p, end); + skip = read_uleb128(p, end); + for (uint32_t i=0; i < count; ++i) { + ImageLoaderMachO::bindLocation(context, address, value, type, symbolName, addend, getIndexedPath((unsigned)it.imageIndex), targetImagePath, "weak "); + boundSomething = true; + address += skip + sizeof(intptr_t); + } + break; + default: + dyld::throwf("bad bind opcode %d in weak binding info", *p); + } + } + // C++ weak coalescing cannot be tracked by reference counting. Error on side of never unloading. + if ( boundSomething && (targetImage != this) ) + context.addDynamicReference(this, targetImage); +} + + +void ImageLoaderMegaDylib::appendImagesNeedingCoalescing(ImageLoader* images[], unsigned imageIndexes[], unsigned& count) +{ + for (unsigned i=0; i < _imageCount; ++i) { + uint16_t index = _bottomUpArray[i]; + if ( _stateFlags[index] == kStateUnused ) + continue; + if ( _imageExtras[index].weakBindingsSize == 0 ) + continue; + images[count] = this; + imageIndexes[count] = index; + ++count; + } +} + + +bool ImageLoaderMegaDylib::weakSymbolsBound(unsigned index) +{ + return ( _stateFlags[index] >= kStateFlagWeakBound ); +} + +void ImageLoaderMegaDylib::setWeakSymbolsBound(unsigned index) +{ + if ( _stateFlags[index] == kStateFlagBound ) + _stateFlags[index] = kStateFlagWeakBound; +} + + +void ImageLoaderMegaDylib::recursiveMarkLoaded(const LinkContext& context, unsigned imageIndex) +{ + if ( _stateFlags[imageIndex] != kStateUnused ) + return; + + const macho_header* mh = getIndexedMachHeader(imageIndex); + const char* path = getIndexedPath(imageIndex); + + if ( context.verboseLoading ) + dyld::log("dyld: loaded: %s\n", path); + if ( context.verboseMapping ) { + dyld::log("dyld: Using shared cached for %s\n", path); + printSegments(mh); + } + + // change state to "loaded" before recursing to break cycles + _stateFlags[imageIndex] = kStateLoaded; + ++fgImagesUsedFromSharedCache; + + dyld_image_info debuggerInfo; + debuggerInfo.imageLoadAddress = (mach_header*)mh; + debuggerInfo.imageFilePath = path; + debuggerInfo.imageFileModDate = 0; + addImagesToAllImages(1, &debuggerInfo); + + if ( _imageExtras[imageIndex].weakBindingsSize != 0 ) { + ++fgImagesRequiringCoalescing; + ++fgImagesHasWeakDefinitions; + } + + unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) { + unsigned subDep = (_dependenciesArray[i] & 0x7FFF); // mask off upward bit + recursiveMarkLoaded(context, subDep); + } +} + +void ImageLoaderMegaDylib::recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath) +{ + unsigned index = findImageIndex(context, loadPath); + recursiveMarkLoaded(context, index); +} + +unsigned int ImageLoaderMegaDylib::recursiveUpdateDepth(unsigned int maxDepth) +{ + setDepth(maxDepth); + return maxDepth; +} + + +const ImageLoader::Symbol* ImageLoaderMegaDylib::findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const +{ + unsigned index; + if ( !hasDylib(thisPath, &index) ) + return NULL; + const uint8_t* exportNode; + const uint8_t* exportTrieEnd; + unsigned foundinIndex; + // always search re-exports + // the point of searchReExports was to break cycles in dylibs, we don't have cycles in cache, so ok to search deep + searchReExports = true; + if ( searchReExports ) { + if ( !exportTrieHasNodeRecursive(name, index, &exportNode, &exportTrieEnd, &foundinIndex) ) + return NULL; + } + else { + if ( !exportTrieHasNode(name, index, &exportNode, &exportTrieEnd) ) + return NULL; + } + *foundIn = this; + return (ImageLoader::Symbol*)exportNode; +} + +bool ImageLoaderMegaDylib::exportTrieHasNode(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd) const +{ + const uint8_t* start = (uint8_t*)(_imageExtras[index].exportsTrieAddr + _slide); + const uint32_t size = _imageExtras[index].exportsTrieSize; + if ( size == 0 ) + return false; + const uint8_t* end = start + size; + const uint8_t* node = ImageLoader::trieWalk(start, end, symbolName); + if ( node == NULL ) + return false; + *exportNode = node; + *exportTrieEnd = end; + return true; +} + +bool ImageLoaderMegaDylib::exportTrieHasNodeRecursive(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd, + unsigned* foundinIndex) const +{ + // look in trie for image index + if ( exportTrieHasNode(symbolName, index, exportNode, exportTrieEnd) ) { + *foundinIndex = index; + return true; + } + // recursively look in all re-exported tries + unsigned startArrayIndex = _imageExtras[index].reExportsStartArrayIndex; + for (int i=startArrayIndex; _reExportsArray[i] != 0xFFFF; ++i) { + unsigned reExIndex = _reExportsArray[i]; + if ( exportTrieHasNodeRecursive(symbolName, reExIndex, exportNode, exportTrieEnd, foundinIndex) ) + return true; + } + return false; +} + +bool ImageLoaderMegaDylib::findExportedSymbolAddress(const LinkContext& context, const char* symbolName, + const ImageLoader* requestorImage, int requestorOrdinalOfDef, + bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const +{ + const char* definedImagePath = requestorImage->libPath(requestorOrdinalOfDef-1); + unsigned index = findImageIndex(context, definedImagePath); + *foundIn = this; + return findInChainedTries(context, symbolName, index, requestorImage, runResolver, address); +} + +uintptr_t ImageLoaderMegaDylib::getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char* symbolName) const +{ + // scan for with trie contains this node + const uint8_t* exportTrieEnd = NULL; + unsigned imageIndex = 0xFFFF; + const macho_header* mh = NULL; + uint64_t unslidTrieNode = ((uintptr_t)sym) - _slide; + for (unsigned i=0; i < _imageCount; ++i) { + uint64_t start = _imageExtras[i].exportsTrieAddr; + uint64_t end = _imageExtras[i].exportsTrieAddr + _imageExtras[i].exportsTrieSize; + if ( (start < unslidTrieNode) && (unslidTrieNode < end) ) { + exportTrieEnd = (uint8_t*)(end + _slide); + imageIndex = i; + mh = (macho_header*)(_images[imageIndex].address + _slide); + break; + } + } + + if ( mh == NULL ) + dyld::throwf("getExportedSymbolAddress(Symbol=%p) not in a cache trie", sym); + + const uint8_t* exportNode = (const uint8_t*)sym; + uintptr_t address; + processExportNode(context, symbolName ? symbolName : "unknown", imageIndex, exportNode, exportTrieEnd, requestor, runResolver, &address); + return address; +} + +void ImageLoaderMegaDylib::processExportNode(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const uint8_t* exportNode, const uint8_t* exportTrieEnd, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const +{ + const macho_header* mh = getIndexedMachHeader(definedImageIndex); + uintptr_t flags = read_uleb128(exportNode, exportTrieEnd); + uintptr_t rawAddress; + switch ( flags & EXPORT_SYMBOL_FLAGS_KIND_MASK ) { + case EXPORT_SYMBOL_FLAGS_KIND_REGULAR: + if ( runResolver && (flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) ) { + // this node has a stub and resolver, run the resolver to get target address + uintptr_t stub = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh; // skip over stub + // interposing dylibs have the stub address as their replacee + uintptr_t interposedStub = interposedAddress(context, stub, requestorImage); + if ( interposedStub != stub ) { + *address = interposedStub; + return; + } + // stub was not interposed, so run resolver + typedef uintptr_t (*ResolverProc)(void); + ResolverProc resolver = (ResolverProc)(read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh); + *address = (*resolver)(); + if ( context.verboseBind ) + dyld::log("dyld: resolver at %p returned 0x%08lX\n", resolver, *address); + return; + } + if ( flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { + // re-export from another dylib, lookup there + const uintptr_t ordinal = read_uleb128(exportNode, exportTrieEnd); + const char* importedName = (char*)exportNode; + if ( importedName[0] == '\0' ) + importedName = symbolName; + unsigned startArrayIndex = _imageExtras[definedImageIndex].dependentsStartArrayIndex; + unsigned reExImageIndex = _dependenciesArray[startArrayIndex + ordinal-1] & 0x7FFF; + if ( findInChainedTries(context, importedName, reExImageIndex, requestorImage, runResolver, address) ) + return; + dyld::throwf("re-exported symbol '%s' not found for image %s expected re-exported in %s, node=%p", + symbolName, getIndexedShortName(definedImageIndex), getIndexedShortName(reExImageIndex), exportNode); + } + rawAddress = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh; + *address = interposedAddress(context, rawAddress, requestorImage); + return; + case EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode); + *address = read_uleb128(exportNode, exportTrieEnd) + (uintptr_t)mh; + return; + case EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE: + if ( flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode); + *address = read_uleb128(exportNode, exportTrieEnd); + return; + default: + dyld::throwf("unsupported exported symbol kind. flags=%lu at node=%p", flags, exportNode); + } + dyld::throwf("unsupported exported symbol node=%p", exportNode); +} + +bool ImageLoaderMegaDylib::findInChainedTries(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const +{ + //dyld::log("findInChainedTries(sym=%s, index=%u, path=%s)\n", symbolName, definedImageIndex, getIndexedPath(definedImageIndex)); + const uint8_t* exportNode; + const uint8_t* exportTrieEnd; + unsigned foundinIndex; + if ( !exportTrieHasNodeRecursive(symbolName, definedImageIndex, &exportNode, &exportTrieEnd, &foundinIndex) ) + return false; + + processExportNode(context, symbolName, foundinIndex, exportNode, exportTrieEnd, requestorImage, runResolver, address); + return true; +} + + +bool ImageLoaderMegaDylib::findInChainedTriesAndDependentsExcept(const LinkContext& context, const char* symbolName, unsigned imageIndex, + const ImageLoader* requestorImage, bool runResolver, bool alreadyVisited[], uintptr_t* address) const +{ + //dyld::log("findInChainedTriesAndDependentsExcept(sym=%s, index=%u, path=%s)\n", symbolName, imageIndex, getIndexedPath(imageIndex)); + if ( alreadyVisited[imageIndex] ) + return false; + alreadyVisited[imageIndex] = true; + + if ( findInChainedTries(context, symbolName, imageIndex, requestorImage, runResolver, address) ) + return true; + + unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) { + // ignore upward links + if ( (_dependenciesArray[i] & 0x8000) == 0 ) { + unsigned depIndex = _dependenciesArray[i] & 0x7FFF; + if ( _stateFlags[depIndex] != kStateFlagInitialized ) + continue; + if ( findInChainedTriesAndDependentsExcept(context, symbolName, depIndex, requestorImage, runResolver, alreadyVisited, address) ) + return true; + } + } + return false; +} + +bool ImageLoaderMegaDylib::findInChainedTriesAndDependents(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const +{ + //dyld::log("findInChainedTriesAndDependents(sym=%s, index=%u, path=%s)\n", symbolName, definedImageIndex, getIndexedPath(definedImageIndex)); + if ( findInChainedTries(context, symbolName, definedImageIndex, requestorImage, runResolver, address) ) + return true; + + bool alreadyVisited[_header->imagesCount]; + bzero(alreadyVisited, sizeof(alreadyVisited)); + return findInChainedTriesAndDependentsExcept(context, symbolName, definedImageIndex, requestorImage, runResolver, alreadyVisited, address); +} + + +bool ImageLoaderMegaDylib::flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + // check export trie of all in-use images + for (unsigned i=0; i < _imageCount ; ++i) { + uint16_t imageIndex = _bottomUpArray[i]; + if ( _stateFlags[imageIndex] == kStateUnused ) + continue; + if ( onlyInCoalesced && (_imageExtras[imageIndex].weakBindingsSize == 0) ) + continue; + const uint8_t* exportNode; + const uint8_t* exportTrieEnd; + if ( exportTrieHasNode(name, imageIndex, &exportNode, &exportTrieEnd) ) { + *sym = (Symbol*)exportNode; + *image = this; + return true; + } + } + return false; +} + + +void ImageLoaderMegaDylib::markAllbound(const LinkContext& context) +{ + for (unsigned i=0; i < _imageCount; ++i) { + uint16_t imageIndex = _bottomUpArray[i]; + if ( _stateFlags[imageIndex] == kStateLoaded ) { + _stateFlags[imageIndex] = kStateFlagBound; + context.notifySingleFromCache(dyld_image_state_bound, (mach_header*)getIndexedMachHeader(imageIndex), getIndexedPath(imageIndex)); + } + } +} + + +void ImageLoaderMegaDylib::recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread) +{ + pthread_mutex_lock(&_lockArrayGuard); + if ( _lockArray == NULL ) + _lockArray = (recursive_lock*)calloc(_imageCount, sizeof(recursive_lock)); + _lockArrayInUseCount++; + pthread_mutex_unlock(&_lockArrayGuard); + + recursive_lock* imagesLock = &_lockArray[imageIndex]; + while ( !OSAtomicCompareAndSwap32Barrier(0, thisThread, (int*)&imagesLock->thread) ) { + if ( imagesLock->thread == thisThread ) + break; + } + imagesLock->count++; +} + +void ImageLoaderMegaDylib::recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread) +{ + recursive_lock* imagesLock = &_lockArray[imageIndex]; + if ( --imagesLock->count == 0 ) + imagesLock->thread = 0; + + pthread_mutex_lock(&_lockArrayGuard); + _lockArrayInUseCount--; + if ( _lockArrayInUseCount == 0 ) { + free((void*)_lockArray); + _lockArray = NULL; + } + pthread_mutex_unlock(&_lockArrayGuard); +} + + +void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, unsigned int imageIndex, + InitializerTimingList& timingInfo, UpwardIndexes& upInits) +{ + // Don't do any locking until libSystem.dylib is initialized, so we can malloc() the lock array + bool useLock = dyld::gProcessInfo->libSystemInitialized; + if ( useLock ) + recursiveSpinLockAcquire(imageIndex, thisThread); + + // only run initializers if currently in bound state + if ( (_stateFlags[imageIndex] == kStateFlagBound) || (_stateFlags[imageIndex] == kStateFlagWeakBound) ) { + + // Each image in cache has its own lock. We only set the state to Initialized if we hold the lock for the image. + _stateFlags[imageIndex] = kStateFlagInitialized; + + // first recursively init all dependents + unsigned startArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + for (int i=startArrayIndex; _dependenciesArray[i] != 0xFFFF; ++i) { + unsigned subDepIndex = _dependenciesArray[i]; + // ignore upward links + if ( (subDepIndex & 0x8000) == 0 ) + recursiveInitialization(context, thisThread, subDepIndex, timingInfo, upInits); + else + upInits.images[upInits.count++] = (subDepIndex & 0x7FFF); + } + + // notify objc about this image + context.notifySingleFromCache(dyld_image_state_dependents_initialized, (mach_header*)getIndexedMachHeader(imageIndex), getIndexedPath(imageIndex)); + + // run all initializers for imageIndex + const dyld_cache_accelerator_initializer* pInitStart = _initializers; + const dyld_cache_accelerator_initializer* pInitEnd = &pInitStart[_initializerCount]; + bool ranSomeInitializers = false; + uint64_t t1 = mach_absolute_time(); + for (const dyld_cache_accelerator_initializer* p=pInitStart; p < pInitEnd; ++p) { + if ( p->imageIndex == imageIndex ) { + Initializer func = (Initializer)(p->functionOffset + (uintptr_t)_header); + if ( context.verboseInit ) + dyld::log("dyld: calling initializer function %p in %s\n", func, getIndexedPath(imageIndex)); + bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); + dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{ + func(context.argc, context.argv, context.envp, context.apple, &context.programVars); + }); + bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL); + ranSomeInitializers = true; + if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) { + // now safe to use malloc() and other calls in libSystem.dylib + dyld::gProcessInfo->libSystemInitialized = true; + } + } + } + if ( ranSomeInitializers ) { + uint64_t t2 = mach_absolute_time(); + const char* shortName = strrchr(getIndexedPath(imageIndex), '/'); + if ( shortName == NULL ) + shortName = getIndexedPath(imageIndex); + else + ++shortName; + timingInfo.images[timingInfo.count].shortName = shortName; + timingInfo.images[timingInfo.count].initTime = (t2-t1); + timingInfo.count++; + } + } + + // only unlock if this frame locked (note: libSystemInitialized changes after libSystem's initializer is run) + if ( useLock ) + recursiveSpinLockRelease(imageIndex, thisThread); +} + + +void ImageLoaderMegaDylib::recursiveInitialization(const LinkContext& context, mach_port_t thisThread, const char* pathToInitialize, + InitializerTimingList& timingInfo, UninitedUpwards&) +{ + unsigned imageIndex; + if ( hasDylib(pathToInitialize, &imageIndex) ) { + UpwardIndexes upsBuffer[256]; + UpwardIndexes& ups = upsBuffer[0]; + ups.count = 0; + this->recursiveInitialization(context, thisThread, imageIndex, timingInfo, ups); + for (int i=0; i < ups.count; ++i) { + UpwardIndexes upsBuffer2[256]; + UpwardIndexes& ignoreUp = upsBuffer2[0]; + ignoreUp.count = 0; + this->recursiveInitialization(context, thisThread, ups.images[i], timingInfo, ignoreUp); + } + } +} + +void ImageLoaderMegaDylib::recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload) +{ + markAllbound(context); +} + +uint8_t ImageLoaderMegaDylib::dyldStateToCacheState(dyld_image_states state) { + switch (state) { + case dyld_image_state_mapped: + case dyld_image_state_dependents_mapped: + return kStateLoaded; + case dyld_image_state_bound: + return kStateFlagBound; + case dyld_image_state_initialized: + return kStateFlagInitialized; + case dyld_image_state_rebased: + case dyld_image_state_dependents_initialized: + case dyld_image_state_terminated: + return kStateUnused; + } + return kStateUnused; +} + +void ImageLoaderMegaDylib::recursiveApplyInterposing(const LinkContext& context) +{ + if ( context.verboseInterposing ) + dyld::log("dyld: interposing %lu tuples onto shared cache\n", fgInterposingTuples.size()); + + +} + +unsigned ImageLoaderMegaDylib::appendImagesToNotify(dyld_image_states state, bool orLater, dyld_image_info* infos) +{ + uint8_t targetCacheState = dyldStateToCacheState(state); + if ( targetCacheState == kStateUnused ) + return 0; + unsigned usedCount = 0; + for (int i=_imageCount-1; i >= 0; --i) { + uint16_t index = _bottomUpArray[i]; + uint8_t imageState = _stateFlags[index]; + if ( imageState == kStateFlagWeakBound ) + imageState = kStateFlagBound; + if ( (imageState == targetCacheState) || (orLater && (imageState > targetCacheState)) ) { + infos[usedCount].imageLoadAddress = (mach_header*)getIndexedMachHeader(index); + infos[usedCount].imageFilePath = getIndexedPath(index); + infos[usedCount].imageFileModDate = 0; + ++usedCount; + } + } + return usedCount; +} + + +bool ImageLoaderMegaDylib::dlopenFromCache(const LinkContext& context, const char* path, int mode, void** handle) +{ + unsigned imageIndex; + if ( !hasDylib(path, &imageIndex) ) { + return false; + } + + // RTLD_NOLOAD means return handle if already loaded, but don't now load it + if ( mode & RTLD_NOLOAD ) { + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + if ( _stateFlags[imageIndex] == kStateUnused ) { + *handle = NULL; + return true; + } + } + else { + this->recursiveMarkLoaded(context, imageIndex); + context.notifyBatch(dyld_image_state_dependents_mapped, false); + this->markAllbound(context); + context.notifyBatch(dyld_image_state_bound, false); + + this->weakBind(context); + + // Release dyld global lock before running initializers in dlopen() with customer cache + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + + InitializerTimingList timingInfo[_initializerCount]; + timingInfo[0].count = 0; + mach_port_t thisThread = mach_thread_self(); + UpwardIndexes upsBuffer[256]; // room for 511 dangling upward links + UpwardIndexes& ups = upsBuffer[0]; + ups.count = 0; + this->recursiveInitialization(context, thisThread, imageIndex, timingInfo[0], ups); + // make sure any upward linked dylibs were initialized + for (int i=0; i < ups.count; ++i) { + UpwardIndexes upsBuffer2[256]; + UpwardIndexes& ignoreUp = upsBuffer2[0]; + ignoreUp.count = 0; + this->recursiveInitialization(context, thisThread, ups.images[i], timingInfo[0], ignoreUp); + } + mach_port_deallocate(mach_task_self(), thisThread); + context.notifyBatch(dyld_image_state_initialized, false); + } + + *handle = makeCacheHandle(imageIndex, mode); + return true; +} + +bool ImageLoaderMegaDylib::makeCacheHandle(const LinkContext& context, unsigned cacheIndex, int mode, void** result) +{ + if ( cacheIndex >= _imageCount ) + return false; + + *result = makeCacheHandle(cacheIndex, mode); + return true; +} + +void* ImageLoaderMegaDylib::makeCacheHandle(unsigned index, int mode) +{ + uint8_t flags = ((mode & RTLD_FIRST) ? 1 : 0); + +#if __LP64__ + return (void*)(uintptr_t)( 0xFFEEDDCC00000000LL | (index << 8) | flags); +#else + return (void*)(uintptr_t)( 0xF8000000 | (index << 8) | flags); +#endif +} + +bool ImageLoaderMegaDylib::isCacheHandle(void* handle, unsigned* index, uint8_t* flags) +{ +#if __LP64__ + if ( (((uintptr_t)handle) >> 32) == 0xFFEEDDCC ) { + if ( index ) + *index = (((uintptr_t)handle) >> 8) & 0xFFFF; + if ( flags ) + *flags = ((uintptr_t)handle) & 0xFF; + return true; + } +#else + if ( (((uintptr_t)handle) >> 24) == 0xF8 ) { + if ( index ) + *index = (((uintptr_t)handle) >> 8) & 0xFFFF; + if ( flags ) + *flags = ((uintptr_t)handle) & 0xFF; + return true; + } +#endif + return false; +} + + +void* ImageLoaderMegaDylib::dlsymFromCache(const LinkContext& context, void* handle, const char* symbolName, unsigned imageIndex) +{ + unsigned indexInHandle; + uint8_t flags; + uintptr_t symAddress; + if ( handle == RTLD_SELF ) { + if ( findInChainedTriesAndDependents(context, symbolName, imageIndex, NULL, true, &symAddress) ) + return (void*)symAddress; + } + else if ( handle == RTLD_NEXT ) { + // FIXME: really need to not look in imageIndex, but look in others. + if ( findInChainedTriesAndDependents(context, symbolName, imageIndex, NULL, true, &symAddress) ) + return (void*)symAddress; + } + else if ( isCacheHandle(handle, &indexInHandle, &flags) ) { + bool searchOnlyFirst = (flags & 1); // RTLD_FIRST + // normal dlsym(handle,) semantics is that the handle is just the first place to search. RTLD_FIRST disables that + if ( searchOnlyFirst ) { + if ( findInChainedTries(context, symbolName, indexInHandle, NULL, true, &symAddress) ) + return (void*)symAddress; + } + else { + if ( findInChainedTriesAndDependents(context, symbolName, indexInHandle, NULL, true, &symAddress) ) + return (void*)symAddress; + } + } + + return NULL; +} + +bool ImageLoaderMegaDylib::dladdrFromCache(const void* address, Dl_info* info) +{ + const mach_header* mh; + unsigned index; + if ( !addressInCache(address, &mh, &info->dli_fname, &index) ) + return false; + + info->dli_fbase = (void*)mh; + if ( address == mh ) { + // special case lookup of header + info->dli_sname = "__dso_handle"; + info->dli_saddr = info->dli_fbase; + return true; + } + + // find closest symbol in the image + info->dli_sname = ImageLoaderMachO::findClosestSymbol(mh, address, (const void**)&info->dli_saddr); + + // never return the mach_header symbol + if ( info->dli_saddr == info->dli_fbase ) { + info->dli_sname = NULL; + info->dli_saddr = NULL; + return true; + } + + // strip off leading underscore + if ( info->dli_sname != NULL ) { + if ( info->dli_sname[0] == '_' ) + info->dli_sname = info->dli_sname +1; + } + return true; +} + + +uintptr_t ImageLoaderMegaDylib::bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned imageIndex) +{ + const dyld_info_command* dyldInfoCmd = ImageLoaderMachO::findDyldInfoLoadCommand(mh); + if ( dyldInfoCmd == NULL ) + return 0; + + const uint8_t* const lazyInfoStart = &_linkEditBias[dyldInfoCmd->lazy_bind_off]; + const uint8_t* const lazyInfoEnd = &lazyInfoStart[dyldInfoCmd->lazy_bind_size]; + uint32_t lbOffset = (uint32_t)lazyBindingInfoOffset; + uint8_t segIndex; + uintptr_t segOffset; + int libraryOrdinal; + const char* symbolName; + bool doneAfterBind; + if ( ImageLoaderMachO::getLazyBindingInfo(lbOffset, lazyInfoStart, lazyInfoEnd, &segIndex, &segOffset, &libraryOrdinal, &symbolName, &doneAfterBind) ) { + //const char* thisPath = getIndexedPath(imageIndex); + //dyld::log("%s needs symbol '%s' from ordinal=%d\n", thisPath, symbolName, libraryOrdinal); + unsigned startDepArrayIndex = _imageExtras[imageIndex].dependentsStartArrayIndex; + unsigned targetIndex; + if ( libraryOrdinal == BIND_SPECIAL_DYLIB_SELF ) + targetIndex = imageIndex; + else + targetIndex = _dependenciesArray[startDepArrayIndex+libraryOrdinal-1] & 0x7FFF; + //const char* targetPath = getIndexedPath(targetIndex); + //dyld::log("%s needs symbol '%s' from %s\n", thisPath, symbolName, targetPath); + uintptr_t targetAddress; + if ( findInChainedTries(context, symbolName, targetIndex, this, true, &targetAddress) ) { + if ( uintptr_t segPrefAddress = ImageLoaderMachO::segPreferredAddress(mh, segIndex) ) { + uintptr_t* lp = (uintptr_t*)(segPrefAddress + segOffset + _slide); + //dyld::log(" storing 0x%0lX to lp %p\n", targetAddress, lp); + *lp = targetAddress; + return targetAddress; + } + } + } + + return 0; +} + + +#endif // SUPPORT_ACCELERATE_TABLES + + + diff --git a/dyld/src/ImageLoaderMegaDylib.h b/dyld/src/ImageLoaderMegaDylib.h new file mode 100644 index 0000000..6046da6 --- /dev/null +++ b/dyld/src/ImageLoaderMegaDylib.h @@ -0,0 +1,261 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __IMAGELOADER_MEGADYLIB__ +#define __IMAGELOADER_MEGADYLIB__ + +#include +#include + +#include "ImageLoaderMachO.h" +#include "dyld_cache_format.h" + + +// +// ImageLoaderMegaDylib is the concrete subclass of ImageLoader which represents +// all dylibs in the shared cache. +// +class ImageLoaderMegaDylib : public ImageLoader { +public: + static ImageLoaderMegaDylib* makeImageLoaderMegaDylib(const dyld_cache_header*, long slide, const macho_header* mainMH, const LinkContext&); + + + virtual ~ImageLoaderMegaDylib(); + + void appendImagesNeedingCoalescing(ImageLoader* images[], unsigned imageIndex[], unsigned& count); + virtual void initializeCoalIterator(CoalIterator&, unsigned int loadOrder, unsigned imageIndex); + virtual bool incrementCoalIterator(CoalIterator&); + virtual uintptr_t getAddressCoalIterator(CoalIterator&, const LinkContext& contex); + virtual void updateUsesCoalIterator(CoalIterator&, uintptr_t newAddr, ImageLoader* target, unsigned imageIndex, const LinkContext& context); + + virtual const char* getIndexedPath(unsigned index) const; + virtual const char* getIndexedShortName(unsigned) const; + virtual const char* getInstallPath() const; + virtual bool inSharedCache() const { return true; } + virtual bool containsSymbol(const void* addr) const { unreachable(); } + virtual void* getThreadPC() const { unreachable(); } + virtual void* getMain() const { unreachable(); } + virtual const struct mach_header* machHeader() const { unreachable(); } + virtual uintptr_t getSlide() const { return _slide; } + virtual const void* getEnd() const { unreachable(); } + virtual bool hasCoalescedExports() const { unreachable(); } + virtual bool findExportedSymbolAddress(const LinkContext& context, const char* symbolName, + const ImageLoader* requestorImage, int requestorOrdinalOfDef, + bool runResolver, const ImageLoader** foundIn, uintptr_t* address) const; + virtual const Symbol* findExportedSymbol(const char* name, bool searchReExports, const char* thisPath, const ImageLoader** foundIn) const; + virtual uintptr_t getExportedSymbolAddress(const Symbol* sym, const LinkContext& context, + const ImageLoader* requestor, bool runResolver, const char* symbolName) const; + virtual DefinitionFlags getExportedSymbolInfo(const Symbol* sym) const { unreachable(); } + virtual const char* getExportedSymbolName(const Symbol* sym) const { unreachable(); } + virtual uint32_t getExportedSymbolCount() const { unreachable(); } + virtual const Symbol* getIndexedExportedSymbol(uint32_t index) const { unreachable(); } + + virtual uint32_t getImportedSymbolCount() const { unreachable(); } + virtual const Symbol* getIndexedImportedSymbol(uint32_t index) const { unreachable(); } + virtual ReferenceFlags getImportedSymbolInfo(const Symbol* sym) const { unreachable(); } + virtual const char* getImportedSymbolName(const Symbol* sym) const { unreachable(); } + virtual const char* findClosestSymbol(const void* addr, const void** closestAddr) const { unreachable(); } + virtual bool isBundle() const { return false; } + virtual bool isDylib() const { return true; } + virtual bool isExecutable() const { unreachable(); } + virtual bool isPositionIndependentExecutable() const { unreachable(); } + virtual bool forceFlat() const { unreachable(); } + virtual uintptr_t doBindLazySymbol(uintptr_t* lazyPointer, const LinkContext& context) { unreachable(); } + virtual uintptr_t doBindFastLazySymbol(uint32_t lazyBindingInfoOffset, const LinkContext& context, + void (*lock)(), void (*unlock)()) { unreachable(); } + virtual void doTermination(const LinkContext& context) { unreachable(); } + virtual bool needsInitialization() { unreachable(); } + virtual bool getSectionContent(const char* segmentName, const char* sectionName, void** start, size_t* length) { unreachable(); } + virtual void getUnwindInfo(dyld_unwind_sections* info) { unreachable(); } + virtual bool findSection(const void* imageInterior, const char** segmentName, const char** sectionName, size_t* sectionOffset) { unreachable(); } + virtual bool isPrebindable() const { unreachable(); } + virtual bool usablePrebinding(const LinkContext& context) const { unreachable(); } + virtual void getRPaths(const LinkContext& context, std::vector&) const { } + virtual bool participatesInCoalescing() const { unreachable(); } + virtual bool getUUID(uuid_t) const { unreachable(); } + virtual void dynamicInterpose(const LinkContext& context) { unreachable(); } + void addDynamicInterposingTuples(const struct dyld_interpose_tuple array[], size_t count) { unreachable(); } + virtual unsigned int segmentCount() const { unreachable(); } + virtual const char* segName(unsigned int) const { unreachable(); } + virtual uintptr_t segSize(unsigned int) const { unreachable(); } + virtual uintptr_t segFileSize(unsigned int) const { unreachable(); } + virtual bool segHasTrailingZeroFill(unsigned int) { unreachable(); } + virtual uintptr_t segFileOffset(unsigned int) const { unreachable(); } + virtual bool segReadable(unsigned int) const { unreachable(); } + virtual bool segWriteable(unsigned int) const { unreachable(); } + virtual bool segExecutable(unsigned int) const { unreachable(); } + virtual bool segUnaccessible(unsigned int) const { unreachable(); } + virtual bool segHasPreferredLoadAddress(unsigned int) const { unreachable(); } + virtual uintptr_t segPreferredLoadAddress(unsigned int) const { unreachable(); } + virtual uintptr_t segActualLoadAddress(unsigned int) const { unreachable(); } + virtual uintptr_t segActualEndAddress(unsigned int) const { unreachable(); } + + + // info from LC_VERSION_MIN_MACOSX or LC_VERSION_MIN_IPHONEOS + virtual uint32_t sdkVersion() const { unreachable(); } + virtual uint32_t minOSVersion() const { unreachable(); } + + // if the image contains interposing functions, register them + virtual void registerInterposing() { unreachable(); } + + virtual ImageLoader* libImage(unsigned int) const { unreachable(); } + virtual bool libReExported(unsigned int) const { unreachable(); } + virtual bool libIsUpward(unsigned int) const { unreachable(); } + virtual void setLibImage(unsigned int, ImageLoader*, bool, bool) { unreachable(); } + virtual const char* libPath(unsigned int) const { unreachable(); } + + unsigned appendImagesToNotify(dyld_image_states state, bool orLater, dyld_image_info* infos); + const char* notify(dyld_image_states state, bool orLater, dyld_image_state_change_handler); + bool dlopenFromCache(const LinkContext& context, const char* path, int mode, void** handle); + bool makeCacheHandle(const LinkContext& context, unsigned cacheIndex, int mode, void** result); + void* dlsymFromCache(const LinkContext& context, void* handle, const char* symName, unsigned index); + bool isCacheHandle(void* handle, unsigned* index, uint8_t* flags); + bool hasDylib(const char* path, unsigned* index) const; + bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index); + bool findUnwindSections(const void* addr, dyld_unwind_sections* info); + bool dladdrFromCache(const void* address, Dl_info* info); + uintptr_t bindLazy(uintptr_t lazyBindingInfoOffset, const LinkContext& context, const mach_header* mh, unsigned index); + bool flatFindSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image); + void getDylibUUID(unsigned int index, uuid_t) const; + +protected: + virtual void setDyldInfo(const dyld_info_command* dyldInfo) { unreachable(); } + virtual void setSymbolTableInfo(const macho_nlist*, const char*, const dysymtab_command*) { unreachable(); } + virtual uint32_t* segmentCommandOffsets() const { unreachable(); } + virtual void rebase(const LinkContext& context, uintptr_t slide) { unreachable(); } + virtual uintptr_t exportedSymbolAddress(const LinkContext& context, const Symbol* symbol, const ImageLoader* requestor, bool runResolver) const { unreachable(); } + virtual bool exportedSymbolIsWeakDefintion(const Symbol* symbol) const { unreachable(); } + virtual const char* exportedSymbolName(const Symbol* symbol) const { unreachable(); } + virtual unsigned int exportedSymbolCount() const { unreachable(); } + virtual const ImageLoader::Symbol* exportedSymbolIndexed(unsigned int) const { unreachable(); } + virtual unsigned int importedSymbolCount() const { unreachable(); } + virtual const ImageLoader::Symbol* importedSymbolIndexed(unsigned int) const { unreachable(); } + virtual const char* importedSymbolName(const Symbol* symbol) const { unreachable(); } +#if PREBOUND_IMAGE_SUPPORT + virtual void resetPreboundLazyPointers(const LinkContext& context) { unreachable(); } +#endif + + virtual void recursiveLoadLibraries(const LinkContext& context, bool preflightOnly, const RPathChain& loaderRPaths, const char* loadPath); + virtual unsigned recursiveUpdateDepth(unsigned int maxDepth); + virtual void recursiveRebase(const LinkContext& context) { } + virtual void recursiveBind(const LinkContext& context, bool forceLazysBound, bool neverUnload); + virtual void recursiveApplyInterposing(const LinkContext& context); + virtual void recursiveGetDOFSections(const LinkContext& context, std::vector& dofs) { } + virtual void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize, + ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&); + + virtual void doGetDependentLibraries(DependentLibraryInfo libs[]) { unreachable(); } + virtual LibraryInfo doGetLibraryInfo(const LibraryInfo& requestorInfo) { return requestorInfo; } + virtual void doRebase(const LinkContext& context) { unreachable(); } + virtual void doBind(const LinkContext& context, bool forceLazysBound) { unreachable(); } + virtual void doBindJustLazies(const LinkContext& context) { unreachable(); } + virtual void doGetDOFSections(const LinkContext& context, std::vector& dofs) { unreachable(); } + virtual void doInterpose(const LinkContext& context) { unreachable(); } + virtual bool doInitialization(const LinkContext& context) { unreachable(); } + virtual bool needsTermination() { unreachable(); } + virtual bool segmentsMustSlideTogether() const { unreachable(); } + virtual bool segmentsCanSlide() const { unreachable(); } + virtual void setSlide(intptr_t slide) { unreachable(); } + bool allDependentLibrariesAsWhenPreBound() const { unreachable(); } + virtual bool isSubframeworkOf(const LinkContext& context, const ImageLoader* image) const { return false; } + virtual bool hasSubLibrary(const LinkContext& context, const ImageLoader* child) const { return false; } + virtual bool weakSymbolsBound(unsigned index); + virtual void setWeakSymbolsBound(unsigned index); + +private: + ImageLoaderMegaDylib(const dyld_cache_header*, long slide, const macho_header* mainMH, const LinkContext&); + + struct UpwardIndexes + { + uint16_t count; + uint16_t images[1]; + }; + + const macho_header* getIndexedMachHeader(unsigned index) const; + const uint8_t* getIndexedTrie(unsigned index, uint32_t& trieSize) const; + unsigned findImageIndex(const LinkContext& context, const char* path) const; + void recursiveMarkLoaded(const LinkContext& context, unsigned imageIndex); + void markAllbound(const LinkContext& context); + bool findInChainedTries(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const; + bool findInChainedTriesAndDependents(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const; + bool findInChainedTriesAndDependentsExcept(const LinkContext& context, const char* symbolName, unsigned imageIndex, + const ImageLoader* requestorImage, bool runResolver, bool alreadyVisited[], uintptr_t* address) const; + bool exportTrieHasNodeRecursive(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd, + unsigned* foundinIndex) const; + bool exportTrieHasNode(const char* symbolName, unsigned index, + const uint8_t** exportNode, const uint8_t** exportTrieEnd) const; + + void initAllLoaded(const LinkContext& context, InitializerTimingList& timingInfo); + void printSegments(const macho_header* mh) const; + void* makeCacheHandle(unsigned index, int mode); + uint8_t flagsFromCacheHandle(void* handle); + void processExportNode(const LinkContext& context, const char* symbolName, unsigned definedImageIndex, + const uint8_t* exportNode, const uint8_t* exportTrieEnd, + const ImageLoader* requestorImage, bool runResolver, uintptr_t* address) const; + static uint8_t dyldStateToCacheState(dyld_image_states state); + void recursiveInitialization(const LinkContext& context, mach_port_t this_thread, unsigned int imageIndex, + InitializerTimingList& timingInfo, UpwardIndexes&); + void recursiveSpinLockAcquire(unsigned int imageIndex, mach_port_t thisThread); + void recursiveSpinLockRelease(unsigned int imageIndex, mach_port_t thisThread); + + __attribute__((noreturn)) + void unreachable() const; + + enum { kStateUnused=0, kStateLoaded=1, kStateFlagBound=2, kStateFlagWeakBound=3, kStateFlagInitialized=4 }; + + + const dyld_cache_header* _header; + const void* _endOfCacheInMemory; + const uint8_t* _linkEditBias; + const dyld_cache_image_info* _images; + const dyld_cache_image_info_extra* _imageExtras; + long _slide; + const dyld_cache_accelerator_initializer* _initializers; + const dyld_cache_range_entry* _rangeTable; + const uint16_t* _reExportsArray; + const uint16_t* _dependenciesArray; + const uint16_t* _bottomUpArray; + const uint8_t* _dylibsTrieStart; + const uint8_t* _dylibsTrieEnd; + const dyld_cache_image_text_info* _imageTextInfo; + uint8_t* _stateFlags; + uint32_t _imageCount; + uint32_t _initializerCount; + uint32_t _rangeTableCount; + pthread_mutex_t _lockArrayGuard; + ImageLoader::recursive_lock* _lockArray; + unsigned int _lockArrayInUseCount; +}; + + + +#endif // __IMAGELOADER_MEGADYLIB__ + + + + diff --git a/dyld/src/dyld.cpp b/dyld/src/dyld.cpp new file mode 100644 index 0000000..c98a9e3 --- /dev/null +++ b/dyld/src/dyld.cpp @@ -0,0 +1,6067 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // mach_absolute_time() +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include <_simple.h> +#include +#include +#include +#include +#include +#include + +extern "C" int __fork(); + +#include +#include +#include + +#ifndef CPU_SUBTYPE_ARM_V5TEJ + #define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#endif +#ifndef CPU_SUBTYPE_ARM_XSCALE + #define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#endif +#ifndef CPU_SUBTYPE_ARM_V7 + #define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) +#endif +#ifndef CPU_SUBTYPE_ARM_V7F + #define CPU_SUBTYPE_ARM_V7F ((cpu_subtype_t) 10) +#endif +#ifndef CPU_SUBTYPE_ARM_V7S + #define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t) 11) +#endif +#ifndef CPU_SUBTYPE_ARM_V7K + #define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) +#endif +#ifndef LC_DYLD_ENVIRONMENT + #define LC_DYLD_ENVIRONMENT 0x27 +#endif + +#ifndef CPU_SUBTYPE_X86_64_H + #define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t) 8) +#endif + +#ifndef CPU_SUBTYPE_ARM64_E + #define CPU_SUBTYPE_ARM64_E 2 +#endif + +#ifndef VM_PROT_SLIDE + #define VM_PROT_SLIDE 0x20 +#endif + +#include "mach-o/dyld_gdb.h" + +#include "dyld.h" +#include "ImageLoader.h" +#include "ImageLoaderMachO.h" +#include "dyldLibSystemInterface.h" +#include "dyld_cache_format.h" +#include "dyld_process_info_internal.h" +#include +#if TARGET_IPHONE_SIMULATOR + extern "C" void xcoresymbolication_load_notifier(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); + extern "C" void xcoresymbolication_unload_notifier(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); + #define coresymbolication_load_notifier(c, t, p, h) xcoresymbolication_load_notifier(c, t, p, h) + #define coresymbolication_unload_notifier(c, t, p, h) xcoresymbolication_unload_notifier(c, t, p, h) +#endif + +#if SUPPORT_ACCELERATE_TABLES + #include "ImageLoaderMegaDylib.h" +#endif + +#if TARGET_IPHONE_SIMULATOR + extern "C" void* gSyscallHelpers; +#else + #include "dyldSyscallInterface.h" +#endif + +#include "LaunchCache.h" +#include "libdyldEntryVector.h" +#include "MachOParser.h" +#include "Loading.h" +#include "DyldSharedCache.h" +#include "SharedCacheRuntime.h" +#include "StringUtils.h" +#include "Tracing.h" +#include "DyldCacheParser.h" + +extern "C" { + #include "closuredProtocol.h" +} + + +// not libc header for send() syscall interface +extern "C" ssize_t __sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); + + +// ARM and x86_64 are the only architecture that use cpu-sub-types +#define CPU_SUBTYPES_SUPPORTED ((__arm__ || __arm64__ || __x86_64__) && !TARGET_IPHONE_SIMULATOR) + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT + #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO + #define macho_segment_command segment_command_64 + #define macho_section section_64 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define LC_SEGMENT_COMMAND_WRONG LC_SEGMENT_64 + #define LC_ENCRYPT_COMMAND LC_ENCRYPTION_INFO_64 + #define macho_segment_command segment_command + #define macho_section section +#endif + + + +#define CPU_TYPE_MASK 0x00FFFFFF /* complement of CPU_ARCH_MASK */ + + +/* implemented in dyld_gdb.cpp */ +extern void resetAllImages(); +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern void removeImageFromAllImages(const mach_header* mh); +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[]); +extern size_t allImagesCount(); + +// magic so CrashReporter logs message +extern "C" { + char error_string[1024]; +} + +// magic linker symbol for start of dyld binary +extern "C" const macho_header __dso_handle; + + +// +// The file contains the core of dyld used to get a process to main(). +// The API's that dyld supports are implemented in dyldAPIs.cpp. +// +// +// +// +// +namespace dyld { + struct RegisteredDOF { const mach_header* mh; int registrationID; }; + struct DylibOverride { const char* installName; const char* override; }; +} + + +VECTOR_NEVER_DESTRUCTED(ImageLoader*); +VECTOR_NEVER_DESTRUCTED(dyld::RegisteredDOF); +VECTOR_NEVER_DESTRUCTED(dyld::ImageCallback); +VECTOR_NEVER_DESTRUCTED(dyld::DylibOverride); +VECTOR_NEVER_DESTRUCTED(ImageLoader::DynamicReference); + +VECTOR_NEVER_DESTRUCTED(dyld_image_state_change_handler); + +namespace dyld { + + +// +// state of all environment variables dyld uses +// +struct EnvironmentVariables { + const char* const * DYLD_FRAMEWORK_PATH; + const char* const * DYLD_FALLBACK_FRAMEWORK_PATH; + const char* const * DYLD_LIBRARY_PATH; + 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_POST_LAUNCH; + bool DYLD_BIND_AT_LAUNCH; + bool DYLD_PRINT_STATISTICS; + bool DYLD_PRINT_STATISTICS_DETAILS; + bool DYLD_PRINT_OPTS; + bool DYLD_PRINT_ENV; + bool DYLD_DISABLE_DOFS; + bool DYLD_PRINT_CS_NOTIFICATIONS; + // DYLD_SHARED_CACHE_DIR ==> sSharedCacheOverrideDir + // DYLD_ROOT_PATH ==> gLinkContext.rootPaths + // DYLD_IMAGE_SUFFIX ==> gLinkContext.imageSuffix + // DYLD_PRINT_OPTS ==> gLinkContext.verboseOpts + // DYLD_PRINT_ENV ==> gLinkContext.verboseEnv + // DYLD_FORCE_FLAT_NAMESPACE ==> gLinkContext.bindFlat + // DYLD_PRINT_INITIALIZERS ==> gLinkContext.verboseInit + // DYLD_PRINT_SEGMENTS ==> gLinkContext.verboseMapping + // DYLD_PRINT_BINDINGS ==> gLinkContext.verboseBind + // DYLD_PRINT_WEAK_BINDINGS ==> gLinkContext.verboseWeakBind + // DYLD_PRINT_REBASINGS ==> gLinkContext.verboseRebase + // DYLD_PRINT_DOFS ==> gLinkContext.verboseDOF + // DYLD_PRINT_APIS ==> gLogAPIs + // DYLD_IGNORE_PREBINDING ==> gLinkContext.prebindUsage + // DYLD_PREBIND_DEBUG ==> gLinkContext.verbosePrebinding + // 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 + // DYLD_PRINT_LIBRARIES ==> gLinkContext.verboseLoading +}; + + + +typedef std::vector StateHandlers; + + +enum EnvVarMode { envNone, envPrintOnly, envAll }; + +// all global state +static const char* sExecPath = NULL; +static const char* sExecShortName = NULL; +static const macho_header* sMainExecutableMachHeader = NULL; +static uintptr_t sMainExecutableSlide = 0; +#if CPU_SUBTYPES_SUPPORTED +static cpu_type_t sHostCPU; +static cpu_subtype_t sHostCPUsubtype; +#endif +static ImageLoaderMachO* sMainExecutable = NULL; +static EnvVarMode sEnvMode = envNone; +static size_t sInsertedDylibCount = 0; +static std::vector sAllImages; +static std::vector sImageRoots; +static std::vector sImageFilesNeedingTermination; +static std::vector sImageFilesNeedingDOFUnregistration; +static std::vector sAddImageCallbacks; +static std::vector sRemoveImageCallbacks; +static bool sRemoveImageCallbacksInUse = false; +static void* sSingleHandlers[7][3]; +static void* sBatchHandlers[7][3]; +static ImageLoader* sLastImageByAddressCache; +static EnvironmentVariables sEnv; +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static const char* sFrameworkFallbackPaths[] = { "$HOME/Library/Frameworks", "/Library/Frameworks", "/Network/Library/Frameworks", "/System/Library/Frameworks", NULL }; +static const char* sLibraryFallbackPaths[] = { "$HOME/lib", "/usr/local/lib", "/usr/lib", NULL }; +static const char* sRestrictedFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; +static const char* sRestrictedLibraryFallbackPaths[] = { "/usr/lib", NULL }; +#else +static const char* sFrameworkFallbackPaths[] = { "/System/Library/Frameworks", NULL }; +static const char* sLibraryFallbackPaths[] = { "/usr/local/lib", "/usr/lib", NULL }; +#endif +static UndefinedHandler sUndefinedHandler = NULL; +static ImageLoader* sBundleBeingLoaded = NULL; // hack until OFI is reworked +static dyld3::SharedCacheLoadInfo sSharedCacheLoadInfo; +static const char* sSharedCacheOverrideDir; + bool gSharedCacheOverridden = false; +ImageLoader::LinkContext gLinkContext; +bool gLogAPIs = false; +#if SUPPORT_ACCELERATE_TABLES +bool gLogAppAPIs = false; +#endif +const struct LibSystemHelpers* gLibSystemHelpers = NULL; +#if SUPPORT_OLD_CRT_INITIALIZATION +bool gRunInitializersOldWay = false; +#endif +static std::vector sDylibOverrides; +#if !TARGET_IPHONE_SIMULATOR +static int sLogSocket = -1; +#endif +static bool sFrameworksFoundAsDylibs = false; +#if __x86_64__ && !TARGET_IPHONE_SIMULATOR +static bool sHaswell = false; +#endif +static std::vector sDynamicReferences; +static OSSpinLock sDynamicReferencesLock = 0; +#if !TARGET_IPHONE_SIMULATOR +static bool sLogToFile = false; +#endif +static char sLoadingCrashMessage[1024] = "dyld: launch, loading dependent libraries"; +static bool sSafeMode = false; +static _dyld_objc_notify_mapped sNotifyObjCMapped; +static _dyld_objc_notify_init sNotifyObjCInit; +static _dyld_objc_notify_unmapped sNotifyObjCUnmapped; + +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR +static bool sForceStderr = false; +#endif + + +#if SUPPORT_ACCELERATE_TABLES +static ImageLoaderMegaDylib* sAllCacheImagesProxy = NULL; +static bool sDisableAcceleratorTables = false; +#endif + +bool gUseDyld3 = false; +static bool sSkipMain = false; +static bool sEnableClosures = false; + +// +// The MappedRanges structure is used for fast address->image lookups. +// The table is only updated when the dyld lock is held, so we don't +// need to worry about multiple writers. But readers may look at this +// data without holding the lock. Therefore, all updates must be done +// in an order that will never cause readers to see inconsistent data. +// The general rule is that if the image field is non-NULL then +// the other fields are valid. +// +struct MappedRanges +{ + MappedRanges* next; + unsigned long count; + struct { + ImageLoader* image; + uintptr_t start; + uintptr_t end; + } array[1]; +}; + +static MappedRanges* sMappedRangesStart; + +void addMappedRange(ImageLoader* image, uintptr_t start, uintptr_t end) +{ + //dyld::log("addMappedRange(0x%lX->0x%lX) for %s\n", start, end, image->getShortName()); + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + for (unsigned long i=0; i < p->count; ++i) { + if ( p->array[i].image == NULL ) { + p->array[i].start = start; + p->array[i].end = end; + // add image field last with a barrier so that any reader will see consistent records + OSMemoryBarrier(); + p->array[i].image = image; + return; + } + } + } + // table must be full, chain another +#if SUPPORT_ACCELERATE_TABLES + unsigned count = (sAllCacheImagesProxy != NULL) ? 16 : 400; +#else + unsigned count = 400; +#endif + size_t allocationSize = sizeof(MappedRanges) + (count-1)*3*sizeof(void*); + MappedRanges* newRanges = (MappedRanges*)malloc(allocationSize); + bzero(newRanges, allocationSize); + newRanges->count = count; + newRanges->array[0].start = start; + newRanges->array[0].end = end; + newRanges->array[0].image = image; + OSMemoryBarrier(); + if ( sMappedRangesStart == NULL ) { + sMappedRangesStart = newRanges; + } + else { + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + if ( p->next == NULL ) { + OSMemoryBarrier(); + p->next = newRanges; + break; + } + } + } +} + +void removedMappedRanges(ImageLoader* image) +{ + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + for (unsigned long i=0; i < p->count; ++i) { + if ( p->array[i].image == image ) { + // clear with a barrier so that any reader will see consistent records + OSMemoryBarrier(); + p->array[i].image = NULL; + } + } + } +} + +ImageLoader* findMappedRange(uintptr_t target) +{ + for (MappedRanges* p = sMappedRangesStart; p != NULL; p = p->next) { + for (unsigned long i=0; i < p->count; ++i) { + if ( p->array[i].image != NULL ) { + if ( (p->array[i].start <= target) && (target < p->array[i].end) ) + return p->array[i].image; + } + } + } + return NULL; +} + + + +const char* mkstringf(const char* format, ...) +{ + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf != NULL ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + const char* t = strdup(_simple_string(buf)); + _simple_sfree(buf); + if ( t != NULL ) + return t; + } + return "mkstringf, out of memory error"; +} + + +void throwf(const char* format, ...) +{ + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf != NULL ) { + va_list list; + va_start(list, format); + _simple_vsprintf(buf, format, list); + va_end(list); + const char* t = strdup(_simple_string(buf)); + _simple_sfree(buf); + if ( t != NULL ) + throw t; + } + throw "throwf, out of memory error"; +} + + +#if !TARGET_IPHONE_SIMULATOR +static int sLogfile = STDERR_FILENO; +#endif + +#if !TARGET_IPHONE_SIMULATOR +// based on CFUtilities.c: also_do_stderr() +static bool useSyslog() +{ + // Use syslog() for processes managed by launchd + static bool launchdChecked = false; + static bool launchdOwned = false; + if ( !launchdChecked && gProcessInfo->libSystemInitialized ) { + if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 11) ) { + // only call isLaunchdOwned() after libSystem is initialized + launchdOwned = (*gLibSystemHelpers->isLaunchdOwned)(); + launchdChecked = true; + } + } + if ( launchdChecked && launchdOwned ) + return true; + + // If stderr is not available, use syslog() + struct stat sb; + int result = fstat(STDERR_FILENO, &sb); + if ( result < 0 ) + return true; // file descriptor 2 is closed + + return false; +} + + +static void socket_syslogv(int priority, const char* format, va_list list) +{ + // lazily create socket and connection to syslogd + if ( sLogSocket == -1 ) { + sLogSocket = ::socket(AF_UNIX, SOCK_DGRAM, 0); + if (sLogSocket == -1) + return; // cannot log + ::fcntl(sLogSocket, F_SETFD, 1); + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, _PATH_LOG, sizeof(addr.sun_path)); + if ( ::connect(sLogSocket, (struct sockaddr *)&addr, sizeof(addr)) == -1 ) { + ::close(sLogSocket); + sLogSocket = -1; + return; + } + } + + // format message to syslogd like: "Process[pid]: message" + _SIMPLE_STRING buf = _simple_salloc(); + if ( buf == NULL ) + return; + if ( _simple_sprintf(buf, "<%d>%s[%d]: ", LOG_USER|LOG_NOTICE, sExecShortName, getpid()) == 0 ) { + if ( _simple_vsprintf(buf, format, list) == 0 ) { + const char* p = _simple_string(buf); + ::__sendto(sLogSocket, p, strlen(p), 0, NULL, 0); + } + } + _simple_sfree(buf); +} + + + +void vlog(const char* format, va_list list) +{ +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // log to console when running iOS app from Xcode + if ( !sLogToFile && !sForceStderr && useSyslog() ) +#else + if ( !sLogToFile && useSyslog() ) +#endif + socket_syslogv(LOG_ERR, format, list); + else { + _simple_vdprintf(sLogfile, format, list); + } +} + +void log(const char* format, ...) +{ + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); +} + + +void vwarn(const char* format, va_list list) +{ + _simple_dprintf(sLogfile, "dyld: warning, "); + _simple_vdprintf(sLogfile, format, list); +} + +void warn(const char* format, ...) +{ + va_list list; + va_start(list, format); + vwarn(format, list); + va_end(list); +} + +#else + extern void vlog(const char* format, va_list list); +#endif // !TARGET_IPHONE_SIMULATOR + + +// control access to sAllImages through a lock +// because global dyld lock is not held during initialization phase of dlopen() +// Use OSSpinLockLock to allow yielding +static OSSpinLock sAllImagesLock = 0; + +static void allImagesLock() +{ + OSSpinLockLock(&sAllImagesLock); +} + +static void allImagesUnlock() +{ + OSSpinLockUnlock(&sAllImagesLock); +} + + +// utility class to assure files are closed when an exception is thrown +class FileOpener { +public: + FileOpener(const char* path); + ~FileOpener(); + int getFileDescriptor() { return fd; } +private: + int fd; +}; + +FileOpener::FileOpener(const char* path) + : fd(-1) +{ + fd = my_open(path, O_RDONLY, 0); +} + +FileOpener::~FileOpener() +{ + if ( fd != -1 ) + close(fd); +} + + +static void registerDOFs(const std::vector& dofs) +{ + const size_t dofSectionCount = dofs.size(); + if ( !sEnv.DYLD_DISABLE_DOFS && (dofSectionCount != 0) ) { + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + if ( fd < 0 ) { + //dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to register dtrace DOF sections\n"); + } + else { + // allocate a buffer on the stack for the variable length dof_ioctl_data_t type + uint8_t buffer[sizeof(dof_ioctl_data_t) + dofSectionCount*sizeof(dof_helper_t)]; + dof_ioctl_data_t* ioctlData = (dof_ioctl_data_t*)buffer; + + // fill in buffer with one dof_helper_t per DOF section + ioctlData->dofiod_count = dofSectionCount; + for (unsigned int i=0; i < dofSectionCount; ++i) { + strlcpy(ioctlData->dofiod_helpers[i].dofhp_mod, dofs[i].imageShortName, DTRACE_MODNAMELEN); + ioctlData->dofiod_helpers[i].dofhp_dof = (uintptr_t)(dofs[i].dof); + ioctlData->dofiod_helpers[i].dofhp_addr = (uintptr_t)(dofs[i].dof); + } + + // tell kernel about all DOF sections en mas + // pass pointer to ioctlData because ioctl() only copies a fixed size amount of data into kernel + user_addr_t val = (user_addr_t)(unsigned long)ioctlData; + if ( ioctl(fd, DTRACEHIOC_ADDDOF, &val) != -1 ) { + // kernel returns a unique identifier for each section in the dofiod_helpers[].dofhp_dof field. + for (unsigned int i=0; i < dofSectionCount; ++i) { + RegisteredDOF info; + info.mh = dofs[i].imageHeader; + info.registrationID = (int)(ioctlData->dofiod_helpers[i].dofhp_dof); + sImageFilesNeedingDOFUnregistration.push_back(info); + if ( gLinkContext.verboseDOF ) { + dyld::log("dyld: registering DOF section %p in %s with dtrace, ID=0x%08X\n", + dofs[i].dof, dofs[i].imageShortName, info.registrationID); + } + } + } + else { + //dyld::log( "dyld: ioctl to register dtrace DOF section failed\n"); + } + close(fd); + } + } +} + +static void unregisterDOF(int registrationID) +{ + int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR); + if ( fd < 0 ) { + dyld::warn("can't open /dev/" DTRACEMNR_HELPER " to unregister dtrace DOF section\n"); + } + else { + ioctl(fd, DTRACEHIOC_REMOVE, registrationID); + close(fd); + if ( gLinkContext.verboseInit ) + dyld::warn("unregistering DOF section ID=0x%08X with dtrace\n", registrationID); + } +} + + +// +// _dyld_register_func_for_add_image() is implemented as part of the general image state change notification +// +static void notifyAddImageCallbacks(ImageLoader* image) +{ + // use guard so that we cannot notify about the same image twice + if ( ! image->addFuncNotified() ) { + for (std::vector::iterator it=sAddImageCallbacks.begin(); it != sAddImageCallbacks.end(); it++) + (*it)(image->machHeader(), image->getSlide()); + image->setAddFuncNotified(); + } +} + + + +// notify gdb about these new images +static const char* updateAllImages(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + // don't add images without paths to all-image-info-list + if ( info[0].imageFilePath != NULL ) + addImagesToAllImages(infoCount, info); + return NULL; +} + + +static StateHandlers* stateToHandlers(dyld_image_states state, void* handlersArray[7][3]) +{ + switch ( state ) { + case dyld_image_state_mapped: + return reinterpret_cast(&handlersArray[0]); + + case dyld_image_state_dependents_mapped: + return reinterpret_cast(&handlersArray[1]); + + case dyld_image_state_rebased: + return reinterpret_cast(&handlersArray[2]); + + case dyld_image_state_bound: + return reinterpret_cast(&handlersArray[3]); + + case dyld_image_state_dependents_initialized: + return reinterpret_cast(&handlersArray[4]); + + case dyld_image_state_initialized: + return reinterpret_cast(&handlersArray[5]); + + case dyld_image_state_terminated: + return reinterpret_cast(&handlersArray[6]); + } + return NULL; +} + +#if SUPPORT_ACCELERATE_TABLES +static dyld_image_state_change_handler getPreInitNotifyHandler(unsigned index) +{ + std::vector* handlers = stateToHandlers(dyld_image_state_dependents_initialized, sSingleHandlers); + if ( index >= handlers->size() ) + return NULL; + return (*handlers)[index]; +} + +static dyld_image_state_change_handler getBoundBatchHandler(unsigned index) +{ + std::vector* handlers = stateToHandlers(dyld_image_state_bound, sBatchHandlers); + if ( index >= handlers->size() ) + return NULL; + return (*handlers)[index]; +} + +static void notifySingleFromCache(dyld_image_states state, const mach_header* mh, const char* path) +{ + //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + dyld_image_info info; + info.imageLoadAddress = mh; + info.imageFilePath = path; + info.imageFileModDate = 0; + for (dyld_image_state_change_handler handler : *handlers) { + const char* result = (*handler)(state, 1, &info); + if ( (result != NULL) && (state == dyld_image_state_mapped) ) { + //fprintf(stderr, " image rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + const char* str = strdup(result); + throw str; + } + } + } + if ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && (mh->flags & MH_HAS_OBJC) ) { + (*sNotifyObjCInit)(path, mh); + } +} +#endif + +static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; +static bool sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; + +static void notifyMonitoringDyld(bool unloading, unsigned portSlot, unsigned imageCount, const dyld_image_info infos[]) +{ + if ( sZombieNotifiers[portSlot] ) + return; + + unsigned entriesSize = imageCount*sizeof(dyld_process_info_image_entry); + unsigned pathsSize = 0; + for (unsigned j=0; j < imageCount; ++j) { + pathsSize += (strlen(infos[j].imageFilePath) + 1); + } + unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + entriesSize + pathsSize + 127) & -128; // align + if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) { + // Putting all image paths into one message would make buffer too big. + // Instead split into two messages. Recurse as needed until paths fit in buffer. + unsigned imageHalfCount = imageCount/2; + notifyMonitoringDyld(unloading, portSlot, imageHalfCount, infos); + notifyMonitoringDyld(unloading, portSlot, imageCount - imageHalfCount, &infos[imageHalfCount]); + return; + } + uint8_t buffer[totalSize]; + dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer; + header->version = 1; + header->imageCount = imageCount; + header->imagesOffset = sizeof(dyld_process_info_notify_header); + header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize; + header->timestamp = dyld::gProcessInfo->infoArrayChangeTimestamp; + dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset]; + char* const pathPoolStart = (char*)&buffer[header->stringsOffset]; + char* pathPool = pathPoolStart; + for (unsigned j=0; j < imageCount; ++j) { + strcpy(pathPool, infos[j].imageFilePath); + uint32_t len = (uint32_t)strlen(pathPool); + bzero(entries->uuid, 16); + const ImageLoader* image = findImageByMachHeader(infos[j].imageLoadAddress); + if ( image != NULL ) { + image->getUUID(entries->uuid); + } +#if SUPPORT_ACCELERATE_TABLES + else if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(infos[j].imageLoadAddress, &mh, &path, &index) ) { + sAllCacheImagesProxy->getDylibUUID(index, entries->uuid); + } + } +#endif + entries->loadAddress = (uint64_t)infos[j].imageLoadAddress; + entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart); + entries->pathLength = len; + pathPool += (len +1); + ++entries; + } + + if ( sNotifyReplyPorts[portSlot] == 0 ) { + if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) ) + mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND); + //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]); + } + //dyld::log("found port to send to\n"); + mach_msg_header_t* h = (mach_msg_header_t*)buffer; + h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE + h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID; + h->msgh_local_port = sNotifyReplyPorts[portSlot]; + h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[portSlot]; + h->msgh_reserved = 0; + h->msgh_size = (mach_msg_size_t)sizeof(buffer); + //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, dyld::gProcessInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id); + kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 5000, MACH_PORT_NULL); + //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size); + if ( sendResult == MACH_SEND_INVALID_DEST ) { + // sender is not responding, detatch + //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]); + mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[portSlot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]); + dyld::gProcessInfo->notifyPorts[portSlot] = 0; + sNotifyReplyPorts[portSlot] = 0; + } + else if ( sendResult == MACH_RCV_TIMED_OUT ) { + // client took too long, ignore him from now on + sZombieNotifiers[portSlot] = true; + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]); + sNotifyReplyPorts[portSlot] = 0; + } +} + +static void notifyMonitoringDyldMain() +{ + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( (dyld::gProcessInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) { + if ( sNotifyReplyPorts[slot] == 0 ) { + if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) ) + mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND); + //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]); + } + //dyld::log("found port to send to\n"); + uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE]; + mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer; + h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE + h->msgh_id = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID; + h->msgh_local_port = sNotifyReplyPorts[slot]; + h->msgh_remote_port = dyld::gProcessInfo->notifyPorts[slot]; + h->msgh_reserved = 0; + h->msgh_size = (mach_msg_size_t)sizeof(messageBuffer); + //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, dyld::gProcessInfo->notifyPorts[slot], h->msgh_size, sNotifyReplyPorts[slot], h->msgh_id); + kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[slot], 5000, MACH_PORT_NULL); + //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size); + if ( sendResult == MACH_SEND_INVALID_DEST ) { + // sender is not responding, detatch + //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", dyld::gProcessInfo->notifyPorts[slot], sNotifyReplyPorts[slot]); + mach_port_deallocate(mach_task_self(), dyld::gProcessInfo->notifyPorts[slot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + dyld::gProcessInfo->notifyPorts[slot] = 0; + sNotifyReplyPorts[slot] = 0; + } + else if ( sendResult == MACH_RCV_TIMED_OUT ) { + // client took too long, ignore him from now on + sZombieNotifiers[slot] = true; + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + sNotifyReplyPorts[slot] = 0; + } + } + } +} + +void notifyKernel(const ImageLoader& image, bool loading) { + if ( !image.inSharedCache() ) { + uint32_t baseCode = loading ? DBG_DYLD_UUID_MAP_A : DBG_DYLD_UUID_UNMAP_A; + uuid_t uuid; + ino_t inode = image.getInode(); + image.getUUID(uuid); + dyld3::kdebug_trace_dyld_image(baseCode, (const uuid_t *)&uuid, *(fsobj_id_t*)&inode, {{ image.getDevice(), 0 }}, image.machHeader()); + } +} + +static void notifySingle(dyld_image_states state, const ImageLoader* image, ImageLoader::InitializerTimingList* timingInfo) +{ + //dyld::log("notifySingle(state=%d, image=%s)\n", state, image->getPath()); + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getRealPath(); + info.imageFileModDate = image->lastModified(); + for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { + const char* result = (*it)(state, 1, &info); + if ( (result != NULL) && (state == dyld_image_state_mapped) ) { + //fprintf(stderr, " image rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + const char* str = strdup(result); + throw str; + } + } + } + if ( state == dyld_image_state_mapped ) { + // 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 ( (state == dyld_image_state_dependents_initialized) && (sNotifyObjCInit != NULL) && image->notifyObjC() ) { + uint64_t t0 = mach_absolute_time(); + (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); + uint64_t t1 = mach_absolute_time(); + uint64_t t2 = mach_absolute_time(); + uint64_t timeInObjC = t1-t0; + uint64_t emptyTime = (t2-t1)*100; + if ( (timeInObjC > emptyTime) && (timingInfo != NULL) ) { + timingInfo->addTime(image->getShortName(), timeInObjC); + } + } + // mach message csdlc about dynamically unloaded images + if ( image->addFuncNotified() && (state == dyld_image_state_terminated) ) { + notifyKernel(*image, false); + + uint64_t loadTimestamp = mach_absolute_time(); + if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + dyld::log("dyld: coresymbolication_unload_notifier(%p, 0x%016llX, %p, %s)\n", + dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->machHeader(), image->getPath()); + } + if ( dyld::gProcessInfo->coreSymbolicationShmPage != NULL) { + coresymbolication_unload_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, image->getPath(), image->machHeader()); + } + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( dyld::gProcessInfo->notifyPorts[slot] != 0 ) { + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getPath(); + info.imageFileModDate = 0; + notifyMonitoringDyld(true, slot, 1, &info); + } + else if ( sNotifyReplyPorts[slot] != 0 ) { + // monitoring process detached from this process, so release reply port + //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + sNotifyReplyPorts[slot] = 0; + sZombieNotifiers[slot] = false; + } + } + } + +} + + +// +// 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::gProcessInfo. +// It should only be called before terminating. +// +void syncAllImages() +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { + dyld_image_info info; + ImageLoader* image = *it; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getRealPath(); + info.imageFileModDate = image->lastModified(); + // add to all_image_infos if not already there + bool found = false; + int existingCount = dyld::gProcessInfo->infoArrayCount; + const dyld_image_info* existing = dyld::gProcessInfo->infoArray; + if ( existing != NULL ) { + for (int i=0; i < existingCount; ++i) { + if ( existing[i].imageLoadAddress == info.imageLoadAddress ) { + //dyld::log("not adding %s\n", info.imageFilePath); + found = true; + break; + } + } + } + if ( ! found ) { + //dyld::log("adding %s\n", info.imageFilePath); + addImagesToAllImages(1, &info); + } + } +} + + +static int imageSorter(const void* l, const void* r) +{ + const ImageLoader* left = *((ImageLoader**)l); + const ImageLoader* right= *((ImageLoader**)r); + return left->compare(right); +} + +static void notifyBatchPartial(dyld_image_states state, bool orLater, dyld_image_state_change_handler onlyHandler, bool preflightOnly, bool onlyObjCMappedNotification) +{ + std::vector* handlers = stateToHandlers(state, sBatchHandlers); + if ( (handlers != NULL) || ((state == dyld_image_state_bound) && (sNotifyObjCMapped != NULL)) ) { + // don't use a vector because it will use malloc/free and we want notifcation to be low cost + allImagesLock(); + dyld_image_info infos[allImagesCount()+1]; + ImageLoader* images[allImagesCount()+1]; + ImageLoader** end = images; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + dyld_image_states imageState = (*it)->getState(); + if ( (imageState == state) || (orLater && (imageState > state)) ) + *end++ = *it; + } + if ( sBundleBeingLoaded != NULL ) { + dyld_image_states imageState = sBundleBeingLoaded->getState(); + if ( (imageState == state) || (orLater && (imageState > state)) ) + *end++ = sBundleBeingLoaded; + } + const char* dontLoadReason = NULL; + uint32_t imageCount = (uint32_t)(end-images); + if ( imageCount != 0 ) { + // sort bottom up + qsort(images, imageCount, sizeof(ImageLoader*), &imageSorter); + // build info array + for (unsigned int i=0; i < imageCount; ++i) { + dyld_image_info* p = &infos[i]; + ImageLoader* image = images[i]; + //dyld::log(" state=%d, name=%s\n", state, image->getPath()); + p->imageLoadAddress = image->machHeader(); + p->imageFilePath = image->getRealPath(); + p->imageFileModDate = image->lastModified(); + // get these registered with the kernel as early as possible + if ( state == dyld_image_state_dependents_mapped) + notifyKernel(*image, true); + // special case for add_image hook + if ( state == dyld_image_state_bound ) + notifyAddImageCallbacks(image); + } + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(state, orLater, &infos[imageCount]); + // support _dyld_register_func_for_add_image() + if ( state == dyld_image_state_bound ) { + for (ImageCallback callback : sAddImageCallbacks) { + for (unsigned i=0; i < cacheCount; ++i) + (*callback)(infos[imageCount+i].imageLoadAddress, sSharedCacheLoadInfo.slide); + } + } + imageCount += cacheCount; + } +#endif + if ( imageCount != 0 ) { + if ( !onlyObjCMappedNotification ) { + if ( onlyHandler != NULL ) { + const char* result = NULL; + if ( result == NULL ) { + result = (*onlyHandler)(state, imageCount, infos); + } + if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { + //fprintf(stderr, " images rejected by handler=%p\n", onlyHandler); + // make copy of thrown string so that later catch clauses can free it + dontLoadReason = strdup(result); + } + } + else { + // call each handler with whole array + if ( handlers != NULL ) { + for (std::vector::iterator it = handlers->begin(); it != handlers->end(); ++it) { + const char* result = (*it)(state, imageCount, infos); + if ( (result != NULL) && (state == dyld_image_state_dependents_mapped) ) { + //fprintf(stderr, " images rejected by handler=%p\n", *it); + // make copy of thrown string so that later catch clauses can free it + dontLoadReason = strdup(result); + break; + } + } + } + } + } + // tell objc about new images + if ( (onlyHandler == NULL) && ((state == dyld_image_state_bound) || (orLater && (dyld_image_state_bound > state))) && (sNotifyObjCMapped != NULL) ) { + const char* paths[imageCount]; + const mach_header* mhs[imageCount]; + unsigned objcImageCount = 0; + for (int i=0; i < imageCount; ++i) { + const ImageLoader* image = findImageByMachHeader(infos[i].imageLoadAddress); + bool hasObjC = false; + if ( image != NULL ) { + hasObjC = image->notifyObjC(); + } +#if SUPPORT_ACCELERATE_TABLES + else if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(infos[i].imageLoadAddress, &mh, &path, &index) ) { + hasObjC = (mh->flags & MH_HAS_OBJC); + } + } +#endif + if ( hasObjC ) { + paths[objcImageCount] = infos[i].imageFilePath; + mhs[objcImageCount] = infos[i].imageLoadAddress; + ++objcImageCount; + } + } + if ( objcImageCount != 0 ) { + uint64_t t0 = mach_absolute_time(); + (*sNotifyObjCMapped)(objcImageCount, paths, mhs); + uint64_t t1 = mach_absolute_time(); + ImageLoader::fgTotalObjCSetupTime += (t1-t0); + } + } + } + allImagesUnlock(); + if ( dontLoadReason != NULL ) + throw dontLoadReason; + if ( !preflightOnly && (state == dyld_image_state_dependents_mapped) ) { + if ( (dyld::gProcessInfo->coreSymbolicationShmPage != NULL) || sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + // mach message csdlc about loaded images + uint64_t loadTimestamp = mach_absolute_time(); + for (unsigned j=0; j < imageCount; ++j) { + if ( sEnv.DYLD_PRINT_CS_NOTIFICATIONS ) { + dyld::log("dyld: coresymbolication_load_notifier(%p, 0x%016llX, %p, %s)\n", + dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageLoadAddress, infos[j].imageFilePath); + } + coresymbolication_load_notifier(dyld::gProcessInfo->coreSymbolicationShmPage, loadTimestamp, infos[j].imageFilePath, infos[j].imageLoadAddress); + } + } + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( dyld::gProcessInfo->notifyPorts[slot] ) + notifyMonitoringDyld(false, slot, imageCount, infos); + } + } + } +} + + + +static void notifyBatch(dyld_image_states state, bool preflightOnly) +{ + notifyBatchPartial(state, false, NULL, preflightOnly, false); +} + +// In order for register_func_for_add_image() callbacks to to be called bottom up, +// we need to maintain a list of root images. The main executable is usally the +// first root. Any images dynamically added are also roots (unless already loaded). +// If DYLD_INSERT_LIBRARIES is used, those libraries are first. +static void addRootImage(ImageLoader* image) +{ + //dyld::log("addRootImage(%p, %s)\n", image, image->getPath()); + // add to list of roots + sImageRoots.push_back(image); +} + + +static void clearAllDepths() +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) + (*it)->clearDepth(); +} + +static void printAllDepths() +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) + dyld::log("%03d %s\n", (*it)->getDepth(), (*it)->getShortName()); +} + + +static unsigned int imageCount() +{ + allImagesLock(); + unsigned int result = (unsigned int)sAllImages.size(); + allImagesUnlock(); + return (result); +} + + +static void setNewProgramVars(const ProgramVars& newVars) +{ + // make a copy of the pointers to program variables + gLinkContext.programVars = newVars; + + // now set each program global to their initial value + *gLinkContext.programVars.NXArgcPtr = gLinkContext.argc; + *gLinkContext.programVars.NXArgvPtr = gLinkContext.argv; + *gLinkContext.programVars.environPtr = gLinkContext.envp; + *gLinkContext.programVars.__prognamePtr = gLinkContext.progname; +} + +#if SUPPORT_OLD_CRT_INITIALIZATION +static void setRunInitialzersOldWay() +{ + gRunInitializersOldWay = true; +} +#endif + +static bool sandboxBlocked(const char* path, const char* kind) +{ +#if TARGET_IPHONE_SIMULATOR + // sandbox calls not yet supported in simulator runtime + return false; +#else + sandbox_filter_type filter = (sandbox_filter_type)(SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT); + return ( sandbox_check(getpid(), kind, filter, path) > 0 ); +#endif +} + +bool sandboxBlockedMmap(const char* path) +{ + return sandboxBlocked(path, "file-map-executable"); +} + +bool sandboxBlockedOpen(const char* path) +{ + return sandboxBlocked(path, "file-read-data"); +} + +bool sandboxBlockedStat(const char* path) +{ + return sandboxBlocked(path, "file-read-metadata"); +} + + +static void addDynamicReference(ImageLoader* from, ImageLoader* to) { + // don't add dynamic reference if target is in the shared cache (since it can't be unloaded) + if ( to->inSharedCache() ) + return; + + // don't add dynamic reference if there already is a static one + if ( from->dependsOn(to) ) + return; + + // don't add if this combination already exists + OSSpinLockLock(&sDynamicReferencesLock); + for (std::vector::iterator it=sDynamicReferences.begin(); it != sDynamicReferences.end(); ++it) { + if ( (it->from == from) && (it->to == to) ) { + OSSpinLockUnlock(&sDynamicReferencesLock); + return; + } + } + + //dyld::log("addDynamicReference(%s, %s\n", from->getShortName(), to->getShortName()); + ImageLoader::DynamicReference t; + t.from = from; + t.to = to; + sDynamicReferences.push_back(t); + OSSpinLockUnlock(&sDynamicReferencesLock); +} + +static void addImage(ImageLoader* image) +{ + // add to master list + allImagesLock(); + sAllImages.push_back(image); + allImagesUnlock(); + + // update mapped ranges + uintptr_t lastSegStart = 0; + uintptr_t lastSegEnd = 0; + for(unsigned int i=0, e=image->segmentCount(); i < e; ++i) { + if ( image->segUnaccessible(i) ) + continue; + uintptr_t start = image->segActualLoadAddress(i); + uintptr_t end = image->segActualEndAddress(i); + if ( start == lastSegEnd ) { + // two segments are contiguous, just record combined segments + lastSegEnd = end; + } + else { + // non-contiguous segments, record last (if any) + if ( lastSegEnd != 0 ) + addMappedRange(image, lastSegStart, lastSegEnd); + lastSegStart = start; + lastSegEnd = end; + } + } + if ( lastSegEnd != 0 ) + addMappedRange(image, lastSegStart, lastSegEnd); + + + if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { + dyld::log("dyld: loaded: %s\n", image->getPath()); + } + +} + +// +// Helper for std::remove_if +// +class RefUsesImage { +public: + RefUsesImage(ImageLoader* image) : _image(image) {} + bool operator()(const ImageLoader::DynamicReference& ref) const { + return ( (ref.from == _image) || (ref.to == _image) ); + } +private: + ImageLoader* _image; +}; + + + +void removeImage(ImageLoader* image) +{ + // if has dtrace DOF section, tell dtrace it is going away, then remove from sImageFilesNeedingDOFUnregistration + for (std::vector::iterator it=sImageFilesNeedingDOFUnregistration.begin(); it != sImageFilesNeedingDOFUnregistration.end(); ) { + if ( it->mh == image->machHeader() ) { + unregisterDOF(it->registrationID); + sImageFilesNeedingDOFUnregistration.erase(it); + // don't increment iterator, the erase caused next element to be copied to where this iterator points + } + else { + ++it; + } + } + + // tell all registered remove image handlers about this + // do this before removing image from internal data structures so that the callback can query dyld about the image + if ( image->getState() >= dyld_image_state_bound ) { + sRemoveImageCallbacksInUse = true; // This only runs inside dyld's global lock, so ok to use a global for the in-use flag. + for (std::vector::iterator it=sRemoveImageCallbacks.begin(); it != sRemoveImageCallbacks.end(); it++) { + (*it)(image->machHeader(), image->getSlide()); + } + sRemoveImageCallbacksInUse = false; + + if ( sNotifyObjCUnmapped != NULL && image->notifyObjC() ) + (*sNotifyObjCUnmapped)(image->getRealPath(), image->machHeader()); + } + + // notify + notifySingle(dyld_image_state_terminated, image, NULL); + + // remove from mapped images table + removedMappedRanges(image); + + // remove from master list + allImagesLock(); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + if ( *it == image ) { + sAllImages.erase(it); + break; + } + } + allImagesUnlock(); + + // remove from sDynamicReferences + OSSpinLockLock(&sDynamicReferencesLock); + sDynamicReferences.erase(std::remove_if(sDynamicReferences.begin(), sDynamicReferences.end(), RefUsesImage(image)), sDynamicReferences.end()); + OSSpinLockUnlock(&sDynamicReferencesLock); + + // flush find-by-address cache (do this after removed from master list, so there is no chance it can come back) + if ( sLastImageByAddressCache == image ) + sLastImageByAddressCache = NULL; + + // if in root list, pull it out + for (std::vector::iterator it=sImageRoots.begin(); it != sImageRoots.end(); it++) { + if ( *it == image ) { + sImageRoots.erase(it); + break; + } + } + + // log if requested + if ( gLinkContext.verboseLoading || (sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH && (sMainExecutable!=NULL) && sMainExecutable->isLinked()) ) { + dyld::log("dyld: unloaded: %s\n", image->getPath()); + } + + // tell gdb, new way + removeImageFromAllImages(image->machHeader()); +} + + +void runImageStaticTerminators(ImageLoader* image) +{ + // if in termination list, pull it out and run terminator + bool mightBeMore; + do { + mightBeMore = false; + for (std::vector::iterator it=sImageFilesNeedingTermination.begin(); it != sImageFilesNeedingTermination.end(); it++) { + if ( *it == image ) { + sImageFilesNeedingTermination.erase(it); + if (gLogAPIs) dyld::log("dlclose(), running static terminators for %p %s\n", image, image->getShortName()); + image->doTermination(gLinkContext); + mightBeMore = true; + break; + } + } + } while ( mightBeMore ); +} + +static void terminationRecorder(ImageLoader* image) +{ + sImageFilesNeedingTermination.push_back(image); +} + +const char* getExecutablePath() +{ + return sExecPath; +} + +static void runAllStaticTerminators(void* extra) +{ + try { + const size_t imageCount = sImageFilesNeedingTermination.size(); + for(size_t i=imageCount; i > 0; --i){ + ImageLoader* image = sImageFilesNeedingTermination[i-1]; + image->doTermination(gLinkContext); + } + sImageFilesNeedingTermination.clear(); + notifyBatch(dyld_image_state_terminated, false); + } + catch (const char* msg) { + halt(msg); + } +} + +void initializeMainExecutable() +{ + // record that we've reached this step + gLinkContext.startedInitializingMainExecutable = true; + + // run initialzers for any inserted dylibs + ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; + initializerTimes[0].count = 0; + const size_t rootCount = sImageRoots.size(); + if ( rootCount > 1 ) { + for(size_t i=1; i < rootCount; ++i) { + sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); + } + } + + // run initializers for main executable and everything it brings up + sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); + + // register cxa_atexit() handler to run static terminators in all loaded images when this process exits + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->cxa_atexit)(&runAllStaticTerminators, NULL, NULL); + + // dump info if requested + if ( sEnv.DYLD_PRINT_STATISTICS ) + ImageLoader::printStatistics((unsigned int)allImagesCount(), initializerTimes[0]); + if ( sEnv.DYLD_PRINT_STATISTICS_DETAILS ) + ImageLoaderMachO::printStatisticsDetails((unsigned int)allImagesCount(), initializerTimes[0]); +} + +bool mainExecutablePrebound() +{ + return sMainExecutable->usablePrebinding(gLinkContext); +} + +ImageLoader* mainExecutable() +{ + return sMainExecutable; +} + + + + +#if SUPPORT_VERSIONED_PATHS + +// forward reference +static bool getDylibVersionAndInstallname(const char* dylibPath, uint32_t* version, char* installName); + + +// +// 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 void checkDylibOverride(const char* dylibFile) +{ + //dyld::log("checkDylibOverride('%s')\n", dylibFile); + uint32_t altVersion; + char sysInstallName[PATH_MAX]; + if ( getDylibVersionAndInstallname(dylibFile, &altVersion, sysInstallName) && (sysInstallName[0] =='/') ) { + //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::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)); + char resolvedPath[PATH_MAX]; + if ( realpath(dylibFile, resolvedPath) != NULL ) + it->override = strdup(resolvedPath); + else + it->override = strdup(dylibFile); + break; + } + } + } + } + if ( ! entryExists ) { + DylibOverride entry; + entry.installName = strdup(sysInstallName); + char resolvedPath[PATH_MAX]; + if ( realpath(dylibFile, resolvedPath) != NULL ) + entry.override = strdup(resolvedPath); + else + entry.override = strdup(dylibFile); + sDylibOverrides.push_back(entry); + //dyld::log("added override: %s -> %s\n", entry.installName, entry.override); + } + } + } + } + +} + +static void checkDylibOverridesInDir(const char* dirPath) +{ + //dyld::log("checkDylibOverridesInDir('%s')\n", dirPath); + char dylibPath[PATH_MAX]; + long dirPathLen = strlcpy(dylibPath, dirPath, PATH_MAX-1); + if ( dirPathLen >= PATH_MAX ) + return; + 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]; + long dirPathLen = strlcpy(frameworkPath, dirPath, PATH_MAX-1); + if ( dirPathLen >= PATH_MAX ) + return; + 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 = (int)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); + } +} +#endif // SUPPORT_VERSIONED_PATHS + + +// +// 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 }; + + if ( list[0] == '\0' ) + return sEmptyList; + + int colonCount = 0; + for(const char* s=list; *s != '\0'; ++s) { + if (*s == ':') + ++colonCount; + } + + int index = 0; + const char* start = list; + char** result = new char*[colonCount+2]; + for(const char* s=list; *s != '\0'; ++s) { + if (*s == ':') { + size_t len = s-start; + if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n"); + continue; + } +#endif + size_t 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) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n"); + continue; + } +#endif + size_t 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; + } + } + } + size_t len = strlen(start); + if ( (mainExecutableDir != NULL) && (strncmp(start, "@loader_path/", 13) == 0) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @loader_path/ ignored in restricted process\n"); + } + else +#endif + { + size_t 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) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + dyld::log("dyld: warning: @executable_path/ ignored in restricted process\n"); + } + else +#endif + { + size_t 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; + } +} + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static void paths_expand_roots(const char **paths, const char *key, const char *val) +{ +// assert(val != NULL); +// assert(paths != NULL); + if(NULL != key) { + size_t keyLen = strlen(key); + for(int i=0; paths[i] != NULL; ++i) { + if ( strncmp(paths[i], key, keyLen) == 0 ) { + char* newPath = new char[strlen(val) + (strlen(paths[i]) - keyLen) + 1]; + strcpy(newPath, val); + strcat(newPath, &paths[i][keyLen]); + paths[i] = newPath; + } + } + } + return; +} + +static void removePathWithPrefix(const char* paths[], const char* prefix) +{ + size_t prefixLen = strlen(prefix); + int skip = 0; + int i; + for(i = 0; paths[i] != NULL; ++i) { + if ( strncmp(paths[i], prefix, prefixLen) == 0 ) + ++skip; + else + paths[i-skip] = paths[i]; + } + paths[i-skip] = NULL; +} +#endif + + +#if 0 +static void paths_dump(const char **paths) +{ +// assert(paths != NULL); + const char **strs = paths; + while(*strs != NULL) + { + dyld::log("\"%s\"\n", *strs); + strs++; + } + return; +} +#endif + + + +static void printOptions(const char* argv[]) +{ + uint32_t i = 0; + while ( NULL != argv[i] ) { + dyld::log("opt[%i] = \"%s\"\n", i, argv[i]); + i++; + } +} + +static void printEnvironmentVariables(const char* envp[]) +{ + while ( NULL != *envp ) { + dyld::log("%s\n", *envp); + envp++; + } +} + +void processDyldEnvironmentVariable(const char* key, const char* value, const char* mainExecutableDir) +{ + if ( strcmp(key, "DYLD_FRAMEWORK_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FRAMEWORK_PATH); + } + else if ( strcmp(key, "DYLD_FALLBACK_FRAMEWORK_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_FRAMEWORK_PATH); + } + else if ( strcmp(key, "DYLD_LIBRARY_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_LIBRARY_PATH); + } + else if ( strcmp(key, "DYLD_FALLBACK_LIBRARY_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_FALLBACK_LIBRARY_PATH); + } +#if SUPPORT_ROOT_PATH + else if ( (strcmp(key, "DYLD_ROOT_PATH") == 0) || (strcmp(key, "DYLD_PATHS_ROOT") == 0) ) { + if ( strcmp(value, "/") != 0 ) { + 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.rootPaths = NULL; + break; + } + } + } + } +#endif + else if ( strcmp(key, "DYLD_IMAGE_SUFFIX") == 0 ) { + gLinkContext.imageSuffix = value; + } + else if ( strcmp(key, "DYLD_INSERT_LIBRARIES") == 0 ) { + sEnv.DYLD_INSERT_LIBRARIES = parseColonList(value, NULL); +#if SUPPORT_ACCELERATE_TABLES + sDisableAcceleratorTables = true; +#endif + } + else if ( strcmp(key, "DYLD_PRINT_OPTS") == 0 ) { + sEnv.DYLD_PRINT_OPTS = true; + } + else if ( strcmp(key, "DYLD_PRINT_ENV") == 0 ) { + sEnv.DYLD_PRINT_ENV = true; + } + else if ( strcmp(key, "DYLD_DISABLE_DOFS") == 0 ) { + sEnv.DYLD_DISABLE_DOFS = true; + } + else if ( strcmp(key, "DYLD_DISABLE_PREFETCH") == 0 ) { + gLinkContext.preFetchDisabled = true; + } + else if ( strcmp(key, "DYLD_PRINT_LIBRARIES") == 0 ) { + gLinkContext.verboseLoading = true; + } + else if ( strcmp(key, "DYLD_PRINT_LIBRARIES_POST_LAUNCH") == 0 ) { + sEnv.DYLD_PRINT_LIBRARIES_POST_LAUNCH = true; + } + else if ( strcmp(key, "DYLD_BIND_AT_LAUNCH") == 0 ) { + sEnv.DYLD_BIND_AT_LAUNCH = true; + } + else if ( strcmp(key, "DYLD_FORCE_FLAT_NAMESPACE") == 0 ) { + gLinkContext.bindFlat = true; + } + else if ( strcmp(key, "DYLD_NEW_LOCAL_SHARED_REGIONS") == 0 ) { + // ignore, no longer relevant but some scripts still set it + } + else if ( strcmp(key, "DYLD_NO_FIX_PREBINDING") == 0 ) { + } + else if ( strcmp(key, "DYLD_PREBIND_DEBUG") == 0 ) { + gLinkContext.verbosePrebinding = true; + } + else if ( strcmp(key, "DYLD_PRINT_INITIALIZERS") == 0 ) { + gLinkContext.verboseInit = true; + } + else if ( strcmp(key, "DYLD_PRINT_DOFS") == 0 ) { + gLinkContext.verboseDOF = true; + } + else if ( strcmp(key, "DYLD_PRINT_STATISTICS") == 0 ) { + sEnv.DYLD_PRINT_STATISTICS = true; +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps + sForceStderr = true; +#endif + } + else if ( strcmp(key, "DYLD_PRINT_TO_STDERR") == 0 ) { +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR + // DYLD_PRINT_STATISTICS no longer logs to xcode console for device apps + sForceStderr = true; +#endif + } + else if ( strcmp(key, "DYLD_PRINT_STATISTICS_DETAILS") == 0 ) { + sEnv.DYLD_PRINT_STATISTICS_DETAILS = true; + } + else if ( strcmp(key, "DYLD_PRINT_SEGMENTS") == 0 ) { + gLinkContext.verboseMapping = true; + } + else if ( strcmp(key, "DYLD_PRINT_BINDINGS") == 0 ) { + gLinkContext.verboseBind = true; + } + else if ( strcmp(key, "DYLD_PRINT_WEAK_BINDINGS") == 0 ) { + gLinkContext.verboseWeakBind = true; + } + else if ( strcmp(key, "DYLD_PRINT_REBASINGS") == 0 ) { + gLinkContext.verboseRebase = true; + } + else if ( strcmp(key, "DYLD_PRINT_APIS") == 0 ) { + gLogAPIs = true; + } +#if SUPPORT_ACCELERATE_TABLES + else if ( strcmp(key, "DYLD_PRINT_APIS_APP") == 0 ) { + gLogAppAPIs = true; + } +#endif + else if ( strcmp(key, "DYLD_PRINT_WARNINGS") == 0 ) { + gLinkContext.verboseWarnings = 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_PRINT_CODE_SIGNATURES") == 0 ) { + gLinkContext.verboseCodeSignatures = true; + } + else if ( (strcmp(key, "DYLD_SHARED_REGION") == 0) && !sSafeMode ) { + if ( strcmp(value, "private") == 0 ) { + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; + } + else if ( strcmp(value, "avoid") == 0 ) { + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + } + else if ( strcmp(value, "use") == 0 ) { + gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; + } + else if ( value[0] == '\0' ) { + gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; + } + else { + dyld::warn("unknown option to DYLD_SHARED_REGION. Valid options are: use, private, avoid\n"); + } + } + else if ( (strcmp(key, "DYLD_SHARED_CACHE_DIR") == 0) && !sSafeMode ) { + sSharedCacheOverrideDir = value; + } + else if ( strcmp(key, "DYLD_USE_CLOSURES") == 0 ) { + if ( dyld3::loader::internalInstall() ) + sEnableClosures = true; + } + else if ( strcmp(key, "DYLD_IGNORE_PREBINDING") == 0 ) { + if ( strcmp(value, "all") == 0 ) { + gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; + } + else if ( strcmp(value, "app") == 0 ) { + gLinkContext.prebindUsage = ImageLoader::kUseAllButAppPredbinding; + } + else if ( strcmp(value, "nonsplit") == 0 ) { + gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding; + } + else if ( value[0] == '\0' ) { + gLinkContext.prebindUsage = ImageLoader::kUseSplitSegPrebinding; + } + else { + 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); + #if SUPPORT_ACCELERATE_TABLES + sDisableAcceleratorTables = true; + #endif + } + else if ( strcmp(key, "DYLD_VERSIONED_FRAMEWORK_PATH") == 0 ) { + appendParsedColonList(value, mainExecutableDir, &sEnv.DYLD_VERSIONED_FRAMEWORK_PATH); + #if SUPPORT_ACCELERATE_TABLES + sDisableAcceleratorTables = true; + #endif + } +#endif +#if !TARGET_IPHONE_SIMULATOR + else if ( (strcmp(key, "DYLD_PRINT_TO_FILE") == 0) && (mainExecutableDir == NULL) && !sSafeMode ) { + int fd = open(value, O_WRONLY | O_CREAT | O_APPEND, 0644); + if ( fd != -1 ) { + sLogfile = fd; + sLogToFile = true; + } + else { + dyld::log("dyld: could not open DYLD_PRINT_TO_FILE='%s', errno=%d\n", value, errno); + } + } + else if ( (strcmp(key, "DYLD_SKIP_MAIN") == 0)) { + if ( dyld3::loader::internalInstall() ) + sSkipMain = true; + } +#endif + else { + dyld::warn("unknown environment variable: %s\n", key); + } +} + + +#if SUPPORT_LC_DYLD_ENVIRONMENT +static void checkLoadCommandEnvironmentVariables() +{ + // 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)+2]; + 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 size_t keyLen = equals-keyEqualsValue; + // don't let malformed load command overflow stack + if ( keyLen < 40 ) { + 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 + + +static bool hasCodeSignatureLoadCommand(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if (cmd->cmd == LC_CODE_SIGNATURE) + return true; + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} + + +#if SUPPORT_VERSIONED_PATHS +static void checkVersionedPaths() +{ + // 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 + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +// +// For security, setuid programs ignore DYLD_* environment variables. +// Additionally, the DYLD_* enviroment variables are removed +// from the environment, so that any child processes don't see them. +// +static void pruneEnvironmentVariables(const char* envp[], const char*** applep) +{ +#if SUPPORT_LC_DYLD_ENVIRONMENT + checkLoadCommandEnvironmentVariables(); +#endif + + // Are we testing dyld on an internal config? + if ( _simple_getenv(envp, "DYLD_SKIP_MAIN") != NULL ) { + if ( dyld3::loader::internalInstall() ) + sSkipMain = true; + } + + // delete all DYLD_* and LD_LIBRARY_PATH environment variables + int removedCount = 0; + const char** d = envp; + for(const char** s = envp; *s != NULL; s++) { + + if ( (strncmp(*s, "DYLD_", 5) != 0) && (strncmp(*s, "LD_LIBRARY_PATH=", 16) != 0) ) { + *d++ = *s; + } + else { + ++removedCount; + } + } + *d++ = NULL; + // slide apple parameters + if ( removedCount > 0 ) { + *applep = d; + 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 + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = NULL; + sEnv.DYLD_FALLBACK_LIBRARY_PATH = NULL; + + if ( removedCount > 0 ) + strlcat(sLoadingCrashMessage, ", ignoring DYLD_* env vars", sizeof(sLoadingCrashMessage)); +} +#endif + +static void defaultUninitializedFallbackPaths(const char* envp[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sRestrictedFrameworkFallbackPaths; + sEnv.DYLD_FALLBACK_LIBRARY_PATH = sRestrictedLibraryFallbackPaths; + return; + } + + // default value for DYLD_FALLBACK_FRAMEWORK_PATH, if not set in environment + const char* home = _simple_getenv(envp, "HOME");; + if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) { + const char** fpaths = sFrameworkFallbackPaths; + if ( home == NULL ) + removePathWithPrefix(fpaths, "$HOME"); + else + paths_expand_roots(fpaths, "$HOME", home); + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = fpaths; + } + + // default value for DYLD_FALLBACK_LIBRARY_PATH, if not set in environment + if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) { + const char** lpaths = sLibraryFallbackPaths; + if ( home == NULL ) + removePathWithPrefix(lpaths, "$HOME"); + else + paths_expand_roots(lpaths, "$HOME", home); + sEnv.DYLD_FALLBACK_LIBRARY_PATH = lpaths; + } +#else + if ( sEnv.DYLD_FALLBACK_FRAMEWORK_PATH == NULL ) + sEnv.DYLD_FALLBACK_FRAMEWORK_PATH = sFrameworkFallbackPaths; + + if ( sEnv.DYLD_FALLBACK_LIBRARY_PATH == NULL ) + sEnv.DYLD_FALLBACK_LIBRARY_PATH = sLibraryFallbackPaths; +#endif +} + + +static void checkEnvironmentVariables(const char* envp[]) +{ + if ( sEnvMode == envNone ) + return; + const char** p; + for(p = envp; *p != NULL; p++) { + const char* keyEqualsValue = *p; + if ( strncmp(keyEqualsValue, "DYLD_", 5) == 0 ) { + const char* equals = strchr(keyEqualsValue, '='); + if ( equals != NULL ) { + strlcat(sLoadingCrashMessage, "\n", sizeof(sLoadingCrashMessage)); + strlcat(sLoadingCrashMessage, keyEqualsValue, sizeof(sLoadingCrashMessage)); + const char* value = &equals[1]; + const size_t keyLen = equals-keyEqualsValue; + char key[keyLen+1]; + strncpy(key, keyEqualsValue, keyLen); + key[keyLen] = '\0'; + if ( (sEnvMode == envPrintOnly) && (strncmp(key, "DYLD_PRINT_", 11) != 0) ) + continue; + processDyldEnvironmentVariable(key, value, NULL); + } + } + else if ( strncmp(keyEqualsValue, "LD_LIBRARY_PATH=", 16) == 0 ) { + const char* path = &keyEqualsValue[16]; + sEnv.LD_LIBRARY_PATH = parseColonList(path, NULL); + } + } + +#if SUPPORT_LC_DYLD_ENVIRONMENT + checkLoadCommandEnvironmentVariables(); +#endif // SUPPORT_LC_DYLD_ENVIRONMENT + +#if SUPPORT_ROOT_PATH + // DYLD_IMAGE_SUFFIX and DYLD_ROOT_PATH cannot be used together + if ( (gLinkContext.imageSuffix != NULL) && (gLinkContext.rootPaths != NULL) ) { + dyld::warn("Ignoring DYLD_IMAGE_SUFFIX because DYLD_ROOT_PATH is used.\n"); + gLinkContext.imageSuffix = NULL; + } +#endif +} + +#if __x86_64__ && !TARGET_IPHONE_SIMULATOR +static bool isGCProgram(const macho_header* mh, uintptr_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; + if (strcmp(seg->segname, "__DATA") == 0) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (strncmp(sect->sectname, "__objc_imageinfo", 16) == 0) { + const uint32_t* objcInfo = (uint32_t*)(sect->addr + slide); + return (objcInfo[1] & 6); // 6 = (OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC) + } + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + return false; +} +#endif + +static void getHostInfo(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide) +{ +#if CPU_SUBTYPES_SUPPORTED +#if __ARM_ARCH_7K__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7K; +#elif __ARM_ARCH_7A__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7; +#elif __ARM_ARCH_6K__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V6; +#elif __ARM_ARCH_7F__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7F; +#elif __ARM_ARCH_7S__ + sHostCPU = CPU_TYPE_ARM; + sHostCPUsubtype = CPU_SUBTYPE_ARM_V7S; +#elif __arm64e__ + sHostCPU = CPU_TYPE_ARM64; + sHostCPUsubtype = CPU_SUBTYPE_ARM64_E; +#elif __arm64__ + sHostCPU = CPU_TYPE_ARM64; + sHostCPUsubtype = CPU_SUBTYPE_ARM64_V8; +#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; + mach_port_deallocate(mach_task_self(), hostPort); + #if __x86_64__ + // host_info returns CPU_TYPE_I386 even for x86_64. Override that here so that + // we don't need to mask the cpu type later. + sHostCPU = CPU_TYPE_X86_64; + #if !TARGET_IPHONE_SIMULATOR + sHaswell = (sHostCPUsubtype == CPU_SUBTYPE_X86_64_H); + // x86_64h: Fall back to the x86_64 slice if an app requires GC. + if ( sHaswell ) { + if ( isGCProgram(mainExecutableMH, mainExecutableSlide) ) { + // When running a GC program on a haswell machine, don't use and 'h slices + sHostCPUsubtype = CPU_SUBTYPE_X86_64_ALL; + sHaswell = false; + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + } + } + #endif + #endif +#endif +#endif +} + +static void checkSharedRegionDisable(const mach_header* mainExecutableMH) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // if main executable has segments that overlap the shared region, + // then disable using the shared region + dyld3::MachOParser parser(mainExecutableMH); + uintptr_t slide = parser.getSlide(); + dyld3::launch_cache::MemoryRange sharedRegion = { (void*)(long)(SHARED_REGION_BASE), SHARED_REGION_SIZE }; + __block bool disable = false; + parser.forEachSegment(^(const char *segName, uint32_t fileOffset, uint32_t fileSize, uint64_t vmAddr, uint64_t vmSize, uint8_t protections, bool &stop) { + dyld3::launch_cache::MemoryRange segRegion = { (void*)(long)(vmAddr+slide), vmSize }; + if ( segRegion.intersects(sharedRegion) ) + disable = true; + }); + if ( disable ) { + gLinkContext.sharedRegionMode = ImageLoader::kDontUseSharedRegion; + if ( gLinkContext.verboseMapping ) + dyld::warn("disabling shared region because main executable overlaps\n"); + } +#if __i386__ + if ( gLinkContext.processIsRestricted ) { + // use private or no shared region for suid processes + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; + } +#endif +#endif + // iOS cannot run without shared region +} + +bool validImage(const ImageLoader* possibleImage) +{ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { + if ( possibleImage == sAllImages[i] ) { + return true; + } + } + return false; +} + +uint32_t getImageCount() +{ + return (uint32_t)sAllImages.size(); +} + +ImageLoader* getIndexedImage(unsigned int index) +{ + if ( index < sAllImages.size() ) + return sAllImages[index]; + return NULL; +} + +ImageLoader* findImageByMachHeader(const struct mach_header* target) +{ + return findMappedRange((uintptr_t)target); +} + + +ImageLoader* findImageContainingAddress(const void* addr) +{ + #if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(addr, &mh, &path, &index) ) + return sAllCacheImagesProxy; + } + #endif + return findMappedRange((uintptr_t)addr); +} + + +ImageLoader* findImageContainingSymbol(const void* symbol) +{ + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* anImage = *it; + if ( anImage->containsSymbol(symbol) ) + return anImage; + } + return NULL; +} + + + +void forEachImageDo( void (*callback)(ImageLoader*, void* userData), void* userData) +{ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { + ImageLoader* anImage = sAllImages[i]; + (*callback)(anImage, userData); + } +} + +ImageLoader* findLoadedImage(const struct stat& stat_buf) +{ + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i){ + ImageLoader* anImage = sAllImages[i]; + if ( anImage->statMatch(stat_buf) ) + return anImage; + } + return NULL; +} + +// based on ANSI-C strstr() +static const char* strrstr(const char* str, const char* sub) +{ + const size_t sublen = strlen(sub); + for(const char* p = &str[strlen(str)]; p != str; --p) { + if ( strncmp(p, sub, sublen) == 0 ) + return p; + } + return NULL; +} + + +// +// Find framework path +// +// /path/foo.framework/foo => foo.framework/foo +// /path/foo.framework/Versions/A/foo => foo.framework/Versions/A/foo +// /path/foo.framework/Frameworks/bar.framework/bar => bar.framework/bar +// /path/foo.framework/Libraries/bar.dylb => NULL +// /path/foo.framework/bar => NULL +// +// Returns NULL if not a framework path +// +static const char* getFrameworkPartialPath(const char* path) +{ + const char* dirDot = strrstr(path, ".framework/"); + if ( dirDot != NULL ) { + const char* dirStart = dirDot; + for ( ; dirStart >= path; --dirStart) { + if ( (*dirStart == '/') || (dirStart == path) ) { + const char* frameworkStart = &dirStart[1]; + if ( dirStart == path ) + --frameworkStart; + size_t len = dirDot - frameworkStart; + char framework[len+1]; + strncpy(framework, frameworkStart, len); + framework[len] = '\0'; + const char* leaf = strrchr(path, '/'); + if ( leaf != NULL ) { + if ( strcmp(framework, &leaf[1]) == 0 ) { + return frameworkStart; + } + if ( gLinkContext.imageSuffix != NULL ) { + // some debug frameworks have install names that end in _debug + if ( strncmp(framework, &leaf[1], len) == 0 ) { + if ( strcmp( gLinkContext.imageSuffix, &leaf[len+1]) == 0 ) + return frameworkStart; + } + } + } + } + } + } + return NULL; +} + + +static const char* getLibraryLeafName(const char* path) +{ + const char* start = strrchr(path, '/'); + if ( start != NULL ) + return &start[1]; + else + return path; +} + + +// only for architectures that use cpu-sub-types +#if CPU_SUBTYPES_SUPPORTED + +const cpu_subtype_t CPU_SUBTYPE_END_OF_LIST = -1; + + +// +// A fat file may contain multiple sub-images for the same CPU type. +// In that case, dyld picks which sub-image to use by scanning a table +// of preferred cpu-sub-types for the running cpu. +// +// There is one row in the table for each cpu-sub-type on which dyld might run. +// The first entry in a row is that cpu-sub-type. It is followed by all +// cpu-sub-types that can run on that cpu, if preferred order. Each row ends with +// a "SUBTYPE_ALL" (to denote that images written to run on any cpu-sub-type are usable), +// followed by one or more CPU_SUBTYPE_END_OF_LIST to pad out this row. +// + + +#if __arm__ +// +// ARM sub-type lists +// +const int kARM_RowCount = 8; +static const cpu_subtype_t kARM[kARM_RowCount][9] = { + + // armv7f can run: v7f, v7, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv7k can run: v7k + { CPU_SUBTYPE_ARM_V7K, CPU_SUBTYPE_END_OF_LIST }, + + // armv7s can run: v7s, v7, v7f, v7k, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7S, CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V7F, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv7 can run: v7, v6, v5, and v4 + { CPU_SUBTYPE_ARM_V7, CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv6 can run: v6, v5, and v4 + { CPU_SUBTYPE_ARM_V6, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // xscale can run: xscale, v5, and v4 + { CPU_SUBTYPE_ARM_XSCALE, CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // armv5 can run: v5 and v4 + { CPU_SUBTYPE_ARM_V5TEJ, CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, + + // armv4 can run: v4 + { CPU_SUBTYPE_ARM_V4T, CPU_SUBTYPE_ARM_ALL, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST, CPU_SUBTYPE_END_OF_LIST }, +}; +#endif + +#if __arm64__ +// +// ARM64 sub-type lists +// +const int kARM64_RowCount = 2; +static const cpu_subtype_t kARM64[kARM64_RowCount][4] = { + + // armv64e can run: 64e, 64 + { CPU_SUBTYPE_ARM64_E, CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // armv64 can run: 64 + { CPU_SUBTYPE_ARM64_V8, CPU_SUBTYPE_ARM64_ALL, CPU_SUBTYPE_END_OF_LIST }, +}; +#endif + +#if __x86_64__ +// +// x86_64 sub-type lists +// +const int kX86_64_RowCount = 2; +static const cpu_subtype_t kX86_64[kX86_64_RowCount][5] = { + + // x86_64h can run: x86_64h, x86_64h(lib), x86_64(lib), and x86_64 + { CPU_SUBTYPE_X86_64_H, (cpu_subtype_t)(CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_H), (cpu_subtype_t)(CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL), CPU_SUBTYPE_X86_64_ALL, CPU_SUBTYPE_END_OF_LIST }, + + // x86_64 can run: x86_64(lib) and x86_64 + { CPU_SUBTYPE_X86_64_ALL, (cpu_subtype_t)(CPU_SUBTYPE_LIB64|CPU_SUBTYPE_X86_64_ALL), CPU_SUBTYPE_END_OF_LIST }, + +}; +#endif + + +// scan the tables above to find the cpu-sub-type-list for this machine +static const cpu_subtype_t* findCPUSubtypeList(cpu_type_t cpu, cpu_subtype_t subtype) +{ + switch (cpu) { +#if __arm__ + case CPU_TYPE_ARM: + for (int i=0; i < kARM_RowCount ; ++i) { + if ( kARM[i][0] == subtype ) + return kARM[i]; + } + break; +#endif +#if __arm64__ + case CPU_TYPE_ARM64: + for (int i=0; i < kARM64_RowCount ; ++i) { + if ( kARM64[i][0] == subtype ) + return kARM64[i]; + } + break; +#endif +#if __x86_64__ + case CPU_TYPE_X86_64: + for (int i=0; i < kX86_64_RowCount ; ++i) { + if ( kX86_64[i][0] == subtype ) + return kX86_64[i]; + } + break; +#endif + } + return NULL; +} + + + + +// scan fat table-of-contents for best most preferred subtype +static bool fatFindBestFromOrderedList(cpu_type_t cpu, const cpu_subtype_t list[], const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + const fat_arch* const archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for (uint32_t subTypeIndex=0; list[subTypeIndex] != CPU_SUBTYPE_END_OF_LIST; ++subTypeIndex) { + for(uint32_t fatIndex=0; fatIndex < OSSwapBigToHostInt32(fh->nfat_arch); ++fatIndex) { + if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[fatIndex].cputype) == cpu) + && (list[subTypeIndex] == (cpu_subtype_t)OSSwapBigToHostInt32(archs[fatIndex].cpusubtype)) ) { + *offset = OSSwapBigToHostInt32(archs[fatIndex].offset); + *len = OSSwapBigToHostInt32(archs[fatIndex].size); + return true; + } + } + } + return false; +} + +// scan fat table-of-contents for exact match of cpu and cpu-sub-type +static bool fatFindExactMatch(cpu_type_t cpu, cpu_subtype_t subtype, const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( ((cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) + && ((cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == subtype) ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + } + return false; +} + +// scan fat table-of-contents for image with matching cpu-type and runs-on-all-sub-types +static bool fatFindRunsOnAllCPUs(cpu_type_t cpu, const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == cpu) { + switch (cpu) { +#if __arm__ + case CPU_TYPE_ARM: + if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_ARM_ALL ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + break; +#endif +#if __arm64__ + case CPU_TYPE_ARM64: + if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_ARM64_ALL ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + break; +#endif +#if __x86_64__ + case CPU_TYPE_X86_64: + if ( (cpu_subtype_t)OSSwapBigToHostInt32(archs[i].cpusubtype) == CPU_SUBTYPE_X86_64_ALL ) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + break; +#endif + } + } + } + return false; +} + +#endif // CPU_SUBTYPES_SUPPORTED + + +// +// Validate the fat_header and fat_arch array: +// +// 1) arch count would not cause array to extend past 4096 byte read buffer +// 2) no slice overlaps the fat_header and arch array +// 3) arch list does not contain duplicate cputype/cpusubtype tuples +// 4) arch list does not have two overlapping slices. +// +static bool fatValidate(const fat_header* fh) +{ + if ( fh->magic != OSSwapBigToHostInt32(FAT_MAGIC) ) + return false; + + // since only first 4096 bytes of file read, we can only handle up to 204 slices. + const uint32_t sliceCount = OSSwapBigToHostInt32(fh->nfat_arch); + if ( sliceCount > 204 ) + return false; + + // compare all slices looking for conflicts + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for (uint32_t i=0; i < sliceCount; ++i) { + uint32_t i_offset = OSSwapBigToHostInt32(archs[i].offset); + uint32_t i_size = OSSwapBigToHostInt32(archs[i].size); + uint32_t i_cputype = OSSwapBigToHostInt32(archs[i].cputype); + uint32_t i_cpusubtype = OSSwapBigToHostInt32(archs[i].cpusubtype); + uint32_t i_end = i_offset + i_size; + // slice cannot overlap with header + if ( i_offset < 4096 ) + return false; + // slice size cannot overflow + if ( i_end < i_offset ) + return false; + for (uint32_t j=i+1; j < sliceCount; ++j) { + uint32_t j_offset = OSSwapBigToHostInt32(archs[j].offset); + uint32_t j_size = OSSwapBigToHostInt32(archs[j].size); + uint32_t j_cputype = OSSwapBigToHostInt32(archs[j].cputype); + uint32_t j_cpusubtype = OSSwapBigToHostInt32(archs[j].cpusubtype); + uint32_t j_end = j_offset + j_size; + // duplicate slices types not allowed + if ( (i_cputype == j_cputype) && (i_cpusubtype == j_cpusubtype) ) + return false; + // slice size cannot overflow + if ( j_end < j_offset ) + return false; + // check for overlap of slices + if ( i_offset <= j_offset ) { + if ( j_offset < i_end ) + return false; // j overlaps end of i + } + else { + // j overlaps end of i + if ( i_offset < j_end ) + return false; // i overlaps end of j + } + } + } + return true; +} + +// +// A fat file may contain multiple sub-images for the same cpu-type, +// each optimized for a different cpu-sub-type (e.g G3 or G5). +// This routine picks the optimal sub-image. +// +static bool fatFindBest(const fat_header* fh, uint64_t* offset, uint64_t* len) +{ + if ( !fatValidate(fh) ) + return false; + +#if CPU_SUBTYPES_SUPPORTED + // assume all dylibs loaded must have same cpu type as main executable + const cpu_type_t cpu = sMainExecutableMachHeader->cputype; + + // We only know the subtype to use if the main executable cpu type matches the host + if ( cpu == sHostCPU ) { + // get preference ordered list of subtypes + const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(cpu, sHostCPUsubtype); + + // use ordered list to find best sub-image in fat file + if ( subTypePreferenceList != NULL ) { + if ( fatFindBestFromOrderedList(cpu, subTypePreferenceList, fh, offset, len) ) + return true; + } + + // if running cpu is not in list, try for an exact match + if ( fatFindExactMatch(cpu, sHostCPUsubtype, fh, offset, len) ) + return true; + } + + // running on an uknown cpu, can only load generic code + return fatFindRunsOnAllCPUs(cpu, fh, offset, len); +#else + // just find first slice with matching architecture + const fat_arch* archs = (fat_arch*)(((char*)fh)+sizeof(fat_header)); + for(uint32_t i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { + if ( (cpu_type_t)OSSwapBigToHostInt32(archs[i].cputype) == sMainExecutableMachHeader->cputype) { + *offset = OSSwapBigToHostInt32(archs[i].offset); + *len = OSSwapBigToHostInt32(archs[i].size); + return true; + } + } + return false; +#endif +} + + + +// +// This is used to validate if a non-fat (aka thin or raw) mach-o file can be used +// on the current processor. // +bool isCompatibleMachO(const uint8_t* firstPage, const char* path) +{ +#if CPU_SUBTYPES_SUPPORTED + // It is deemed compatible if any of the following are true: + // 1) mach_header subtype is in list of compatible subtypes for running processor + // 2) mach_header subtype is same as running processor subtype + // 3) mach_header subtype runs on all processor variants + const mach_header* mh = (mach_header*)firstPage; + if ( mh->magic == sMainExecutableMachHeader->magic ) { + if ( mh->cputype == sMainExecutableMachHeader->cputype ) { + if ( mh->cputype == sHostCPU ) { + // get preference ordered list of subtypes that this machine can use + const cpu_subtype_t* subTypePreferenceList = findCPUSubtypeList(mh->cputype, sHostCPUsubtype); + if ( subTypePreferenceList != NULL ) { + // if image's subtype is in the list, it is compatible + for (const cpu_subtype_t* p = subTypePreferenceList; *p != CPU_SUBTYPE_END_OF_LIST; ++p) { + if ( *p == mh->cpusubtype ) + return true; + } + // have list and not in list, so not compatible + throwf("incompatible cpu-subtype: 0x%08X in %s", mh->cpusubtype, path); + } + // unknown cpu sub-type, but if exact match for current subtype then ok to use + if ( mh->cpusubtype == sHostCPUsubtype ) + return true; + } + + // cpu type has no ordered list of subtypes + switch (mh->cputype) { + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + // subtypes are not used or these architectures + return true; + } + } + } +#else + // For architectures that don't support cpu-sub-types + // this just check the cpu type. + const mach_header* mh = (mach_header*)firstPage; + if ( mh->magic == sMainExecutableMachHeader->magic ) { + if ( mh->cputype == sMainExecutableMachHeader->cputype ) { + return true; + } + } +#endif + return false; +} + + + + +// The kernel maps in main executable before dyld gets control. We need to +// make an ImageLoader* for the already mapped in main executable. +static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path) +{ + // try mach-o loader + if ( isCompatibleMachO((const uint8_t*)mh, path) ) { + ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext); + addImage(image); + return (ImageLoaderMachO*)image; + } + + throw "main executable not a known format"; +} + +#if SUPPORT_ACCELERATE_TABLES +static bool dylibsCanOverrideCache() +{ + if ( !dyld3::loader::internalInstall() ) + return false; + return ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeDevelopment) ); +} +#endif + +const void* imMemorySharedCacheHeader() +{ + return sSharedCacheLoadInfo.loadAddress; +} + + +const char* getStandardSharedCacheFilePath() +{ + if ( sSharedCacheLoadInfo.loadAddress != nullptr ) + return sSharedCacheLoadInfo.path; + else + return nullptr; +} + + +static bool findInSharedCacheImage(const char* path, bool searchByPath, const struct stat* stat_buf, const macho_header** mh, const char** pathInCache, long* slide) +{ + dyld3::SharedCacheFindDylibResults results; + if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &results) ) { + *mh = (macho_header*)results.mhInCache; + *pathInCache = results.pathInCache; + *slide = results.slideInCache; + return true; + } + return false; +} + +bool inSharedCache(const char* path) +{ + return dyld3::pathIsInSharedCacheImage(sSharedCacheLoadInfo, path); +} + + +static ImageLoader* checkandAddImage(ImageLoader* image, const LoadContext& context) +{ + // now sanity check that this loaded image does not have the same install path as any existing image + const char* loadedImageInstallPath = image->getInstallPath(); + if ( image->isDylib() && (loadedImageInstallPath != NULL) && (loadedImageInstallPath[0] == '/') ) { + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* anImage = *it; + const char* installPath = anImage->getInstallPath(); + if ( installPath != NULL) { + if ( strcmp(loadedImageInstallPath, installPath) == 0 ) { + //dyld::log("duplicate(%s) => %p\n", installPath, anImage); + removeImage(image); + ImageLoader::deleteImage(image); + return anImage; + } + } + } + } + + // some API's restrict what they can load + if ( context.mustBeBundle && !image->isBundle() ) + throw "not a bundle"; + if ( context.mustBeDylib && !image->isDylib() ) + throw "not a dylib"; + + // regular main executables cannot be loaded + if ( image->isExecutable() ) { + if ( !context.canBePIE || !image->isPositionIndependentExecutable() ) + throw "can't load a main executable"; + } + + // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list + if ( ! image->isBundle() ) + addImage(image); + + return image; +} + +#if TARGET_IPHONE_SIMULATOR +static bool isSimulatorBinary(const uint8_t* firstPages, const char* path) +{ + const macho_header* mh = (macho_header*)firstPages; + const uint32_t cmd_count = mh->ncmds; + const load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const load_command* const cmdsEnd = (load_command*)((char*)cmds + mh->sizeofcmds); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + #if TARGET_OS_WATCH + case LC_VERSION_MIN_WATCHOS: + return true; + #elif TARGET_OS_TV + case LC_VERSION_MIN_TVOS: + return true; + #elif TARGET_OS_IOS + case LC_VERSION_MIN_IPHONEOS: + return true; + #endif + case LC_VERSION_MIN_MACOSX: + // grandfather in a few libSystem dylibs + if ((strcmp(path, "/usr/lib/system/libsystem_kernel.dylib") == 0) || + (strcmp(path, "/usr/lib/system/libsystem_platform.dylib") == 0) || + (strcmp(path, "/usr/lib/system/libsystem_pthread.dylib") == 0)) + return true; + return false; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + if ( cmd > cmdsEnd ) + return false; + } + return false; +} +#endif + +// map in file and instantiate an ImageLoader +static ImageLoader* loadPhase6(int fd, const struct stat& stat_buf, const char* path, const LoadContext& context) +{ + //dyld::log("%s(%s)\n", __func__ , path); + uint64_t fileOffset = 0; + uint64_t fileLength = stat_buf.st_size; + + // validate it is a file (not directory) + if ( (stat_buf.st_mode & S_IFMT) != S_IFREG ) + throw "not a file"; + + uint8_t firstPages[MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE]; + bool shortPage = false; + + // min mach-o file is 4K + if ( fileLength < 4096 ) { + if ( pread(fd, firstPages, (size_t)fileLength, 0) != (ssize_t)fileLength ) + throwf("pread of short file failed: %d", errno); + shortPage = true; + } + else { + // optimistically read only first 4KB + if ( pread(fd, firstPages, 4096, 0) != 4096 ) + throwf("pread of first 4K failed: %d", errno); + } + + // if fat wrapper, find usable sub-file + const fat_header* fileStartAsFat = (fat_header*)firstPages; + if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + if ( OSSwapBigToHostInt32(fileStartAsFat->nfat_arch) > ((4096 - sizeof(fat_header)) / sizeof(fat_arch)) ) + throwf("fat header too large: %u entries", OSSwapBigToHostInt32(fileStartAsFat->nfat_arch)); + if ( fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) { + if ( (fileOffset+fileLength) > (uint64_t)(stat_buf.st_size) ) + throwf("truncated fat file. file length=%llu, but needed slice goes to %llu", stat_buf.st_size, fileOffset+fileLength); + if (pread(fd, firstPages, 4096, fileOffset) != 4096) + throwf("pread of fat file failed: %d", errno); + } + else { + throw "no matching architecture in universal wrapper"; + } + } + + // try mach-o loader + if ( shortPage ) + throw "file too short"; + if ( isCompatibleMachO(firstPages, path) ) { + + // only MH_BUNDLE, MH_DYLIB, and some MH_EXECUTE can be dynamically loaded + const mach_header* mh = (mach_header*)firstPages; + switch ( mh->filetype ) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_BUNDLE: + break; + default: + throw "mach-o, but wrong filetype"; + } + + uint32_t headerAndLoadCommandsSize = sizeof(macho_header) + mh->sizeofcmds; + if ( headerAndLoadCommandsSize > MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE ) + throwf("malformed mach-o: load commands size (%u) > %u", headerAndLoadCommandsSize, MAX_MACH_O_HEADER_AND_LOAD_COMMANDS_SIZE); + + if ( headerAndLoadCommandsSize > fileLength ) + dyld::throwf("malformed mach-o: load commands size (%u) > mach-o file size (%llu)", headerAndLoadCommandsSize, fileLength); + + if ( headerAndLoadCommandsSize > 4096 ) { + // read more pages + unsigned readAmount = headerAndLoadCommandsSize - 4096; + if ( pread(fd, &firstPages[4096], readAmount, fileOffset+4096) != readAmount ) + throwf("pread of extra load commands past 4KB failed: %d", errno); + } + +#if TARGET_IPHONE_SIMULATOR + // dyld_sim should restrict loading osx binaries + if ( !isSimulatorBinary(firstPages, path) ) { + #if TARGET_OS_WATCH + throw "mach-o, but not built for watchOS simulator"; + #elif TARGET_OS_TV + throw "mach-o, but not built for tvOS simulator"; + #else + throw "mach-o, but not built for iOS simulator"; + #endif + } +#endif + + // instantiate an image + ImageLoader* image = ImageLoaderMachO::instantiateFromFile(path, fd, firstPages, headerAndLoadCommandsSize, fileOffset, fileLength, stat_buf, gLinkContext); + + // validate + return checkandAddImage(image, context); + } + + // try other file formats here... + + + // throw error about what was found + switch (*(uint32_t*)firstPages) { + case MH_MAGIC: + case MH_CIGAM: + case MH_MAGIC_64: + case MH_CIGAM_64: + throw "mach-o, but wrong architecture"; + default: + throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", + firstPages[0], firstPages[1], firstPages[2], firstPages[3], firstPages[4], firstPages[5], firstPages[6],firstPages[7]); + } +} + + +static ImageLoader* loadPhase5open(const char* path, const LoadContext& context, const struct stat& stat_buf, std::vector* 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; + if ( (err == EPERM) && sandboxBlockedOpen(path) ) + newMsg = dyld::mkstringf("file system sandbox blocked open() of '%s'", path); + else + 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; + } +} + + + +// try to open file +static ImageLoader* loadPhase5load(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + if ( sAllCacheImagesProxy->hasDylib(path, &cacheIndex) ) + return sAllCacheImagesProxy; + } +#endif +#if TARGET_IPHONE_SIMULATOR + // in simulators, 'path' has DYLD_ROOT_PATH prepended, but cache index does not have the prefix, so use orgPath + const char* pathToFindInCache = orgPath; +#else + const char* pathToFindInCache = path; +#endif + uint statErrNo; + struct stat statBuf; + bool didStat = false; + bool existsOnDisk; + dyld3::SharedCacheFindDylibResults shareCacheResults; + if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, pathToFindInCache, &shareCacheResults) ) { + // see if this image in the cache was already loaded via a different path + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); ++it) { + ImageLoader* anImage = *it; + if ( (const mach_header*)anImage->machHeader() == shareCacheResults.mhInCache ) + return anImage; + } + // if RTLD_NOLOAD, do nothing if not already loaded + if ( context.dontLoad ) + return NULL; + bool useCache = false; + if ( shareCacheResults.imageData == nullptr ) { + // HACK to support old caches + existsOnDisk = ( my_stat(path, &statBuf) == 0 ); + didStat = true; + statErrNo = errno; + useCache = !existsOnDisk; + } + else { + // zero out stat buffer so mtime, etc are zero for items from the shared cache + bzero(&statBuf, sizeof(statBuf)); + dyld3::launch_cache::Image image(shareCacheResults.imageData); + if ( image.overridableDylib() ) { + existsOnDisk = ( my_stat(path, &statBuf) == 0 ); + didStat = true; + statErrNo = errno; + if ( sSharedCacheLoadInfo.loadAddress->header.dylibsExpectedOnDisk ) { + if ( (image.fileModTime() == statBuf.st_mtime) && (image.fileINode() == statBuf.st_ino) ) + useCache = true; + } + else { + if ( !existsOnDisk ) + useCache = true; + } + } + else { + useCache = true; + } + } + if ( useCache ) { + ImageLoader* imageLoader = ImageLoaderMachO::instantiateFromCache((macho_header*)shareCacheResults.mhInCache, shareCacheResults.pathInCache, shareCacheResults.slideInCache, statBuf, gLinkContext); + return checkandAddImage(imageLoader, context); + } + } + + // not in cache or cache not usable + if ( !didStat ) { + existsOnDisk = ( my_stat(path, &statBuf) == 0 ); + statErrNo = errno; + } + if ( existsOnDisk ) { + // in case image was renamed or found via symlinks, check for inode match + ImageLoader* imageLoader = findLoadedImage(statBuf); + if ( imageLoader != NULL ) + return imageLoader; + // do nothing if not already loaded and if RTLD_NOLOAD + if ( context.dontLoad ) + return NULL; + // try opening file + imageLoader = loadPhase5open(path, context, statBuf, exceptions); + if ( imageLoader != NULL ) + return imageLoader; + } + + // just return NULL if file not found, but record any other errors + if ( (statErrNo != ENOENT) && (statErrNo != 0) ) { + if ( (statErrNo == EPERM) && sandboxBlockedStat(path) ) + exceptions->push_back(dyld::mkstringf("%s: file system sandbox blocked stat()", path)); + else + exceptions->push_back(dyld::mkstringf("%s: stat() failed with errno=%d", path, statErrNo)); + } + return NULL; +} + +// look for path match with existing loaded images +static ImageLoader* loadPhase5check(const char* path, const char* orgPath, const LoadContext& context) +{ + //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); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + 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 ( 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) { + if ( strcmp(path, installPath) == 0 ) { + // if we are looking for a dylib don't return something else + if ( !context.mustBeDylib || anImage->isDylib() ) + return anImage; + } + } + } + // 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); + return NULL; +} + + +// open or check existing +static ImageLoader* loadPhase5(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + + // check for specific dylib overrides + for (std::vector::iterator it = sDylibOverrides.begin(); it != sDylibOverrides.end(); ++it) { + if ( strcmp(it->installName, path) == 0 ) { + path = it->override; + break; + } + } + + if ( exceptions != NULL ) + return loadPhase5load(path, orgPath, context, cacheIndex, exceptions); + else + return loadPhase5check(path, orgPath, context); +} + +// try with and without image suffix +static ImageLoader* loadPhase4(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* 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, orgPath, context, cacheIndex, exceptions); + } + if ( image == NULL ) + image = loadPhase5(path, orgPath, context, cacheIndex, exceptions); + return image; +} + +static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, + const char* const frameworkPaths[], const char* const libraryPaths[], + unsigned& cacheIndex, std::vector* exceptions); // forward reference + + +// expand @ variables +static ImageLoader* loadPhase3(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + ImageLoader* image = NULL; + if ( strncmp(path, "@executable_path/", 17) == 0 ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // executable_path cannot be in used in any binary in a setuid process rdar://problem/4589305 + if ( gLinkContext.processIsRestricted ) + throwf("unsafe use of @executable_path in %s with restricted binary", context.origin); +#endif + // handle @executable_path path prefix + const char* executablePath = sExecPath; + char newPath[strlen(executablePath) + strlen(path)]; + strcpy(newPath, executablePath); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newPath, &path[17]); + image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + + // perhaps main executable path is a sym link, find realpath and retry + char resolvedPath[PATH_MAX]; + if ( realpath(sExecPath, resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[17]); + else + strcpy(newRealPath, &path[17]); + image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + else if ( (strncmp(path, "@loader_path/", 13) == 0) && (context.origin != NULL) ) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // @loader_path cannot be used from the main executable of a setuid process rdar://problem/4589305 + if ( gLinkContext.processIsRestricted && (strcmp(context.origin, sExecPath) == 0) ) + throwf("unsafe use of @loader_path in %s with restricted binary", context.origin); +#endif + // handle @loader_path path prefix + char newPath[strlen(context.origin) + strlen(path)]; + strcpy(newPath, context.origin); + char* addPoint = strrchr(newPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[13]); + else + strcpy(newPath, &path[13]); + image = loadPhase4(newPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + + // perhaps loader path is a sym link, find realpath and retry + char resolvedPath[PATH_MAX]; + if ( realpath(context.origin, resolvedPath) != NULL ) { + char newRealPath[strlen(resolvedPath) + strlen(path)]; + strcpy(newRealPath, resolvedPath); + addPoint = strrchr(newRealPath,'/'); + if ( addPoint != NULL ) + strcpy(&addPoint[1], &path[13]); + else + strcpy(newRealPath, &path[13]); + image = loadPhase4(newRealPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + else if ( context.implicitRPath || (strncmp(path, "@rpath/", 7) == 0) ) { + const char* trailingPath = (strncmp(path, "@rpath/", 7) == 0) ? &path[7] : path; + // substitute @rpath with all -rpath paths up the load chain + for(const ImageLoader::RPathChain* rp=context.rpath; rp != NULL; rp=rp->next) { + if (rp->paths != NULL ) { + for(std::vector::iterator it=rp->paths->begin(); it != rp->paths->end(); ++it) { + const char* anRPath = *it; + char newPath[strlen(anRPath) + strlen(trailingPath)+2]; + strcpy(newPath, anRPath); + if ( newPath[strlen(newPath)-1] != '/' ) + strcat(newPath, "/"); + strcat(newPath, trailingPath); + image = loadPhase4(newPath, orgPath, context, cacheIndex, 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, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + + // if this is the "open" pass, don't try to open @rpath/... as a relative path + if ( (exceptions != NULL) && (trailingPath != path) ) + return NULL; + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + else if ( gLinkContext.processIsRestricted && (path[0] != '/' ) ) { + throwf("unsafe use of relative rpath %s in %s with restricted binary", path, context.origin); + } +#endif + + return loadPhase4(path, orgPath, context, cacheIndex, exceptions); +} + +static ImageLoader* loadPhase2cache(const char* path, const char *orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) { + ImageLoader* image = NULL; +#if !TARGET_IPHONE_SIMULATOR + if ( exceptions != NULL) { + char resolvedPath[PATH_MAX]; + realpath(path, resolvedPath); + int myerr = errno; + // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT + if ( (myerr == ENOENT) || (myerr == 0) ) + { + image = loadPhase4(resolvedPath, orgPath, context, cacheIndex, exceptions); + } + } +#endif + return image; +} + + +// try search paths +static ImageLoader* loadPhase2(const char* path, const char* orgPath, const LoadContext& context, + const char* const frameworkPaths[], const char* const libraryPaths[], + unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + ImageLoader* image = NULL; + const char* frameworkPartialPath = getFrameworkPartialPath(path); + if ( frameworkPaths != NULL ) { + if ( frameworkPartialPath != NULL ) { + const size_t frameworkPartialPathLen = strlen(frameworkPartialPath); + for(const char* const* fp = frameworkPaths; *fp != NULL; ++fp) { + char npath[strlen(*fp)+frameworkPartialPathLen+8]; + strcpy(npath, *fp); + strcat(npath, "/"); + strcat(npath, frameworkPartialPath); + //dyld::log("dyld: fallback framework path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, npath); + image = loadPhase4(npath, orgPath, context, cacheIndex, exceptions); + // Look in the cache if appropriate + if ( image == NULL) + image = loadPhase2cache(npath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + } + // An executable with the same name as a framework & DYLD_LIBRARY_PATH pointing to it gets loaded twice + // Some apps depend on frameworks being found via library paths + if ( (libraryPaths != NULL) && ((frameworkPartialPath == NULL) || sFrameworksFoundAsDylibs) ) { + const char* libraryLeafName = getLibraryLeafName(path); + const size_t libraryLeafNameLen = strlen(libraryLeafName); + for(const char* const* lp = libraryPaths; *lp != NULL; ++lp) { + char libpath[strlen(*lp)+libraryLeafNameLen+8]; + strcpy(libpath, *lp); + strcat(libpath, "/"); + strcat(libpath, libraryLeafName); + //dyld::log("dyld: fallback library path used: %s() -> loadPhase4(\"%s\", ...)\n", __func__, libpath); + image = loadPhase4(libpath, orgPath, context, cacheIndex, exceptions); + // Look in the cache if appropriate + if ( image == NULL) + image = loadPhase2cache(libpath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } + return NULL; +} + +// try search overrides and fallbacks +static ImageLoader* loadPhase1(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* 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, orgPath, context, NULL, sEnv.LD_LIBRARY_PATH, cacheIndex,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, orgPath, context, sEnv.DYLD_FRAMEWORK_PATH, sEnv.DYLD_LIBRARY_PATH, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + + // try raw path + image = loadPhase3(path, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + + // try fallback paths during second time (will open file) + const char* const* fallbackLibraryPaths = sEnv.DYLD_FALLBACK_LIBRARY_PATH; + if ( (fallbackLibraryPaths != NULL) && !context.useFallbackPaths ) + fallbackLibraryPaths = NULL; + if ( !context.dontLoad && (exceptions != NULL) && ((sEnv.DYLD_FALLBACK_FRAMEWORK_PATH != NULL) || (fallbackLibraryPaths != NULL)) ) { + image = loadPhase2(path, orgPath, context, sEnv.DYLD_FALLBACK_FRAMEWORK_PATH, fallbackLibraryPaths, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + + return NULL; +} + +// try root substitutions +static ImageLoader* loadPhase0(const char* path, const char* orgPath, const LoadContext& context, unsigned& cacheIndex, std::vector* exceptions) +{ + //dyld::log("%s(%s, %p)\n", __func__ , path, exceptions); + +#if SUPPORT_ROOT_PATH + // handle DYLD_ROOT_PATH which forces absolute paths to use a new root + if ( (gLinkContext.rootPaths != NULL) && (path[0] == '/') ) { + for(const char* const* rootPath = gLinkContext.rootPaths ; *rootPath != NULL; ++rootPath) { + char newPath[strlen(*rootPath) + strlen(path)+2]; + strcpy(newPath, *rootPath); + strcat(newPath, path); + ImageLoader* image = loadPhase1(newPath, orgPath, context, cacheIndex, exceptions); + if ( image != NULL ) + return image; + } + } +#endif + + // try raw path + return loadPhase1(path, orgPath, context, cacheIndex, exceptions); +} + +static bool cacheablePath(const char* path) { + if (strncmp(path, "/usr/lib/", 9) == 0) + return true; + if (strncmp(path, "/System/Library/", 16) == 0) + return true; + return false; +} + +// +// Given all the DYLD_ environment variables, the general case for loading libraries +// is that any given path expands into a list of possible locations to load. We +// also must take care to ensure two copies of the "same" library are never loaded. +// +// The algorithm used here is that there is a separate function for each "phase" of the +// path expansion. Each phase function calls the next phase with each possible expansion +// of that phase. The result is the last phase is called with all possible paths. +// +// To catch duplicates the algorithm is run twice. The first time, the last phase checks +// the path against all loaded images. The second time, the last phase calls open() on +// the path. Either time, if an image is found, the phases all unwind without checking +// for other paths. +// +ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheIndex) +{ + CRSetCrashLogMessage2(path); + const char* orgPath = path; + cacheIndex = UINT32_MAX; + + //dyld::log("%s(%s)\n", __func__ , path); + char realPath[PATH_MAX]; + // when DYLD_IMAGE_SUFFIX is in used, do a realpath(), otherwise a load of "Foo.framework/Foo" will not match + if ( context.useSearchPaths && ( gLinkContext.imageSuffix != NULL) ) { + if ( realpath(path, realPath) != NULL ) + path = realPath; + } + + // try all path permutations and check against existing loaded images + + ImageLoader* image = loadPhase0(path, orgPath, context, cacheIndex, NULL); + if ( image != NULL ) { + CRSetCrashLogMessage2(NULL); + return image; + } + + // try all path permutations and try open() until first success + std::vector exceptions; + image = loadPhase0(path, orgPath, context, cacheIndex, &exceptions); +#if !TARGET_IPHONE_SIMULATOR + // support symlinks on disk to a path in dyld shared cache + if ( image == NULL) + image = loadPhase2cache(path, orgPath, context, cacheIndex, &exceptions); +#endif + CRSetCrashLogMessage2(NULL); + if ( image != NULL ) { + // leak in dyld during dlopen when using DYLD_ variables + for (std::vector::iterator it = exceptions.begin(); it != exceptions.end(); ++it) { + free((void*)(*it)); + } + // if loaded image is not from cache, but original path is in cache + // set gSharedCacheOverridden flag to disable some ObjC optimizations + if ( !gSharedCacheOverridden && !image->inSharedCache() && image->isDylib() && cacheablePath(path) && inSharedCache(path) ) { + gSharedCacheOverridden = true; + } + return image; + } + else if ( exceptions.size() == 0 ) { + if ( context.dontLoad ) { + return NULL; + } + else + throw "image not found"; + } + else { + const char* msgStart = "no suitable image found. Did find:"; + const char* delim = "\n\t"; + size_t allsizes = strlen(msgStart)+8; + for (size_t i=0; i < exceptions.size(); ++i) + allsizes += (strlen(exceptions[i]) + strlen(delim)); + char* fullMsg = new char[allsizes]; + strcpy(fullMsg, msgStart); + for (size_t i=0; i < exceptions.size(); ++i) { + strcat(fullMsg, delim); + strcat(fullMsg, exceptions[i]); + free((void*)exceptions[i]); + } + throw (const char*)fullMsg; + } +} + + + + + +static void mapSharedCache() +{ + dyld3::SharedCacheOptions opts; + opts.cacheDirOverride = sSharedCacheOverrideDir; + opts.forcePrivate = (gLinkContext.sharedRegionMode == ImageLoader::kUsePrivateSharedRegion); +#if __x86_64__ && !TARGET_IPHONE_SIMULATOR + opts.useHaswell = sHaswell; +#else + opts.useHaswell = false; +#endif + opts.verbose = gLinkContext.verboseMapping; + loadDyldCache(opts, &sSharedCacheLoadInfo); + + // update global state + if ( sSharedCacheLoadInfo.loadAddress != nullptr ) { + dyld::gProcessInfo->processDetachedFromSharedRegion = opts.forcePrivate; + dyld::gProcessInfo->sharedCacheSlide = sSharedCacheLoadInfo.slide; + dyld::gProcessInfo->sharedCacheBaseAddress = (unsigned long)sSharedCacheLoadInfo.loadAddress; + sSharedCacheLoadInfo.loadAddress->getUUID(dyld::gProcessInfo->sharedCacheUUID); + dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_SHARED_CACHE_A, (const uuid_t *)&dyld::gProcessInfo->sharedCacheUUID[0], {0,0}, {{ 0, 0 }}, (const mach_header *)sSharedCacheLoadInfo.loadAddress); + } + +//#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR +// RAM disk booting does not have shared cache yet +// Don't make lack of a shared cache fatal in that case +// if ( sSharedCacheLoadInfo.loadAddress == nullptr ) { +// if ( sSharedCacheLoadInfo.errorMessage != nullptr ) +// halt(sSharedCacheLoadInfo.errorMessage); +// else +// halt("error loading dyld shared cache"); +// } +//#endif +} + + + +// create when NSLinkModule is called for a second time on a bundle +ImageLoader* cloneImage(ImageLoader* image) +{ + // open file (automagically closed when this function exits) + FileOpener file(image->getPath()); + + struct stat stat_buf; + if ( fstat(file.getFileDescriptor(), &stat_buf) == -1) + throw "stat error"; + + dyld::LoadContext context; + context.useSearchPaths = false; + context.useFallbackPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = true; + context.mustBeDylib = false; + context.canBePIE = false; + context.origin = NULL; + context.rpath = NULL; + return loadPhase6(file.getFileDescriptor(), stat_buf, image->getPath(), context); +} + + +ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName) +{ + // if fat wrapper, find usable sub-file + const fat_header* memStartAsFat = (fat_header*)mem; + uint64_t fileOffset = 0; + uint64_t fileLength = len; + if ( memStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + if ( fatFindBest(memStartAsFat, &fileOffset, &fileLength) ) { + mem = &mem[fileOffset]; + len = fileLength; + } + else { + throw "no matching architecture in universal wrapper"; + } + } + + // try each loader + if ( isCompatibleMachO(mem, moduleName) ) { + ImageLoader* image = ImageLoaderMachO::instantiateFromMemory(moduleName, (macho_header*)mem, len, gLinkContext); + // don't add bundles to global list, they can be loaded but not linked. When linked it will be added to list + if ( ! image->isBundle() ) + addImage(image); + return image; + } + + // try other file formats here... + + // throw error about what was found + switch (*(uint32_t*)mem) { + case MH_MAGIC: + case MH_CIGAM: + case MH_MAGIC_64: + case MH_CIGAM_64: + throw "mach-o, but wrong architecture"; + default: + throwf("unknown file type, first eight bytes: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X", + mem[0], mem[1], mem[2], mem[3], mem[4], mem[5], mem[6],mem[7]); + } +} + + +void registerAddCallback(ImageCallback func) +{ + // now add to list to get notified when any more images are added + sAddImageCallbacks.push_back(func); + + // call callback with all existing images + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( image->getState() >= dyld_image_state_bound && image->getState() < dyld_image_state_terminated ) + (*func)(image->machHeader(), image->getSlide()); + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + dyld_image_info infos[allImagesCount()+1]; + unsigned cacheCount = sAllCacheImagesProxy->appendImagesToNotify(dyld_image_state_bound, true, infos); + for (unsigned i=0; i < cacheCount; ++i) { + (*func)(infos[i].imageLoadAddress, sSharedCacheLoadInfo.slide); + } + } +#endif +} + +void registerRemoveCallback(ImageCallback func) +{ + // ignore calls to register a notification during a notification + if ( sRemoveImageCallbacksInUse ) + return; + sRemoveImageCallbacks.push_back(func); +} + +void clearErrorMessage() +{ + error_string[0] = '\0'; +} + +void setErrorMessage(const char* message) +{ + // save off error message in global buffer for CrashReporter to find + strlcpy(error_string, message, sizeof(error_string)); +} + +const char* getErrorMessage() +{ + return error_string; +} + +void halt(const char* message) +{ + dyld::log("dyld: %s\n", message); + setErrorMessage(message); + dyld::gProcessInfo->errorMessage = error_string; + if ( !gLinkContext.startedInitializingMainExecutable ) + dyld::gProcessInfo->terminationFlags = 1; + else + dyld::gProcessInfo->terminationFlags = 0; + + char payloadBuffer[EXIT_REASON_PAYLOAD_MAX_LEN]; + dyld_abort_payload* payload = (dyld_abort_payload*)payloadBuffer; + payload->version = 1; + payload->flags = gLinkContext.startedInitializingMainExecutable ? 0 : 1; + payload->targetDylibPathOffset = 0; + payload->clientPathOffset = 0; + payload->symbolOffset = 0; + int payloadSize = sizeof(dyld_abort_payload); + + if ( dyld::gProcessInfo->errorTargetDylibPath != NULL ) { + payload->targetDylibPathOffset = payloadSize; + payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorTargetDylibPath, sizeof(payloadBuffer)-payloadSize) + 1; + } + if ( dyld::gProcessInfo->errorClientOfDylibPath != NULL ) { + payload->clientPathOffset = payloadSize; + payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorClientOfDylibPath, sizeof(payloadBuffer)-payloadSize) + 1; + } + if ( dyld::gProcessInfo->errorSymbol != NULL ) { + payload->symbolOffset = payloadSize; + payloadSize += strlcpy(&payloadBuffer[payloadSize], dyld::gProcessInfo->errorSymbol, sizeof(payloadBuffer)-payloadSize) + 1; + } + char truncMessage[EXIT_REASON_USER_DESC_MAX_LEN]; + strlcpy(truncMessage, message, EXIT_REASON_USER_DESC_MAX_LEN); + abort_with_payload(OS_REASON_DYLD, dyld::gProcessInfo->errorKind ? dyld::gProcessInfo->errorKind : DYLD_EXIT_REASON_OTHER, payloadBuffer, payloadSize, truncMessage, 0); +} + +static void setErrorStrings(unsigned errorCode, const char* errorClientOfDylibPath, + const char* errorTargetDylibPath, const char* errorSymbol) +{ + dyld::gProcessInfo->errorKind = errorCode; + dyld::gProcessInfo->errorClientOfDylibPath = errorClientOfDylibPath; + dyld::gProcessInfo->errorTargetDylibPath = errorTargetDylibPath; + dyld::gProcessInfo->errorSymbol = errorSymbol; +} + + +uintptr_t bindLazySymbol(const mach_header* mh, uintptr_t* lazyPointer) +{ + uintptr_t result = 0; + // acquire read-lock on dyld's data structures +#if 0 // rdar://problem/3811777 turn off locking until deadlock is resolved + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->lockForReading)(); +#endif + // lookup and bind lazy pointer and get target address + try { + ImageLoader* target; + #if __i386__ + // fast stubs pass NULL for mh and image is instead found via the location of stub (aka lazyPointer) + if ( mh == NULL ) + target = dyld::findImageContainingAddress(lazyPointer); + else + target = dyld::findImageByMachHeader(mh); + #else + // note, target should always be mach-o, because only mach-o lazy handler wired up to this + target = dyld::findImageByMachHeader(mh); + #endif + if ( target == NULL ) + throwf("image not found for lazy pointer at %p", lazyPointer); + result = target->doBindLazySymbol(lazyPointer, gLinkContext); + } + catch (const char* message) { + dyld::log("dyld: lazy symbol binding failed: %s\n", message); + halt(message); + } + // release read-lock on dyld's data structures +#if 0 + if ( gLibSystemHelpers != NULL ) + (*gLibSystemHelpers->unlockForReading)(); +#endif + // return target address to glue which jumps to it with real parameters restored + return result; +} + + +uintptr_t fastBindLazySymbol(ImageLoader** imageLoaderCache, uintptr_t lazyBindingInfoOffset) +{ + uintptr_t result = 0; + // get image + if ( *imageLoaderCache == NULL ) { + // save in cache + *imageLoaderCache = dyld::findMappedRange((uintptr_t)imageLoaderCache); + if ( *imageLoaderCache == NULL ) { +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + const mach_header* mh; + const char* path; + unsigned index; + if ( sAllCacheImagesProxy->addressInCache(imageLoaderCache, &mh, &path, &index) ) { + result = sAllCacheImagesProxy->bindLazy(lazyBindingInfoOffset, gLinkContext, mh, index); + if ( result == 0 ) { + halt("dyld: lazy symbol binding failed for image in dyld shared\n"); + } + return result; + } + } +#endif + const char* message = "fast lazy binding from unknown image"; + dyld::log("dyld: %s\n", message); + halt(message); + } + } + + // bind lazy pointer and return it + try { + result = (*imageLoaderCache)->doBindFastLazySymbol((uint32_t)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); + halt(message); + } + + // return target address to glue which jumps to it with real parameters restored + return result; +} + + + +void registerUndefinedHandler(UndefinedHandler handler) +{ + sUndefinedHandler = handler; +} + +static void undefinedHandler(const char* symboName) +{ + if ( sUndefinedHandler != NULL ) { + (*sUndefinedHandler)(symboName); + } +} + +static bool findExportedSymbol(const char* name, bool onlyInCoalesced, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + // search all images in order + const ImageLoader* firstWeakImage = NULL; + const ImageLoader::Symbol* firstWeakSym = NULL; + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i) { + ImageLoader* anImage = sAllImages[i]; + // the use of inserted libraries alters search order + // so that inserted libraries are found before the main executable + if ( sInsertedDylibCount > 0 ) { + if ( i < sInsertedDylibCount ) + anImage = sAllImages[i+1]; + else if ( i == sInsertedDylibCount ) + anImage = sAllImages[0]; + } + if ( ! anImage->hasHiddenExports() && (!onlyInCoalesced || anImage->hasCoalescedExports()) ) { + *sym = anImage->findExportedSymbol(name, false, image); + if ( *sym != NULL ) { + // if weak definition found, record first one found + if ( ((*image)->getExportedSymbolInfo(*sym) & ImageLoader::kWeakDefinition) != 0 ) { + if ( firstWeakImage == NULL ) { + firstWeakImage = *image; + firstWeakSym = *sym; + } + } + else { + // found non-weak, so immediately return with it + return true; + } + } + } + } + if ( firstWeakSym != NULL ) { + // found a weak definition, but no non-weak, so return first weak found + *sym = firstWeakSym; + *image = firstWeakImage; + return true; + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + if ( sAllCacheImagesProxy->flatFindSymbol(name, onlyInCoalesced, sym, image) ) + return true; + } +#endif + + return false; +} + +bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + return findExportedSymbol(name, false, sym, image); +} + +bool findCoalescedExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + return findExportedSymbol(name, true, sym, image); +} + + +bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image) +{ + // search all images in order + const size_t imageCount = sAllImages.size(); + for(size_t i=0; i < imageCount; ++i){ + ImageLoader* anImage = sAllImages[i]; + // only look at images whose paths contain the hint string (NULL hint string is wildcard) + if ( ! anImage->isBundle() && ((librarySubstring==NULL) || (strstr(anImage->getPath(), librarySubstring) != NULL)) ) { + *sym = anImage->findExportedSymbol(name, false, image); + if ( *sym != NULL ) { + return true; + } + } + } + return false; +} + + +unsigned int getCoalescedImages(ImageLoader* images[], unsigned imageIndex[]) +{ + unsigned int count = 0; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( image->participatesInCoalescing() ) { + images[count] = *it; + imageIndex[count] = 0; + ++count; + } + } +#if SUPPORT_ACCELERATE_TABLES + if ( sAllCacheImagesProxy != NULL ) { + sAllCacheImagesProxy->appendImagesNeedingCoalescing(images, imageIndex, count); + } +#endif + return count; +} + + +static ImageLoader::MappedRegion* getMappedRegions(ImageLoader::MappedRegion* regions) +{ + ImageLoader::MappedRegion* end = regions; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + (*it)->getMappedRegions(end); + } + return end; +} + +void registerImageStateSingleChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) +{ + // mark the image that the handler is in as never-unload because dyld has a reference into it + ImageLoader* handlerImage = findImageContainingAddress((void*)handler); + if ( handlerImage != NULL ) + handlerImage->setNeverUnload(); + + // add to list of handlers + std::vector* handlers = stateToHandlers(state, sSingleHandlers); + if ( handlers != NULL ) { + // need updateAllImages() to be last in dyld_image_state_mapped list + // so that if ObjC adds a handler that prevents a load, it happens before the gdb list is updated + if ( state == dyld_image_state_mapped ) + handlers->insert(handlers->begin(), handler); + else + handlers->push_back(handler); + + // call callback with all existing images + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + dyld_image_info info; + info.imageLoadAddress = image->machHeader(); + info.imageFilePath = image->getRealPath(); + info.imageFileModDate = image->lastModified(); + // should only call handler if state == image->state + if ( image->getState() == state ) + (*handler)(state, 1, &info); + // ignore returned string, too late to do anything + } + } +} + +void registerImageStateBatchChangeHandler(dyld_image_states state, dyld_image_state_change_handler handler) +{ + // mark the image that the handler is in as never-unload because dyld has a reference into it + ImageLoader* handlerImage = findImageContainingAddress((void*)handler); + if ( handlerImage != NULL ) + handlerImage->setNeverUnload(); + + // add to list of handlers + std::vector* handlers = stateToHandlers(state, sBatchHandlers); + if ( handlers != NULL ) { + // insert at front, so that gdb handler is always last + handlers->insert(handlers->begin(), handler); + + // call callback with all existing images + try { + notifyBatchPartial(state, true, handler, false, false); + } + catch (const char* msg) { + // ignore request to abort during registration + } + } +} + + +void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped) +{ + // record functions to call + sNotifyObjCMapped = mapped; + sNotifyObjCInit = init; + sNotifyObjCUnmapped = unmapped; + + // call 'mapped' function with all images mapped so far + try { + notifyBatchPartial(dyld_image_state_bound, true, NULL, false, true); + } + catch (const char* msg) { + // ignore request to abort during registration + } + + // call 'init' function on all images already init'ed (below libSystem) + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) { + (*sNotifyObjCInit)(image->getRealPath(), image->machHeader()); + } + } +} + +bool sharedCacheUUID(uuid_t uuid) +{ + if ( sSharedCacheLoadInfo.loadAddress == nullptr ) + return false; + + sSharedCacheLoadInfo.loadAddress->getUUID(uuid); + return true; +} + +#if SUPPORT_ACCELERATE_TABLES + +bool dlopenFromCache(const char* path, int mode, void** handle) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + char fallbackPath[PATH_MAX]; + bool result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, path, mode, handle); + if ( !result && (strchr(path, '/') == NULL) ) { + // POSIX says you can call dlopen() with a leaf name (e.g. dlopen("libz.dylb")) + strcpy(fallbackPath, "/usr/lib/"); + strlcat(fallbackPath, path, PATH_MAX); + result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, fallbackPath, mode, handle); + if ( !result ) + path = fallbackPath; + } + if ( !result ) { + // leaf name could be a symlink + char resolvedPath[PATH_MAX]; + realpath(path, resolvedPath); + int realpathErrno = errno; + // If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT + if ( (realpathErrno == ENOENT) || (realpathErrno == 0) ) { + result = sAllCacheImagesProxy->dlopenFromCache(gLinkContext, resolvedPath, mode, handle); + } + } + + return result; +} + +bool makeCacheHandle(ImageLoader* image, unsigned cacheIndex, int mode, void** result) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->makeCacheHandle(gLinkContext, cacheIndex, mode, result); +} + +bool isCacheHandle(void* handle) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->isCacheHandle(handle, NULL, NULL); +} + +bool isPathInCache(const char* path) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + unsigned index; + return sAllCacheImagesProxy->hasDylib(path, &index); +} + +const char* getPathFromIndex(unsigned cacheIndex) +{ + if ( sAllCacheImagesProxy == NULL ) + return NULL; + return sAllCacheImagesProxy->getIndexedPath(cacheIndex); +} + +void* dlsymFromCache(void* handle, const char* symName, unsigned index) +{ + if ( sAllCacheImagesProxy == NULL ) + return NULL; + return sAllCacheImagesProxy->dlsymFromCache(gLinkContext, handle, symName, index); +} + +bool addressInCache(const void* address, const mach_header** mh, const char** path, unsigned* index) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + unsigned ignore; + return sAllCacheImagesProxy->addressInCache(address, mh, path, index ? index : &ignore); +} + +bool findUnwindSections(const void* addr, dyld_unwind_sections* info) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->findUnwindSections(addr, info); +} + +bool dladdrFromCache(const void* address, Dl_info* info) +{ + if ( sAllCacheImagesProxy == NULL ) + return false; + return sAllCacheImagesProxy->dladdrFromCache(address, info); +} +#endif + +static ImageLoader* libraryLocator(const char* libraryName, bool search, const char* origin, const ImageLoader::RPathChain* rpaths, unsigned& cacheIndex) +{ + dyld::LoadContext context; + context.useSearchPaths = search; + context.useFallbackPaths = search; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = true; + context.canBePIE = false; + context.origin = origin; + context.rpath = rpaths; + return load(libraryName, context, cacheIndex); +} + +static const char* basename(const char* path) +{ + const char* last = path; + for (const char* s = path; *s != '\0'; s++) { + if (*s == '/') + last = s+1; + } + return last; +} + +static void setContext(const macho_header* mainExecutableMH, int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + gLinkContext.loadLibrary = &libraryLocator; + gLinkContext.terminationRecorder = &terminationRecorder; + gLinkContext.flatExportFinder = &flatFindExportedSymbol; + gLinkContext.coalescedExportFinder = &findCoalescedExportedSymbol; + gLinkContext.getCoalescedImages = &getCoalescedImages; + gLinkContext.undefinedHandler = &undefinedHandler; + gLinkContext.getAllMappedRegions = &getMappedRegions; + gLinkContext.bindingHandler = NULL; + gLinkContext.notifySingle = ¬ifySingle; + gLinkContext.notifyBatch = ¬ifyBatch; + gLinkContext.removeImage = &removeImage; + gLinkContext.registerDOFs = ®isterDOFs; + gLinkContext.clearAllDepths = &clearAllDepths; + gLinkContext.printAllDepths = &printAllDepths; + gLinkContext.imageCount = &imageCount; + gLinkContext.setNewProgramVars = &setNewProgramVars; + gLinkContext.inSharedCache = &inSharedCache; + gLinkContext.setErrorStrings = &setErrorStrings; +#if SUPPORT_OLD_CRT_INITIALIZATION + gLinkContext.setRunInitialzersOldWay= &setRunInitialzersOldWay; +#endif + gLinkContext.findImageContainingAddress = &findImageContainingAddress; + gLinkContext.addDynamicReference = &addDynamicReference; +#if SUPPORT_ACCELERATE_TABLES + gLinkContext.notifySingleFromCache = ¬ifySingleFromCache; + gLinkContext.getPreInitNotifyHandler= &getPreInitNotifyHandler; + gLinkContext.getBoundBatchHandler = &getBoundBatchHandler; +#endif + gLinkContext.bindingOptions = ImageLoader::kBindingNone; + gLinkContext.argc = argc; + gLinkContext.argv = argv; + gLinkContext.envp = envp; + gLinkContext.apple = apple; + gLinkContext.progname = (argv[0] != NULL) ? basename(argv[0]) : ""; + gLinkContext.programVars.mh = mainExecutableMH; + gLinkContext.programVars.NXArgcPtr = &gLinkContext.argc; + gLinkContext.programVars.NXArgvPtr = &gLinkContext.argv; + gLinkContext.programVars.environPtr = &gLinkContext.envp; + gLinkContext.programVars.__prognamePtr=&gLinkContext.progname; + gLinkContext.mainExecutable = NULL; + gLinkContext.imageSuffix = NULL; + gLinkContext.dynamicInterposeArray = NULL; + gLinkContext.dynamicInterposeCount = 0; + gLinkContext.prebindUsage = ImageLoader::kUseAllPrebinding; +#if TARGET_IPHONE_SIMULATOR + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; +#else + gLinkContext.sharedRegionMode = ImageLoader::kUseSharedRegion; +#endif +} + + + +// +// Look for a special segment in the mach header. +// Its presences means that the binary wants to have DYLD ignore +// DYLD_ environment variables. +// +#if __MAC_OS_X_VERSION_MIN_REQUIRED +static bool hasRestrictedSegment(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + + //dyld::log("seg name: %s\n", seg->segname); + if (strcmp(seg->segname, "__RESTRICT") == 0) { + const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); + const struct macho_section* const sectionsEnd = §ionsStart[seg->nsects]; + for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { + if (strcmp(sect->sectname, "__restrict") == 0) + return true; + } + } + } + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} +#endif + +#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_IPHONE_SIMULATOR +static bool isFairPlayEncrypted(const macho_header* mh) +{ + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + if ( cmd->cmd == LC_ENCRYPT_COMMAND ) { + const encryption_info_command* enc = (encryption_info_command*)cmd; + return (enc->cryptid != 0); + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + return false; +} +#endif + +#if SUPPORT_VERSIONED_PATHS + +static bool readFirstPage(const char* dylibPath, uint8_t firstPage[4096]) +{ + firstPage[0] = 0; + // open file (automagically closed when this function exits) + FileOpener file(dylibPath); + + if ( file.getFileDescriptor() == -1 ) + return false; + + 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; + } + } + + return true; +} + +// +// 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) +{ + uint8_t firstPage[4096]; + const macho_header* mh = (macho_header*)firstPage; + if ( !readFirstPage(dylibPath, firstPage) ) { + // If file cannot be read, check to see if path is in shared cache + const macho_header* mhInCache; + const char* pathInCache; + long slideInCache; + if ( !findInSharedCacheImage(dylibPath, true, NULL, &mhInCache, &pathInCache, &slideInCache) ) + return false; + mh = mhInCache; + } + + // check mach-o header + 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; +} +#endif // SUPPORT_VERSIONED_PATHS + + +#if 0 +static void printAllImages() +{ + dyld::log("printAllImages()\n"); + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + dyld_image_states imageState = image->getState(); + dyld::log(" state=%d, dlopen-count=%d, never-unload=%d, in-use=%d, name=%s\n", + imageState, image->dlopenCount(), image->neverUnload(), image->isMarkedInUse(), image->getShortName()); + } +} +#endif + +void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex) +{ + // add to list of known images. This did not happen at creation time for bundles + if ( image->isBundle() && !image->isLinked() ) + addImage(image); + + // we detect root images as those not linked in yet + if ( !image->isLinked() ) + addRootImage(image); + + // process images + try { + const char* path = image->getPath(); +#if SUPPORT_ACCELERATE_TABLES + if ( image == sAllCacheImagesProxy ) + path = sAllCacheImagesProxy->getIndexedPath(cacheIndex); +#endif + image->link(gLinkContext, forceLazysBound, false, neverUnload, loaderRPaths, path); + } + catch (const char* msg) { + garbageCollectImages(); + throw; + } +} + + +void runInitializers(ImageLoader* image) +{ + // do bottom up initialization + ImageLoader::InitializerTimingList initializerTimes[allImagesCount()]; + initializerTimes[0].count = 0; + image->runInitializers(gLinkContext, initializerTimes[0]); +} + +// This function is called at the end of dlclose() when the reference count goes to zero. +// The dylib being unloaded may have brought in other dependent dylibs when it was loaded. +// Those dependent dylibs need to be unloaded, but only if they are not referenced by +// something else. We use a standard mark and sweep garbage collection. +// +// The tricky part is that when a dylib is unloaded it may have a termination function that +// can run and itself call dlclose() on yet another dylib. The problem is that this +// sort of gabage collection is not re-entrant. Instead a terminator's call to dlclose() +// which calls garbageCollectImages() will just set a flag to re-do the garbage collection +// when the current pass is done. +// +// Also note that this is done within the dyld global lock, so it is always single threaded. +// +void garbageCollectImages() +{ + static bool sDoingGC = false; + static bool sRedo = false; + + if ( sDoingGC ) { + // GC is currently being run, just set a flag to have it run again. + sRedo = true; + return; + } + + sDoingGC = true; + do { + sRedo = false; + + // mark phase: mark all images not-in-use + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + //dyld::log("gc: neverUnload=%d name=%s\n", image->neverUnload(), image->getShortName()); + image->markNotUsed(); + } + + // sweep phase: mark as in-use, images reachable from never-unload or in-use image + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( (image->dlopenCount() != 0) || image->neverUnload() || (image == sMainExecutable) ) { + OSSpinLockLock(&sDynamicReferencesLock); + image->markedUsedRecursive(sDynamicReferences); + OSSpinLockUnlock(&sDynamicReferencesLock); + } + } + + // collect phase: build array of images not marked in-use + ImageLoader* deadImages[sAllImages.size()]; + unsigned deadCount = 0; + int maxRangeCount = 0; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( ! image->isMarkedInUse() ) { + deadImages[deadCount++] = image; + if (gLogAPIs) dyld::log("dlclose(), found unused image %p %s\n", image, image->getShortName()); + maxRangeCount += image->segmentCount(); + } + } + + // collect phase: run termination routines for images not marked in-use + __cxa_range_t ranges[maxRangeCount]; + int rangeCount = 0; + for (unsigned i=0; i < deadCount; ++i) { + ImageLoader* image = deadImages[i]; + for (unsigned int j=0; j < image->segmentCount(); ++j) { + if ( !image->segExecutable(j) ) + continue; + if ( rangeCount < maxRangeCount ) { + ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j); + ranges[rangeCount].length = image->segSize(j); + ++rangeCount; + } + } + try { + runImageStaticTerminators(image); + } + catch (const char* msg) { + dyld::warn("problem running terminators for image: %s\n", msg); + } + } + + // dyld should call __cxa_finalize_ranges() + if ( (rangeCount > 0) && (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 13) ) + (*gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount); + + // collect phase: delete all images which are not marked in-use + bool mightBeMore; + do { + mightBeMore = false; + for (std::vector::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) { + ImageLoader* image = *it; + if ( ! image->isMarkedInUse() ) { + try { + if (gLogAPIs) dyld::log("dlclose(), deleting %p %s\n", image, image->getShortName()); + removeImage(image); + ImageLoader::deleteImage(image); + mightBeMore = true; + break; // interator in invalidated by this removal + } + catch (const char* msg) { + dyld::warn("problem deleting image: %s\n", msg); + } + } + } + } while ( mightBeMore ); + } while (sRedo); + sDoingGC = false; + + //printAllImages(); + +} + + +static void preflight_finally(ImageLoader* image) +{ + if ( image->isBundle() ) { + removeImageFromAllImages(image->machHeader()); + ImageLoader::deleteImage(image); + } + sBundleBeingLoaded = NULL; + dyld::garbageCollectImages(); +} + + +void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex) +{ + try { + if ( image->isBundle() ) + sBundleBeingLoaded = image; // hack + const char* path = image->getPath(); +#if SUPPORT_ACCELERATE_TABLES + if ( image == sAllCacheImagesProxy ) + path = sAllCacheImagesProxy->getIndexedPath(cacheIndex); +#endif + image->link(gLinkContext, false, true, false, loaderRPaths, path); + } + catch (const char* msg) { + preflight_finally(image); + throw; + } + preflight_finally(image); +} + +static void loadInsertedDylib(const char* path) +{ + ImageLoader* image = NULL; + unsigned cacheIndex; + try { + LoadContext context; + context.useSearchPaths = false; + context.useFallbackPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = true; + context.canBePIE = false; + context.origin = NULL; // can't use @loader_path with DYLD_INSERT_LIBRARIES + context.rpath = NULL; + image = load(path, context, cacheIndex); + } + catch (const char* msg) { +#if TARGET_IPHONE_SIMULATOR + dyld::log("dyld: warning: could not load inserted library '%s' because %s\n", path, msg); +#else +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processUsingLibraryValidation ) + dyld::log("dyld: warning: could not load inserted library '%s' into library validated process because %s\n", path, msg); + else +#endif + halt(dyld::mkstringf("could not load inserted library '%s' because %s\n", path, msg)); +#endif + } + catch (...) { + halt(dyld::mkstringf("could not load inserted library '%s'\n", path)); + } +} + + +// +// Sets: +// sEnvMode +// gLinkContext.requireCodeSignature +// gLinkContext.processIsRestricted // Mac OS X only +// gLinkContext.processUsingLibraryValidation // Mac OS X only +// +static void configureProcessRestrictions(const macho_header* mainExecutableMH) +{ +#if TARGET_IPHONE_SIMULATOR + sEnvMode = envAll; + gLinkContext.requireCodeSignature = true; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + sEnvMode = envNone; + gLinkContext.requireCodeSignature = true; + uint32_t flags; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + if ( flags & CS_ENFORCEMENT ) { + if ( flags & CS_GET_TASK_ALLOW ) { + // Xcode built app for Debug allowed to use DYLD_* variables + sEnvMode = envAll; + } + else { + // Development kernel can use DYLD_PRINT_* variables on any FairPlay encrypted app + uint32_t secureValue = 0; + size_t secureValueSize = sizeof(secureValue); + if ( (sysctlbyname("kern.secure_kernel", &secureValue, &secureValueSize, NULL, 0) == 0) && (secureValue == 0) && isFairPlayEncrypted(mainExecutableMH) ) { + sEnvMode = envPrintOnly; + } + } + } + else { + // Development kernel can run unsigned code + sEnvMode = envAll; + gLinkContext.requireCodeSignature = false; + } + } + if ( issetugid() ) { + sEnvMode = envNone; + } +#elif __MAC_OS_X_VERSION_MIN_REQUIRED + sEnvMode = envAll; + gLinkContext.requireCodeSignature = false; + gLinkContext.processIsRestricted = false; + gLinkContext.processUsingLibraryValidation = false; + // any processes with setuid or setgid bit set or with __RESTRICT segment is restricted + if ( issetugid() || hasRestrictedSegment(mainExecutableMH) ) { + gLinkContext.processIsRestricted = true; + } + bool usingSIP = (csr_check(CSR_ALLOW_TASK_FOR_PID) != 0); + uint32_t flags; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) != -1 ) { + // On OS X CS_RESTRICT means the program was signed with entitlements + if ( ((flags & CS_RESTRICT) == CS_RESTRICT) && usingSIP ) { + gLinkContext.processIsRestricted = true; + } + // Library Validation loosens searching but requires everything to be code signed + if ( flags & CS_REQUIRE_LV ) { + gLinkContext.processIsRestricted = false; + //gLinkContext.requireCodeSignature = true; + gLinkContext.processUsingLibraryValidation = true; + sSafeMode = usingSIP; + } + } +#endif +} + + +bool processIsRestricted() +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + return gLinkContext.processIsRestricted; +#else + return false; +#endif +} + + +// Add dyld to uuidArray to enable symbolication of stackshots +static void addDyldImageToUUIDList() +{ + const struct macho_header* mh = (macho_header*)&__dso_handle; + 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_UUID: { + uuid_command* uc = (uuid_command*)cmd; + dyld_uuid_info info; + info.imageLoadAddress = (mach_header*)mh; + memcpy(info.imageUUID, uc->uuid, 16); + addNonSharedCacheImageUUID(info); + return; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +void notifyKernelAboutImage(const struct macho_header* mh, const char* fileInfo) +{ + const char *endptr = nullptr; + uint64_t fsid_scalar = hexToUInt64(fileInfo, &endptr); + uint64_t fsobj_id_scalar = 0; + if (endptr != nullptr) { + fsobj_id_scalar = hexToUInt64(endptr+1, &endptr); + } + 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_UUID: { + // Add dyld to the kernel image info + uuid_command* uc = (uuid_command*)cmd; + dyld3::kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, (const uuid_t *)&uc->uuid[0], *reinterpret_cast(&fsobj_id_scalar), *reinterpret_cast(&fsid_scalar), (const mach_header *)mh); + return; + } + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } +} + +#if __MAC_OS_X_VERSION_MIN_REQUIRED +typedef int (*open_proc_t)(const char*, int, int); +typedef int (*fcntl_proc_t)(int, int, void*); +typedef int (*ioctl_proc_t)(int, unsigned long, void*); +static void* getProcessInfo() { return dyld::gProcessInfo; } +static SyscallHelpers sSysCalls = { + 8, + // added in version 1 + (open_proc_t)&open, + &close, + &pread, + &write, + &mmap, + &munmap, + &madvise, + &stat, + (fcntl_proc_t)&fcntl, + (ioctl_proc_t)&ioctl, + &issetugid, + &getcwd, + &realpath, + &vm_allocate, + &vm_deallocate, + &vm_protect, + &vlog, + &vwarn, + &pthread_mutex_lock, + &pthread_mutex_unlock, + &mach_thread_self, + &mach_port_deallocate, + &task_self_trap, + &mach_timebase_info, + &OSAtomicCompareAndSwapPtrBarrier, + &OSMemoryBarrier, + &getProcessInfo, + &__error, + &mach_absolute_time, + // added in version 2 + &thread_switch, + // added in version 3 + &opendir, + &readdir_r, + &closedir, + // added in version 4 + &coresymbolication_load_notifier, + &coresymbolication_unload_notifier, + // Added in version 5 + &proc_regionfilename, + &getpid, + &mach_port_insert_right, + &mach_port_allocate, + &mach_msg, + // Added in version 6 + &abort_with_payload, + // Added in version 7 + &task_register_dyld_image_infos, + &task_unregister_dyld_image_infos, + &task_get_dyld_image_infos, + &task_register_dyld_shared_cache_image_info, + &task_register_dyld_set_dyld_state, + &task_register_dyld_get_process_state, + // Added in version 8 + &task_info, + &thread_info, + &kdebug_is_enabled, + &kdebug_trace +}; + +__attribute__((noinline)) +static const char* useSimulatorDyld(int fd, const macho_header* mainExecutableMH, const char* dyldPath, + int argc, const char* argv[], const char* envp[], const char* apple[], + uintptr_t* startGlue, uintptr_t* mainAddr) +{ + *startGlue = 0; + *mainAddr = 0; + + // simulator does not support restricted processes + uint32_t flags; + if ( csops(0, CS_OPS_STATUS, &flags, sizeof(flags)) == -1 ) + return "csops() failed"; + if ( (flags & CS_RESTRICT) == CS_RESTRICT ) + return "dyld_sim cannot be loaded in a restricted process"; + if ( issetugid() ) + return "dyld_sim cannot be loaded in a setuid process"; + if ( hasRestrictedSegment(mainExecutableMH) ) + return "dyld_sim cannot be loaded in a restricted process"; + + // get file size of dyld_sim + struct stat sb; + if ( fstat(fd, &sb) == -1 ) + return "stat(dyld_sim) failed"; + + // read first page of dyld_sim file + uint8_t firstPage[4096]; + if ( pread(fd, firstPage, 4096, 0) != 4096 ) + return "pread(dyld_sim) failed"; + + // if fat file, pick matching slice + uint64_t fileOffset = 0; + uint64_t fileLength = sb.st_size; + const fat_header* fileStartAsFat = (fat_header*)firstPage; + if ( fileStartAsFat->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + if ( !fatFindBest(fileStartAsFat, &fileOffset, &fileLength) ) + return "no matching arch in dyld_sim"; + // re-read buffer from start of mach-o slice in fat file + if ( pread(fd, firstPage, 4096, fileOffset) != 4096 ) + return "pread(dyld_sim) failed"; + } + else if ( !isCompatibleMachO(firstPage, dyldPath) ) { + return "dyld_sim is not compatible with the loaded process, likely due to architecture mismatch"; + } + + // calculate total size of dyld segments + const macho_header* mh = (const macho_header*)firstPage; + struct macho_segment_command* lastSeg = NULL; + struct macho_segment_command* firstSeg = NULL; + uintptr_t mappingSize = 0; + uintptr_t preferredLoadAddress = 0; + const uint32_t cmd_count = mh->ncmds; + if ( mh->sizeofcmds > 4096 ) + return "dyld_sim load commands to large"; + if ( (sizeof(macho_header) + mh->sizeofcmds) > 4096 ) + return "dyld_sim load commands to large"; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* const endCmds = (struct load_command*)(((char*)mh) + sizeof(macho_header) + mh->sizeofcmds); + const struct load_command* cmd = cmds; + for (uint32_t i = 0; i < cmd_count; ++i) { + uint32_t cmdLength = cmd->cmdsize; + if ( cmdLength < 8 ) + return "dyld_sim load command too small"; + const struct load_command* const nextCmd = (const struct load_command*)(((char*)cmd)+cmdLength); + if ( (nextCmd > endCmds) || (nextCmd < cmd) ) + return "dyld_sim load command too large"; + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( seg->vmaddr + seg->vmsize < seg->vmaddr ) + return "dyld_sim seg wraps address space"; + if ( seg->vmsize < seg->filesize ) + return "dyld_sim seg vmsize too small"; + if ( (seg->fileoff + seg->filesize) < seg->fileoff ) + return "dyld_sim seg size wraps address space"; + if ( lastSeg == NULL ) { + // first segment must be __TEXT and start at beginning of file/slice + firstSeg = seg; + if ( strcmp(seg->segname, "__TEXT") != 0 ) + return "dyld_sim first segment not __TEXT"; + if ( seg->fileoff != 0 ) + return "dyld_sim first segment not at file offset zero"; + if ( seg->filesize < (sizeof(macho_header) + mh->sizeofcmds) ) + return "dyld_sim first segment smaller than load commands"; + preferredLoadAddress = seg->vmaddr; + } + else { + // other sements must be continguous with previous segment and not executable + if ( lastSeg->fileoff + lastSeg->filesize != seg->fileoff ) + return "dyld_sim segments not contiguous"; + if ( lastSeg->vmaddr + lastSeg->vmsize != seg->vmaddr ) + return "dyld_sim segments not address contiguous"; + if ( (seg->initprot & VM_PROT_EXECUTE) != 0 ) + return "dyld_sim non-first segment is executable"; + } + mappingSize += seg->vmsize; + lastSeg = seg; + } + break; + case LC_SEGMENT_COMMAND_WRONG: + return "dyld_sim wrong load segment load command"; + } + cmd = nextCmd; + } + // last segment must be named __LINKEDIT and not writable + if ( strcmp(lastSeg->segname, "__LINKEDIT") != 0 ) + return "dyld_sim last segment not __LINKEDIT"; + if ( lastSeg->initprot & VM_PROT_WRITE ) + return "dyld_sim __LINKEDIT segment writable"; + + // reserve space, then mmap each segment + vm_address_t loadAddress = 0; + if ( ::vm_allocate(mach_task_self(), &loadAddress, mappingSize, VM_FLAGS_ANYWHERE) != 0 ) + return "dyld_sim cannot allocate space"; + cmd = cmds; + struct linkedit_data_command* codeSigCmd = NULL; + struct source_version_command* dyldVersionCmd = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + uintptr_t requestedLoadAddress = seg->vmaddr - preferredLoadAddress + loadAddress; + void* segAddress = ::mmap((void*)requestedLoadAddress, seg->filesize, seg->initprot, MAP_FIXED | MAP_PRIVATE, fd, fileOffset + seg->fileoff); + //dyld::log("dyld_sim %s mapped at %p\n", seg->segname, segAddress); + if ( segAddress == (void*)(-1) ) + return "dyld_sim mmap() of segment failed"; + if ( ((uintptr_t)segAddress < loadAddress) || ((uintptr_t)segAddress+seg->filesize > loadAddress+mappingSize) ) + return "dyld_sim mmap() to wrong location"; + } + break; + case LC_CODE_SIGNATURE: + codeSigCmd = (struct linkedit_data_command*)cmd; + break; + case LC_SOURCE_VERSION: + dyldVersionCmd = (struct source_version_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // must have code signature which is contained within LINKEDIT segment + if ( codeSigCmd == NULL ) + return "dyld_sim not code signed"; + if ( codeSigCmd->dataoff < lastSeg->fileoff ) + return "dyld_sim code signature not in __LINKEDIT"; + if ( (codeSigCmd->dataoff + codeSigCmd->datasize) < codeSigCmd->dataoff ) + return "dyld_sim code signature size wraps"; + if ( (codeSigCmd->dataoff + codeSigCmd->datasize) > (lastSeg->fileoff + lastSeg->filesize) ) + return "dyld_sim code signature extends beyond __LINKEDIT"; + + fsignatures_t siginfo; + siginfo.fs_file_start=fileOffset; // start of mach-o slice in fat file + siginfo.fs_blob_start=(void*)(long)(codeSigCmd->dataoff); // start of code-signature in mach-o file + siginfo.fs_blob_size=codeSigCmd->datasize; // size of code-signature + int result = fcntl(fd, F_ADDFILESIGS_FOR_DYLD_SIM, &siginfo); + if ( result == -1 ) { + return mkstringf("dyld_sim fcntl(F_ADDFILESIGS_FOR_DYLD_SIM) failed with errno=%d", errno); + } + close(fd); + // file range covered by code signature must extend up to code signature itself + if ( siginfo.fs_file_start < codeSigCmd->dataoff ) + return mkstringf("dyld_sim code signature does not cover all of dyld_sim. Signature covers up to 0x%08lX. Signature starts at 0x%08X", (unsigned long)siginfo.fs_file_start, codeSigCmd->dataoff); + + // walk newly mapped dyld_sim __TEXT load commands to find entry point + uintptr_t entry = 0; + cmd = (struct load_command*)(((char*)loadAddress)+sizeof(macho_header)); + const uint32_t count = ((macho_header*)(loadAddress))->ncmds; + for (uint32_t i = 0; i < count; ++i) { + if (cmd->cmd == LC_UNIXTHREAD) { + #if __i386__ + const i386_thread_state_t* registers = (i386_thread_state_t*)(((char*)cmd) + 16); + // entry point must be in first segment + if ( registers->__eip < firstSeg->vmaddr ) + return "dyld_sim entry point not in __TEXT segment"; + if ( registers->__eip > (firstSeg->vmaddr + firstSeg->vmsize) ) + return "dyld_sim entry point not in __TEXT segment"; + entry = (registers->__eip + loadAddress - preferredLoadAddress); + #elif __x86_64__ + const x86_thread_state64_t* registers = (x86_thread_state64_t*)(((char*)cmd) + 16); + // entry point must be in first segment + if ( registers->__rip < firstSeg->vmaddr ) + return "dyld_sim entry point not in __TEXT segment"; + if ( registers->__rip > (firstSeg->vmaddr + firstSeg->vmsize) ) + return "dyld_sim entry point not in __TEXT segment"; + entry = (registers->__rip + loadAddress - preferredLoadAddress); + #endif + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // notify debugger that dyld_sim is loaded + dyld_image_info info; + info.imageLoadAddress = (mach_header*)loadAddress; + info.imageFilePath = strdup(dyldPath); + info.imageFileModDate = sb.st_mtime; + addImagesToAllImages(1, &info); + dyld::gProcessInfo->notification(dyld_image_adding, 1, &info); + + const char** appleParams = apple; + // jump into new simulator dyld + typedef uintptr_t (*sim_entry_proc_t)(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers* vtable, uintptr_t* startGlue); + sim_entry_proc_t newDyld = (sim_entry_proc_t)entry; + *mainAddr = (*newDyld)(argc, argv, envp, appleParams, mainExecutableMH, (macho_header*)loadAddress, + loadAddress - preferredLoadAddress, + &sSysCalls, startGlue); + return NULL; +} +#endif + +// +// If the DYLD_SKIP_MAIN environment is set to 1, dyld will return the +// address of this function instead of main() in the target program which +// __dyld_start jumps to. Useful for qualifying dyld itself. +// +int +fake_main() +{ + return 0; +} + + + + +static bool envVarMatches(dyld3::launch_cache::Closure mainClosure, const char* envp[], const char* varName) +{ + __block const char* valueFromClosure = nullptr; + mainClosure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) { + size_t keyLen = strlen(varName); + if ( (strncmp(varName, keyEqualValue, keyLen) == 0) && (keyEqualValue[keyLen] == '=') ) { + valueFromClosure = &keyEqualValue[keyLen+1]; + stop = true; + } + }); + + const char* valueFromEnv = _simple_getenv(envp, varName); + + bool inClosure = (valueFromClosure != nullptr); + bool inEnv = (valueFromEnv != nullptr); + if ( inClosure != inEnv ) + return false; + if ( !inClosure && !inEnv ) + return true; + return ( strcmp(valueFromClosure, valueFromEnv) == 0 ); +} + +static const char* const sEnvVarsToCheck[] = { + "DYLD_LIBRARY_PATH", + "DYLD_FRAMEWORK_PATH", + "DYLD_FALLBACK_LIBRARY_PATH", + "DYLD_FALLBACK_FRAMEWORK_PATH", + "DYLD_INSERT_LIBRARIES", + "DYLD_IMAGE_SUFFIX", + "DYLD_VERSIONED_FRAMEWORK_PATH", + "DYLD_VERSIONED_LIBRARY_PATH", + "DYLD_ROOT_PATH" +}; + +static bool envVarsMatch(dyld3::launch_cache::Closure mainClosure, const char* envp[]) +{ + for (const char* envVar : sEnvVarsToCheck) { + if ( !envVarMatches(mainClosure, envp, envVar) ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because %s changed\n", mainClosure.binaryData(), envVar); + return false; + } + } + + return true; +} + +static bool closureValid(const dyld3::launch_cache::BinaryClosureData* mainClosureData, const mach_header* mainExecutableMH, const uint8_t* mainExecutableCDHash, bool closureInCache, const char* envp[]) +{ + const dyld3::launch_cache::Closure mainClosure(mainClosureData); + const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group(); + + // verify current dyld cache is same as expected + if ( sSharedCacheLoadInfo.loadAddress == nullptr ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p dyld cache not loaded\n", mainClosureData); + return false; + } + if ( !closureInCache ) { + // closures in cache don't have cache's UUID + uuid_t cacheUUID; + sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID); + if ( memcmp(mainClosure.dyldCacheUUID(), cacheUUID, sizeof(uuid_t)) != 0 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because built against different dyld cache\n", mainClosureData); + return false; + } + } +#if __MAC_OS_X_VERSION_MIN_REQUIRED + else { + // HACK until closured for dlopen can run against live cache file + int fd = my_open(sSharedCacheLoadInfo.path, O_RDONLY, 0); + if ( fd != -1 ) { + dyld_cache_header fileHeader; + if ( pread(fd, &fileHeader, sizeof(fileHeader), 0) == sizeof(fileHeader) ) { + uuid_t cacheUUID; + sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID); + if ( memcmp(fileHeader.uuid, cacheUUID, sizeof(uuid_t)) != 0 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because current cache on disk is not they one being used\n", mainClosureData); + ::close(fd); + return false; + } + } + ::close(fd); + } + } +#endif + + // verify main executable file has not changed since closure was built + const dyld3::launch_cache::Image mainImage = mainGroup.image(mainClosure.mainExecutableImageIndex()); + if ( mainImage.validateUsingModTimeAndInode() ) { + struct stat statBuf; + if ( ::stat(mainImage.path(), &statBuf) != 0 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because stat() failed on main executable\n", mainClosureData); + return false; + } + else if ( (statBuf.st_mtime != mainImage.fileModTime()) || (statBuf.st_ino != mainImage.fileINode()) ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because mtime/inode changed since closure was built\n", mainClosureData); + return false; + } + } + + // verify cdHash of main executable is same as recorded in closure + if ( mainImage.validateUsingCdHash() ) { + if ( mainExecutableCDHash == nullptr ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because main executable is not code signed but was expected to be\n", mainClosureData); + return false; + } + if ( memcmp(mainExecutableCDHash, mainClosure.cdHash(), 20) != 0 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because main executable cd-hash changed since closure was built\n", mainClosureData); + return false; + } + } + + // verify UUID of main executable is same as recorded in closure + const uuid_t* closureMainUUID = mainImage.uuid(); + dyld3::MachOParser parser(mainExecutableMH); + uuid_t actualUUID; + parser.getUuid(actualUUID); + if ( memcmp(actualUUID, closureMainUUID, sizeof(uuid_t)) != 0 ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because UUID of executable changed since closure was built\n", mainClosureData); + return false; + } + + // verify DYLD_* env vars are same as when closure was built + if ( !envVarsMatch(mainClosure, envp) ) { + return false; + } + + // verify files that are supposed to be missing actually are missing + __block bool foundFileThatInvalidatesClosure = false; + mainClosure.forEachMustBeMissingFile(^(const char* path, bool& stop) { + struct stat statBuf; + if ( ::stat(path, &statBuf) == 0 ) { + stop = true; + foundFileThatInvalidatesClosure = true; + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because found unexpected file '%s'\n", mainClosureData, path); + } + }); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // verify no key frameworks have been overridden since cache was built + if ( dyld3::loader::internalInstall() ) { + dyld3::loader::forEachLineInFile("/AppleInternal/Library/Preferences/dyld-potential-framework-overrides", ^(const char* path, bool& stop) { + dyld3::SharedCacheFindDylibResults shareCacheResults; + if ( dyld3::findInSharedCacheImage(sSharedCacheLoadInfo, path, &shareCacheResults) ) { + dyld3::launch_cache::Image image(shareCacheResults.imageData); + struct stat statBuf; + if ( ::stat(path, &statBuf) == 0 ) { + if ( (image.fileModTime() != statBuf.st_mtime) || (image.fileINode() != statBuf.st_ino)) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closure %p not used because framework has changed: '%s'\n", mainClosureData, path); + foundFileThatInvalidatesClosure = true; + stop = true; + } + } + } + }); + } +#endif + + return !foundFileThatInvalidatesClosure; +} + +static bool nolog(const char* format, ...) +{ + return false; +} + +static bool dolog(const char* format, ...) +{ + va_list list; + va_start(list, format); + vlog(format, list); + va_end(list); + return true; +} + +static bool launchWithClosure(const dyld3::launch_cache::BinaryClosureData* mainClosureData, + const DyldSharedCache* dyldCache, + const mach_header* mainExecutableMH, uintptr_t mainExecutableSlide, + int argc, const char* argv[], const char* envp[], const char* apple[], + uintptr_t* entry, uintptr_t* startGlue) +{ + dyld3::launch_cache::Closure mainClosure(mainClosureData); + const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group(); + const uint32_t mainExecutableIndex = mainClosure.mainExecutableImageIndex(); + const dyld3::launch_cache::Image mainImage = mainGroup.image(mainExecutableIndex); + const uint32_t loadedImageCount = mainClosure.initialImageCount(); + + // construct array of groups + dyld3::DyldCacheParser cacheParser(dyldCache, false); + STACK_ALLOC_DYNARRAY(const dyld3::launch_cache::binary_format::ImageGroup*, 3, theGroups); + theGroups[0] = cacheParser.cachedDylibsGroup(); + theGroups[1] = cacheParser.otherDylibsGroup(); + theGroups[2] = mainClosure.group().binaryData(); + + // construct array of all Image*, starting with any inserted dylibs, then main executable + const dyld3::launch_cache::BinaryImageData* images[loadedImageCount]; + dyld3::launch_cache::SlowLoadSet imageSet(&images[0], &images[loadedImageCount]); + for (uint32_t i=0; i <= mainExecutableIndex; ++i) { + imageSet.add(mainGroup.image(i).binaryData()); + } + // add all dependents of main executable + if ( !mainImage.recurseAllDependentImages(theGroups, imageSet, nullptr) ) { + dyld::log("initial image list overflow, expected only %d\n", loadedImageCount); + return false; + } + // add dependents of any inserted dylibs + for (uint32_t i=0; i < mainExecutableIndex; ++i) { + if ( !mainGroup.image(i).recurseAllDependentImages(theGroups, imageSet, nullptr) ) { + dyld::log("initial image list overflow in inserted libraries, expected only %d\n", loadedImageCount); + return false; + } + } + const uint32_t actualImageCount = (uint32_t)imageSet.count(); + // construct array of allImages + STACK_ALLOC_DYNARRAY(dyld3::loader::ImageInfo, actualImageCount, allImages); + for (int i=0; i < actualImageCount; ++i) { + dyld3::launch_cache::Image img(images[i]); + dyld3::launch_cache::ImageGroup grp = img.group(); + allImages[i].imageData = img.binaryData(); + allImages[i].loadAddress = nullptr; + allImages[i].groupNum = grp.groupNum(); + allImages[i].indexInGroup = grp.indexInGroup(img.binaryData()); + allImages[i].previouslyFixedUp = false; + allImages[i].justMapped = false; + allImages[i].justUsedFromDyldCache = false; + allImages[i].neverUnload = false; + } + // prefill address of main executable to mark it is already loaded + allImages[mainExecutableIndex].loadAddress = mainExecutableMH; + + // map new images and apply all fixups + Diagnostics diag; + mapAndFixupImages(diag, allImages, (const uint8_t*)dyldCache, (gLinkContext.verboseLoading ? &dolog : &nolog), + (gLinkContext.verboseMapping ? &dolog : &nolog), + (gLinkContext.verboseBind ? &dolog : &nolog), + (gLinkContext.verboseDOF ? &dolog : &nolog)); + if ( diag.hasError() ) { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: %s\n", diag.errorMessage()); + return false; + } + + //dyld::log("loaded image list:\n"); + //for (int i=0; i < allImages.count(); ++i) { + // dyld3::launch_cache::Image img(allImages[i].imageData); + // dyld::log("binImage[%d]=%p, mh=%p, path=%s\n", i, allImages[i].imageData, allImages[i].loadAddress, img.path()); + //} + + // find special images + const dyld3::launch_cache::BinaryImageData* libSystemImage = mainClosure.libSystem(theGroups); + const dyld3::launch_cache::BinaryImageData* libDyldImage = mainClosure.libDyld(theGroups); + const mach_header* libdyldMH = nullptr; + const mach_header* libSystemMH = nullptr; + for (int i=0; i < allImages.count(); ++i) { + if ( allImages[i].imageData == libSystemImage ) + libSystemMH = allImages[i].loadAddress; + else if ( allImages[i].imageData == libDyldImage ) + libdyldMH = allImages[i].loadAddress; + } + + // send info on all images to libdyld.dylb + const dyld3::LibDyldEntryVector* libDyldEntry = (dyld3::LibDyldEntryVector*)((uint8_t*)libdyldMH + mainClosure.libdyldVectorOffset()); + libDyldEntry->setVars(mainExecutableMH, argc, argv, envp, apple); + libDyldEntry->setHaltFunction(&halt); + if ( libDyldEntry->vectorVersion > 2 ) + libDyldEntry->setChildForkFunction(&_dyld_fork_child); +#if !TARGET_IPHONE_SIMULATOR + if ( libDyldEntry->vectorVersion > 3 ) + libDyldEntry->setLogFunction(&dyld::vlog); +#endif + libDyldEntry->setOldAllImageInfo(gProcessInfo); + libDyldEntry->setInitialImageList(mainClosureData, dyldCache, sSharedCacheLoadInfo.path, allImages, libSystemMH, libSystemImage); + // run initializers + CRSetCrashLogMessage("dyld3: launch, running initializers"); + libDyldEntry->runInitialzersBottomUp((mach_header*)mainExecutableMH); + //dyld::log("returned from runInitialzersBottomUp()\n"); + + dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0); + if ( mainClosure.mainExecutableUsesCRT() ) { + // old style app linked with crt1.o + // entry is "start" function in program + *startGlue = 0; + *entry = (uintptr_t)mainExecutableMH + mainClosure.mainExecutableEntryOffset(); + } + else { + // modern app with LC_MAIN + // set startGlue to "start" function in libdyld.dylib + // set entry to "main" function in program + *startGlue = (uintptr_t)(libDyldEntry->startFunc); + *entry =(uintptr_t)mainExecutableMH + mainClosure.mainExecutableEntryOffset(); + } + CRSetCrashLogMessage(NULL); + return true; +} + +static void putHexNibble(uint8_t value, char*& p) +{ + if ( value < 10 ) + *p++ = '0' + value; + else + *p++ = 'A' + value - 10; +} + +static void putHexByte(uint8_t value, char*& p) +{ + value &= 0xFF; + putHexNibble(value >> 4, p); + putHexNibble(value & 0x0F, p); +} + +static void makeHexLong(unsigned long value, char* p) +{ + *p++ = '0'; + *p++ = 'x'; +#if __LP64__ + putHexByte(value >> 56, p); + putHexByte(value >> 48, p); + putHexByte(value >> 40, p); + putHexByte(value >> 32, p); +#endif + putHexByte(value >> 24, p); + putHexByte(value >> 16, p); + putHexByte(value >> 8, p); + putHexByte(value, p); + *p = '\0'; +} + +static void makeUUID(uint8_t uuid[16], char* p) +{ + putHexByte(uuid[0], p); + putHexByte(uuid[1], p); + putHexByte(uuid[2], p); + putHexByte(uuid[3], p); + *p++ = '-'; + putHexByte(uuid[4], p); + putHexByte(uuid[5], p); + *p++ = '-'; + putHexByte(uuid[6], p); + putHexByte(uuid[7], p); + *p++ = '-'; + putHexByte(uuid[8], p); + putHexByte(uuid[9], p); + *p++ = '-'; + putHexByte(uuid[10], p); + putHexByte(uuid[11], p); + putHexByte(uuid[12], p); + putHexByte(uuid[13], p); + putHexByte(uuid[14], p); + putHexByte(uuid[15], p); + *p = '\0'; +} + +#if !TARGET_IPHONE_SIMULATOR +static const dyld3::launch_cache::BinaryClosureData* callClosureDaemon(const char* mainExecPath, const char* envp[]) +{ + // temp, until we can get a bootstrap_lookup that works from dyld +#if 1 + // Create a pipe + int sockets[2]; + if ( ::pipe(sockets) < 0 ) { + dyld::log("error opening stream socket pair to closured\n"); + return NULL; + } + //dyld::log("created sockets %d and %d\n", sockets[0], sockets[1]); + // use fork/exec to launch closured + int child = ::__fork(); + if ( child == -1 ) { + dyld::log("error forking, errno=%d\n", errno); + return NULL; + } + if ( child ) { + // parent side + //dyld::log("parent side pid=%d\n", getpid()); + ::close(sockets[1]); + SocketBasedClousureHeader header; + long amount = ::read(sockets[0], &header, sizeof(SocketBasedClousureHeader)); + if ( amount != sizeof(SocketBasedClousureHeader) ) { + dyld::log("error reading, errno=%d\n", errno); + return NULL; + } + vm_address_t bufferAddress = 0; + if ( ::vm_allocate(mach_task_self(), &bufferAddress, header.length, VM_FLAGS_ANYWHERE) != 0 ) { + dyld::log("error allocating buffer\n"); + return NULL; + } + amount = ::read(sockets[0], (void*)bufferAddress, header.length); + close(sockets[0]); + if ( amount != header.length ) { + dyld::log("dyld: error reading buffer header from closured, amount=%ld, errno=%d\n", amount, errno); + return NULL; + } + if ( header.success ) { + // make buffer read-only + vm_protect(mach_task_self(), bufferAddress, header.length, VM_PROT_READ, VM_PROT_READ); + return (const dyld3::launch_cache::BinaryClosureData*)bufferAddress; + } + else { + // buffer contains error message as to why closure could not be built + dyld::log("%s", (char*)bufferAddress); + ::vm_deallocate(mach_task_self(), bufferAddress, header.length); + return NULL; + } + } + else { + // child side + //dyld::log("child side pid=%d\n", getpid()); + close(sockets[0]); + const char* closuredPath = "/usr/libexec/closured"; + char pipeStr[8]; + pipeStr[0] = '0' + sockets[1]; + pipeStr[1] = '\0'; + const char* argv[32]; + char cacheUuidString[64]; + char cacheAddrString[64]; + char cacheSizeString[64]; + int i = 0; + uuid_t cacheUUID; + sSharedCacheLoadInfo.loadAddress->getUUID(cacheUUID); + makeHexLong((long)sSharedCacheLoadInfo.loadAddress, cacheAddrString); + makeHexLong((long)sSharedCacheLoadInfo.loadAddress->mappedSize(), cacheSizeString); + makeUUID(cacheUUID, cacheUuidString); + argv[i++] = closuredPath; + argv[i++] = "-create_closure"; + argv[i++] = mainExecPath; + argv[i++] = "-pipefd"; + argv[i++] = pipeStr; + argv[i++] = "-cache_uuid"; + argv[i++] = cacheUuidString; + argv[i++] = "-cache_address"; + argv[i++] = cacheAddrString; + argv[i++] = "-cache_size"; + argv[i++] = cacheSizeString; + for (const char**p=envp; *p != NULL; ++p) { + const char* envToCheck = *p; + for (const char* dyldEnvVar : sEnvVarsToCheck) { + size_t dyldEnvVarLen = strlen(dyldEnvVar); + if ( (strncmp(dyldEnvVar, envToCheck, dyldEnvVarLen) == 0) && (envToCheck[dyldEnvVarLen] == '=') ) { + argv[i++] = "-env"; + argv[i++] = envToCheck; + } + } + } + argv[i] = nullptr; + //dyld::log("closured args:\n"); + //for (int j=0; argv[j] != nullptr; ++j) + // dyld::log(" argv[%d]=%s\n", j, argv[j]); + execve(closuredPath, (char**)argv, nullptr); + dyld::log("exec() of closured failed, errno=%d\n", errno); + } + return NULL; + +#else + // get port to closured + mach_port_t serverPort = dyld3::loader::lookupClosuredPort(); + if ( serverPort == MACH_PORT_NULL ) + return NULL; + + // build env var list + char envBuffer[2048]; + char* s = envBuffer; + for (const char* envVar : sEnvVarsToCheck) { + if ( const char* valueFromEnv = _simple_getenv(envp, envVar) ) { + strcpy(s, envVar); + strcat(s, "="); + strcat(s, valueFromEnv); + s += strlen(s)+1; + } + } + *s++ = '\0'; + + // get uuid of main executable + dyld3::MachOParser mainParser((mach_header*)sMainExecutableMachHeader); + uuid_t mainUuid; + mainParser.getUuid(mainUuid); + + // message closured to build closure + bool success = false; + vm_offset_t reply = 0; + uint32_t replySize = 0; + if ( closured_CreateLaunchClosure(serverPort, sExecPath, sSharedCachePath, mainUuid, envBuffer, &success, &reply, &replySize) != KERN_SUCCESS ) + return NULL; + + // release server port + mach_port_deallocate(mach_task_self(), serverPort); + + if ( success ) + return (const dyld3::launch_cache::BinaryClosureData*)reply; + + dyld::log("closure failed to build: %s\n", (char*)reply); + return NULL; +#endif +} +#endif // !TARGET_IPHONE_SIMULATOR + + +#if !__MAC_OS_X_VERSION_MIN_REQUIRED +static const char* sWhiteListDirs[] = { + "/bin/", + "/sbin/", + "/usr/bin/" +}; +#endif + +static bool inWhiteList(const char* execPath) +{ + // First test to see if we forced in dyld2 via a kernel boot-arg + if ( dyld3::loader::bootArgsContains("force_dyld2=1") ) + return false; + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + + // rdar://problem/32701418: Don't use dyld3 for i386 for now. +#if __i386__ + return false; +#else + + + return true; +#endif // #if __i386__ + +#else + // enable dyld3 mode for all OS programs when using customer dyld cache (no roots) + if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && (sSharedCacheLoadInfo.loadAddress->header.cacheType == kDyldSharedCacheTypeProduction) ) + return true; + + for (const char* dir : sWhiteListDirs) { + if ( strncmp(dir, sExecPath, strlen(dir)) == 0 ) { + return true; + } + } + return dyld3::loader::bootArgsContains("force_dyld3=1"); +#endif +} + +// +// Entry point for dyld. The kernel loads dyld and jumps to __dyld_start which +// sets up some registers and call this function. +// +// Returns address of main() in target program which __dyld_start jumps to +// +uintptr_t +_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, + int argc, const char* argv[], const char* envp[], const char* apple[], + uintptr_t* startGlue) +{ + dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_DYLD, 0, 0); + + // Grab the cdHash of the main executable from the environment + uint8_t mainExecutableCDHashBuffer[20]; + const uint8_t* mainExecutableCDHash = nullptr; + if ( hexToBytes(_simple_getenv(apple, "executable_cdhash"), 40, mainExecutableCDHashBuffer) ) + mainExecutableCDHash = mainExecutableCDHashBuffer; + + // Trace dyld's load + notifyKernelAboutImage((macho_header*)&__dso_handle, _simple_getenv(apple, "dyld_file")); +#if !TARGET_IPHONE_SIMULATOR + // Trace the main executable's load + notifyKernelAboutImage(mainExecutableMH, _simple_getenv(apple, "executable_file")); +#endif + + uintptr_t result = 0; + sMainExecutableMachHeader = mainExecutableMH; + sMainExecutableSlide = mainExecutableSlide; +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // if this is host dyld, check to see if iOS simulator is being run + const char* rootPath = _simple_getenv(envp, "DYLD_ROOT_PATH"); + if ( rootPath != NULL ) { + + // look to see if simulator has its own dyld + char simDyldPath[PATH_MAX]; + strlcpy(simDyldPath, rootPath, PATH_MAX); + strlcat(simDyldPath, "/usr/lib/dyld_sim", PATH_MAX); + int fd = my_open(simDyldPath, O_RDONLY, 0); + if ( fd != -1 ) { + const char* errMessage = useSimulatorDyld(fd, mainExecutableMH, simDyldPath, argc, argv, envp, apple, startGlue, &result); + if ( errMessage != NULL ) + halt(errMessage); + return result; + } + } +#endif + + CRSetCrashLogMessage("dyld: launch started"); + + setContext(mainExecutableMH, argc, argv, envp, apple); + + // Pickup the pointer to the exec path. + sExecPath = _simple_getenv(apple, "executable_path"); + + // Remove interim apple[0] transition code from dyld + if (!sExecPath) sExecPath = apple[0]; + + if ( sExecPath[0] != '/' ) { + // have relative path, use cwd to make absolute + char cwdbuff[MAXPATHLEN]; + if ( getcwd(cwdbuff, MAXPATHLEN) != NULL ) { + // maybe use static buffer to avoid calling malloc so early... + char* s = new char[strlen(cwdbuff) + strlen(sExecPath) + 2]; + strcpy(s, cwdbuff); + strcat(s, "/"); + strcat(s, sExecPath); + sExecPath = s; + } + } + + // Remember short name of process for later logging + sExecShortName = ::strrchr(sExecPath, '/'); + if ( sExecShortName != NULL ) + ++sExecShortName; + else + sExecShortName = sExecPath; + + configureProcessRestrictions(mainExecutableMH); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + if ( gLinkContext.processIsRestricted ) { + pruneEnvironmentVariables(envp, &apple); + // set again because envp and apple may have changed or moved + setContext(mainExecutableMH, argc, argv, envp, apple); + } + else +#endif + { + checkEnvironmentVariables(envp); + defaultUninitializedFallbackPaths(envp); + } + if ( sEnv.DYLD_PRINT_OPTS ) + printOptions(argv); + if ( sEnv.DYLD_PRINT_ENV ) + printEnvironmentVariables(envp); + getHostInfo(mainExecutableMH, mainExecutableSlide); + + // load shared cache + checkSharedRegionDisable((mach_header*)mainExecutableMH); +#if TARGET_IPHONE_SIMULATOR + // until is fixed + gLinkContext.sharedRegionMode = ImageLoader::kUsePrivateSharedRegion; + // +#endif + if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) { + mapSharedCache(); + } + + if ( (sEnableClosures || inWhiteList(sExecPath)) && (sSharedCacheLoadInfo.loadAddress != nullptr) ) { + if ( sSharedCacheLoadInfo.loadAddress->header.formatVersion == dyld3::launch_cache::binary_format::kFormatVersion ) { + const dyld3::launch_cache::BinaryClosureData* mainClosureData; + // check for closure in cache first + dyld3::DyldCacheParser cacheParser(sSharedCacheLoadInfo.loadAddress, false); + mainClosureData = cacheParser.findClosure(sExecPath); + #if __IPHONE_OS_VERSION_MIN_REQUIRED + if ( mainClosureData == nullptr ) { + // see if this is an OS app that was moved + if ( strncmp(sExecPath, "/var/containers/Bundle/Application/", 35) == 0 ) { + dyld3::MachOParser mainParser((mach_header*)mainExecutableMH); + uint32_t textOffset; + uint32_t textSize; + if ( !mainParser.isFairPlayEncrypted(textOffset, textSize) ) { + __block bool hasEmbeddedDylibs = false; + mainParser.forEachDependentDylib(^(const char* loadPath, bool, bool, bool, uint32_t, uint32_t, bool& stop) { + if ( loadPath[0] == '@' ) { + hasEmbeddedDylibs = true; + stop = true; + } + }); + if ( !hasEmbeddedDylibs ) { + char altPath[1024]; + const char* lastSlash = strrchr(sExecPath, '/'); + if ( lastSlash != nullptr ) { + strlcpy(altPath, "/private/var/staged_system_apps", sizeof(altPath)); + strlcat(altPath, lastSlash, sizeof(altPath)); + strlcat(altPath, ".app", sizeof(altPath)); + strlcat(altPath, lastSlash, sizeof(altPath)); + if ( gLinkContext.verboseWarnings ) + dyld::log("try path: %s\n", altPath); + mainClosureData = cacheParser.findClosure(altPath); + } + } + } + } + } + #endif + if ( gLinkContext.verboseWarnings && (mainClosureData != nullptr) ) + dyld::log("dyld: found closure %p in dyld shared cache\n", mainClosureData); + #if !TARGET_IPHONE_SIMULATOR + if ( (mainClosureData == nullptr) || !closureValid(mainClosureData, (mach_header*)mainExecutableMH, mainExecutableCDHash, true, envp) ) { + mainClosureData = nullptr; + if ( sEnableClosures ) { + // if forcing closures, and no closure in cache, or it is invalid, then RPC to closured + mainClosureData = callClosureDaemon(sExecPath, envp); + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: closured return %p for %s\n", mainClosureData, sExecPath); + if ( (mainClosureData != nullptr) && !closureValid(mainClosureData, (mach_header*)mainExecutableMH, mainExecutableCDHash, false, envp) ) { + // some how freshly generated closure is invalid... + mainClosureData = nullptr; + } + } + } + #endif + // try using launch closure + if ( mainClosureData != nullptr ) { + CRSetCrashLogMessage("dyld3: launch started"); + if ( launchWithClosure(mainClosureData, sSharedCacheLoadInfo.loadAddress, (mach_header*)mainExecutableMH, mainExecutableSlide, + argc, argv, envp, apple, &result, startGlue) ) { + if (sSkipMain) + result = (uintptr_t)&fake_main; + return result; + } + else { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: unable to use closure %p\n", mainClosureData); + } + } + } + else { + if ( gLinkContext.verboseWarnings ) + dyld::log("dyld: not using closure because shared cache format version does not match dyld's\n"); + } + // could not use closure info, launch old way + } + + + // 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 + sImageRoots.reserve(16); + sAddImageCallbacks.reserve(4); + sRemoveImageCallbacks.reserve(4); + sImageFilesNeedingTermination.reserve(16); + sImageFilesNeedingDOFUnregistration.reserve(8); + +#if !TARGET_IPHONE_SIMULATOR +#ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE + // Add gating mechanism to dyld support system order file generation process + WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag); +#endif +#endif + + + try { + // add dyld itself to UUID list + addDyldImageToUUIDList(); + +#if SUPPORT_ACCELERATE_TABLES + bool mainExcutableAlreadyRebased = false; + if ( (sSharedCacheLoadInfo.loadAddress != nullptr) && !dylibsCanOverrideCache() && !sDisableAcceleratorTables && (sSharedCacheLoadInfo.loadAddress->header.accelerateInfoAddr != 0) ) { + struct stat statBuf; + if ( ::stat(IPHONE_DYLD_SHARED_CACHE_DIR "no-dyld2-accelerator-tables", &statBuf) != 0 ) + sAllCacheImagesProxy = ImageLoaderMegaDylib::makeImageLoaderMegaDylib(&sSharedCacheLoadInfo.loadAddress->header, sSharedCacheLoadInfo.slide, mainExecutableMH, gLinkContext); + } + +reloadAllImages: +#endif + + CRSetCrashLogMessage(sLoadingCrashMessage); + // instantiate ImageLoader for main executable + sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath); + gLinkContext.mainExecutable = sMainExecutable; + gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH); + +#if TARGET_IPHONE_SIMULATOR + // check main executable is not too new for this OS + { + if ( ! isSimulatorBinary((uint8_t*)mainExecutableMH, sExecPath) ) { + throwf("program was built for a platform that is not supported by this runtime"); + } + uint32_t mainMinOS = sMainExecutable->minOSVersion(); + + // dyld is always built for the current OS, so we can get the current OS version + // from the load command in dyld itself. + uint32_t dyldMinOS = ImageLoaderMachO::minOSVersion((const mach_header*)&__dso_handle); + if ( mainMinOS > dyldMinOS ) { + #if TARGET_OS_WATCH + throwf("app was built for watchOS %d.%d which is newer than this simulator %d.%d", + mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), + dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); + #elif TARGET_OS_TV + throwf("app was built for tvOS %d.%d which is newer than this simulator %d.%d", + mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), + dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); + #else + throwf("app was built for iOS %d.%d which is newer than this simulator %d.%d", + mainMinOS >> 16, ((mainMinOS >> 8) & 0xFF), + dyldMinOS >> 16, ((dyldMinOS >> 8) & 0xFF)); + #endif + } + } +#endif + + + #if __MAC_OS_X_VERSION_MIN_REQUIRED + // be less strict about old mach-o binaries + uint32_t mainSDK = sMainExecutable->sdkVersion(); + gLinkContext.strictMachORequired = (mainSDK >= DYLD_MACOSX_VERSION_10_12) || gLinkContext.processUsingLibraryValidation; + #else + // simulators, iOS, tvOS, and watchOS are always strict + gLinkContext.strictMachORequired = true; + #endif + + #if SUPPORT_ACCELERATE_TABLES + sAllImages.reserve((sAllCacheImagesProxy != NULL) ? 16 : INITIAL_IMAGE_COUNT); + #else + sAllImages.reserve(INITIAL_IMAGE_COUNT); + #endif + + // Now that shared cache is loaded, setup an versioned dylib overrides + #if SUPPORT_VERSIONED_PATHS + checkVersionedPaths(); + #endif + + + // dyld_all_image_infos image list does not contain dyld + // add it as dyldPath field in dyld_all_image_infos + // for simulator, dyld_sim is in image list, need host dyld added +#if TARGET_IPHONE_SIMULATOR + // get path of host dyld from table of syscall vectors in host dyld + void* addressInDyld = gSyscallHelpers; +#else + // get path of dyld itself + void* addressInDyld = (void*)&__dso_handle; +#endif + char dyldPathBuffer[MAXPATHLEN+1]; + int len = proc_regionfilename(getpid(), (uint64_t)(long)addressInDyld, dyldPathBuffer, MAXPATHLEN); + if ( len > 0 ) { + dyldPathBuffer[len] = '\0'; // proc_regionfilename() does not zero terminate returned string + if ( strcmp(dyldPathBuffer, gProcessInfo->dyldPath) != 0 ) + gProcessInfo->dyldPath = strdup(dyldPathBuffer); + } + + // load any inserted libraries + if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) { + for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib) + loadInsertedDylib(*lib); + } + // record count of inserted libraries so that a flat search will look at + // inserted libraries, then main, then others. + sInsertedDylibCount = sAllImages.size()-1; + + // link main executable + gLinkContext.linkingMainExecutable = true; +#if SUPPORT_ACCELERATE_TABLES + if ( mainExcutableAlreadyRebased ) { + // previous link() on main executable has already adjusted its internal pointers for ASLR + // work around that by rebasing by inverse amount + sMainExecutable->rebase(gLinkContext, -mainExecutableSlide); + } +#endif + link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); + sMainExecutable->setNeverUnloadRecursive(); + if ( sMainExecutable->forceFlat() ) { + gLinkContext.bindFlat = true; + gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding; + } + + // link any inserted libraries + // do this after linking main executable so that any dylibs pulled in by inserted + // dylibs (e.g. libSystem) will not be in front of dylibs the program uses + if ( sInsertedDylibCount > 0 ) { + for(unsigned int i=0; i < sInsertedDylibCount; ++i) { + ImageLoader* image = sAllImages[i+1]; + link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1); + image->setNeverUnloadRecursive(); + } + // only INSERTED libraries can interpose + // register interposing info after all inserted libraries are bound so chaining works + for(unsigned int i=0; i < sInsertedDylibCount; ++i) { + ImageLoader* image = sAllImages[i+1]; + image->registerInterposing(); + } + } + + // dyld should support interposition even without DYLD_INSERT_LIBRARIES + for (long i=sInsertedDylibCount+1; i < sAllImages.size(); ++i) { + ImageLoader* image = sAllImages[i]; + if ( image->inSharedCache() ) + continue; + image->registerInterposing(); + } + #if SUPPORT_ACCELERATE_TABLES + if ( (sAllCacheImagesProxy != NULL) && ImageLoader::haveInterposingTuples() ) { + // Accelerator tables cannot be used with implicit interposing, so relaunch with accelerator tables disabled + ImageLoader::clearInterposingTuples(); + // unmap all loaded dylibs (but not main executable) + for (long i=1; i < sAllImages.size(); ++i) { + ImageLoader* image = sAllImages[i]; + if ( image == sMainExecutable ) + continue; + if ( image == sAllCacheImagesProxy ) + continue; + image->setCanUnload(); + ImageLoader::deleteImage(image); + } + // note: we don't need to worry about inserted images because if DYLD_INSERT_LIBRARIES was set we would not be using the accelerator table + sAllImages.clear(); + sImageRoots.clear(); + sImageFilesNeedingTermination.clear(); + sImageFilesNeedingDOFUnregistration.clear(); + sAddImageCallbacks.clear(); + sRemoveImageCallbacks.clear(); + sDisableAcceleratorTables = true; + sAllCacheImagesProxy = NULL; + sMappedRangesStart = NULL; + mainExcutableAlreadyRebased = true; + gLinkContext.linkingMainExecutable = false; + resetAllImages(); + goto reloadAllImages; + } + #endif + + // apply interposing to initial set of images + for(int i=0; i < sImageRoots.size(); ++i) { + sImageRoots[i]->applyInterposing(gLinkContext); + } + gLinkContext.linkingMainExecutable = false; + + // do weak binding only after all inserted images linked + sMainExecutable->weakBind(gLinkContext); + + // If cache has branch island dylibs, tell debugger about them + if ( (sSharedCacheLoadInfo.loadAddress != NULL) && (sSharedCacheLoadInfo.loadAddress->header.mappingOffset >= 0x78) && (sSharedCacheLoadInfo.loadAddress->header.branchPoolsOffset != 0) ) { + uint32_t count = sSharedCacheLoadInfo.loadAddress->header.branchPoolsCount; + dyld_image_info info[count]; + const uint64_t* poolAddress = (uint64_t*)((char*)sSharedCacheLoadInfo.loadAddress + sSharedCacheLoadInfo.loadAddress->header.branchPoolsOffset); + // empty branch pools can be in development cache + if ( ((mach_header*)poolAddress)->magic == sMainExecutableMachHeader->magic ) { + for (int poolIndex=0; poolIndex < count; ++poolIndex) { + uint64_t poolAddr = poolAddress[poolIndex] + sSharedCacheLoadInfo.slide; + info[poolIndex].imageLoadAddress = (mach_header*)(long)poolAddr; + info[poolIndex].imageFilePath = "dyld_shared_cache_branch_islands"; + info[poolIndex].imageFileModDate = 0; + } + // add to all_images list + addImagesToAllImages(count, info); + // tell gdb about new branch island images + gProcessInfo->notification(dyld_image_adding, count, info); + } + } + + 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(); + #else + // run all initializers + initializeMainExecutable(); + #endif + + // notify any montoring proccesses that this process is about to enter main() + dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN_DYLD2, 0, 0); + notifyMonitoringDyldMain(); + + // find entry point for main executable + result = (uintptr_t)sMainExecutable->getThreadPC(); + if ( result != 0 ) { + // main executable uses LC_MAIN, needs to return to glue in libdyld.dylib + if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) ) + *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit; + else + halt("libdyld.dylib support not present for LC_MAIN"); + } + else { + // main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main() + result = (uintptr_t)sMainExecutable->getMain(); + *startGlue = 0; + } + } + catch(const char* message) { + syncAllImages(); + halt(message); + } + catch(...) { + dyld::log("dyld: launch failed\n"); + } + + CRSetCrashLogMessage(NULL); + + if (sSkipMain) { + dyld3::kdebug_trace_dyld_signpost(DBG_DYLD_SIGNPOST_START_MAIN, 0, 0); + result = (uintptr_t)&fake_main; + *startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit; + } + + return result; +} + + +} // namespace + + + diff --git a/dyld/src/dyld.exp b/dyld/src/dyld.exp new file mode 100644 index 0000000..59a6660 --- /dev/null +++ b/dyld/src/dyld.exp @@ -0,0 +1,39 @@ +# +# Copyright (c) 2004-2008 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +# + +# +# Only the following symbols should be "global". +# gdb and Symbolication lookup these symbols in dyld in order to determine what images the process is using +# + +# gdb and Symbolication look at these to discover a process's loaded images +_dyld_all_image_infos +__dyld_debugger_notification + +# CrashReporter uses this to get message as to why dyld terminated the process +_error_string + +# Used by various tools to see build number of dyld +_dyldVersionString +_dyldVersionNumber + diff --git a/dyld/src/dyld.h b/dyld/src/dyld.h new file mode 100644 index 0000000..de649ab --- /dev/null +++ b/dyld/src/dyld.h @@ -0,0 +1,137 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + +#include "ImageLoader.h" +#include "mach-o/dyld_priv.h" + + + +// +// dyld functions available when implementing dyld API's +// +// +namespace dyld { + + struct LoadContext + { + bool useSearchPaths; + bool useFallbackPaths; + bool useLdLibraryPath; + bool implicitRPath; + bool matchByInstallName; + bool dontLoad; + bool mustBeBundle; + bool mustBeDylib; + bool canBePIE; + const char* origin; // path for expanding @loader_path + const ImageLoader::RPathChain* rpath; // paths for expanding @rpath + }; + + + + typedef void (*ImageCallback)(const struct mach_header* mh, intptr_t slide); + typedef void (*UndefinedHandler)(const char* symbolName); + typedef const char* (*ImageLocator)(const char* dllName); + + + extern ImageLoader::LinkContext gLinkContext; + extern struct dyld_all_image_infos* gProcessInfo; + extern bool gLogAPIs; +#if SUPPORT_ACCELERATE_TABLES + extern bool gLogAppAPIs; +#endif + extern bool gSharedCacheOverridden; + extern const struct LibSystemHelpers* gLibSystemHelpers; +#if SUPPORT_OLD_CRT_INITIALIZATION + extern bool gRunInitializersOldWay; +#endif + extern void registerAddCallback(ImageCallback func); + extern void registerRemoveCallback(ImageCallback func); + extern void registerUndefinedHandler(UndefinedHandler); + extern void initializeMainExecutable(); + extern void preflight(ImageLoader* image, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex); + extern void link(ImageLoader* image, bool forceLazysBound, bool neverUnload, const ImageLoader::RPathChain& loaderRPaths, unsigned cacheIndex); + extern void runInitializers(ImageLoader* image); + extern void runImageStaticTerminators(ImageLoader* image); + extern const char* getExecutablePath(); + extern bool validImage(const ImageLoader*); + extern ImageLoader* getIndexedImage(uint32_t index); + extern uint32_t getImageCount(); + extern ImageLoader* findImageByMachHeader(const struct mach_header* target); + extern ImageLoader* findImageContainingAddress(const void* addr); + extern ImageLoader* findImageContainingSymbol(const void* symbol); + extern ImageLoader* findImageByName(const char* path); + extern ImageLoader* findLoadedImageByInstallPath(const char* path); + extern bool flatFindExportedSymbol(const char* name, const ImageLoader::Symbol** sym, const ImageLoader** image); + extern bool flatFindExportedSymbolWithHint(const char* name, const char* librarySubstring, const ImageLoader::Symbol** sym, const ImageLoader** image); + extern ImageLoader* load(const char* path, const LoadContext& context, unsigned& cacheIndex); + extern ImageLoader* loadFromMemory(const uint8_t* mem, uint64_t len, const char* moduleName); + extern void removeImage(ImageLoader* image); + extern ImageLoader* cloneImage(ImageLoader* image); + extern void forEachImageDo( void (*)(ImageLoader*, void*), void*); + extern uintptr_t _main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide, int argc, const char* argv[], const char* envp[], + const char* apple[], uintptr_t* startGlue) __attribute__((noinline)); // +// +// + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include +#include +#include +#include // for task_self_trap() + + +#include "mach-o/dyld_images.h" +#include "mach-o/dyld.h" +#include "mach-o/dyld_priv.h" + +#include "ImageLoader.h" +#include "ImageLoaderMachO.h" +#include "dyld.h" +#include "dyldLibSystemInterface.h" +#include "DyldSharedCache.h" + +#undef _POSIX_C_SOURCE +#include "dlfcn.h" + + +// this was in dyld_priv.h but it is no longer exported +extern "C" { + const struct dyld_all_image_infos* _dyld_get_all_image_infos(); +} + +// 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[]); +extern uint32_t allImagesCount(); +extern const mach_header* allImagesIndexedMachHeader(uint32_t index); +extern const char* allImagesIndexedPath(uint32_t index); + +extern "C" int _dyld_func_lookup(const char* name, void** address); + +// deprecated APIs are still availble on Mac OS X, but not on iPhone OS +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define DEPRECATED_APIS_SUPPORTED 0 +#else + #define DEPRECATED_APIS_SUPPORTED 1 +#endif + +static bool sDynamicInterposing = false; + +#if DEPRECATED_APIS_SUPPORTED +static char sLastErrorFilePath[1024]; +static NSLinkEditErrors sLastErrorFileCode; +static int sLastErrorNo; +#endif + +// In 10.3.x and earlier all the NSObjectFileImage API's were implemeneted in libSystem.dylib +// Beginning in 10.4 the NSObjectFileImage API's are implemented in dyld and libSystem just forwards +// This conditional keeps support for old libSystem's which needed some help implementing the API's +#define OLD_LIBSYSTEM_SUPPORT (__i386__) + +// The following functions have no prototype in any header. They are special cases +// where _dyld_func_lookup() is used directly. +static void _dyld_make_delayed_module_initializer_calls(); +static void registerThreadHelpers(const dyld::LibSystemHelpers*); +#if DEPRECATED_APIS_SUPPORTED +static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit); +#if OLD_LIBSYSTEM_SUPPORT +static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options); +#endif +static void _dyld_register_binding_handler(void * (*)(const char *, const char *, void *), ImageLoader::BindingOptions); +static bool NSMakePrivateModulePublic(NSModule module); +static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header); + +// The following functions are dyld API's, but since dyld links with a static copy of libc.a +// the public name cannot be used. +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 SUPPORT_ZERO_COST_EXCEPTIONS +static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info); +#endif +#if DEPRECATED_APIS_SUPPORTED +#endif + +static void unimplemented() +{ + dyld::halt("unimplemented dyld function\n"); +} + +struct dyld_func { + const char* name; + void* implementation; +}; + +static struct dyld_func dyld_funcs[] = { + {"__dyld_register_func_for_add_image", (void*)_dyld_register_func_for_add_image }, + {"__dyld_register_func_for_remove_image", (void*)_dyld_register_func_for_remove_image }, + {"__dyld_dladdr", (void*)dladdr }, + {"__dyld_dlclose", (void*)dlclose }, + {"__dyld_dlerror", (void*)dlerror }, + {"__dyld_dlopen", (void*)dlopen }, + {"__dyld_dlsym", (void*)dlsym }, + {"__dyld_dlopen_preflight", (void*)dlopen_preflight }, + {"__dyld_image_count", (void*)_dyld_image_count }, + {"__dyld_get_image_header", (void*)_dyld_get_image_header }, + {"__dyld_get_image_vmaddr_slide", (void*)_dyld_get_image_vmaddr_slide }, + {"__dyld_get_image_name", (void*)_dyld_get_image_name }, + {"__dyld_get_image_slide", (void*)_dyld_get_image_slide }, + {"__dyld__NSGetExecutablePath", (void*)_NSGetExecutablePath }, + + // SPIs + {"__dyld_register_thread_helpers", (void*)registerThreadHelpers }, + {"__dyld_fork_child", (void*)_dyld_fork_child }, + {"__dyld_make_delayed_module_initializer_calls", (void*)_dyld_make_delayed_module_initializer_calls }, + {"__dyld_get_all_image_infos", (void*)_dyld_get_all_image_infos }, +#if SUPPORT_ZERO_COST_EXCEPTIONS + {"__dyld_find_unwind_sections", (void*)client_dyld_find_unwind_sections }, +#endif +#if __i386__ || __x86_64__ || __arm__ || __arm64__ + {"__dyld_fast_stub_entry", (void*)dyld::fastBindLazySymbol }, +#endif + {"__dyld_image_path_containing_address", (void*)dyld_image_path_containing_address }, + {"__dyld_shared_cache_some_image_overridden", (void*)dyld_shared_cache_some_image_overridden }, + {"__dyld_process_is_restricted", (void*)dyld::processIsRestricted }, + {"__dyld_dynamic_interpose", (void*)dyld_dynamic_interpose }, + {"__dyld_shared_cache_file_path", (void*)dyld::getStandardSharedCacheFilePath }, + {"__dyld_get_image_header_containing_address", (void*)dyld_image_header_containing_address }, + {"__dyld_is_memory_immutable", (void*)_dyld_is_memory_immutable }, + {"__dyld_objc_notify_register", (void*)_dyld_objc_notify_register }, + {"__dyld_get_shared_cache_uuid", (void*)_dyld_get_shared_cache_uuid }, + {"__dyld_get_shared_cache_range", (void*)_dyld_get_shared_cache_range }, + + + // deprecated +#if DEPRECATED_APIS_SUPPORTED + {"__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 }, + {"__dyld_install_handlers", (void*)_dyld_install_handlers }, + {"__dyld_link_edit_error", (void*)NSLinkEditError }, + {"__dyld_unlink_module", (void*)NSUnLinkModule }, + {"__dyld_bind_fully_image_containing_address", (void*)_dyld_bind_fully_image_containing_address }, + {"__dyld_image_containing_address", (void*)_dyld_image_containing_address }, + {"__dyld_register_binding_handler", (void*)_dyld_register_binding_handler }, + {"__dyld_NSNameOfSymbol", (void*)NSNameOfSymbol }, + {"__dyld_NSAddressOfSymbol", (void*)NSAddressOfSymbol }, + {"__dyld_NSModuleForSymbol", (void*)NSModuleForSymbol }, + {"__dyld_NSLookupAndBindSymbol", (void*)NSLookupAndBindSymbol }, + {"__dyld_NSLookupAndBindSymbolWithHint", (void*)NSLookupAndBindSymbolWithHint }, + {"__dyld_NSLookupSymbolInModule", (void*)NSLookupSymbolInModule}, + {"__dyld_NSLookupSymbolInImage", (void*)NSLookupSymbolInImage}, + {"__dyld_NSMakePrivateModulePublic", (void*)NSMakePrivateModulePublic}, + {"__dyld_NSIsSymbolNameDefined", (void*)client_NSIsSymbolNameDefined}, + {"__dyld_NSIsSymbolNameDefinedWithHint", (void*)NSIsSymbolNameDefinedWithHint }, + {"__dyld_NSIsSymbolNameDefinedInImage", (void*)NSIsSymbolNameDefinedInImage}, + {"__dyld_NSNameOfModule", (void*)NSNameOfModule }, + {"__dyld_NSLibraryNameForModule", (void*)NSLibraryNameForModule }, + {"__dyld_NSAddLibrary", (void*)NSAddLibrary }, + {"__dyld_NSAddLibraryWithSearching", (void*)NSAddLibraryWithSearching }, + {"__dyld_NSAddImage", (void*)NSAddImage }, + {"__dyld_launched_prebound", (void*)_dyld_launched_prebound }, + {"__dyld_all_twolevel_modules_prebound", (void*)_dyld_all_twolevel_modules_prebound }, + {"__dyld_call_module_initializers_for_dylib", (void*)_dyld_call_module_initializers_for_dylib }, + {"__dyld_NSCreateObjectFileImageFromFile", (void*)NSCreateObjectFileImageFromFile }, + {"__dyld_NSCreateObjectFileImageFromMemory", (void*)NSCreateObjectFileImageFromMemory }, + {"__dyld_NSDestroyObjectFileImage", (void*)NSDestroyObjectFileImage }, + {"__dyld_NSLinkModule", (void*)NSLinkModule }, + {"__dyld_NSSymbolDefinitionCountInObjectFileImage", (void*)NSSymbolDefinitionCountInObjectFileImage }, + {"__dyld_NSSymbolDefinitionNameInObjectFileImage", (void*)NSSymbolDefinitionNameInObjectFileImage }, + {"__dyld_NSIsSymbolDefinedInObjectFileImage", (void*)NSIsSymbolDefinedInObjectFileImage }, + {"__dyld_NSSymbolReferenceNameInObjectFileImage", (void*)NSSymbolReferenceNameInObjectFileImage }, + {"__dyld_NSSymbolReferenceCountInObjectFileImage", (void*)NSSymbolReferenceCountInObjectFileImage }, + {"__dyld_NSGetSectionDataInObjectFileImage", (void*)NSGetSectionDataInObjectFileImage }, +#if OLD_LIBSYSTEM_SUPPORT + {"__dyld_link_module", (void*)_dyld_link_module }, +#endif +#endif //DEPRECATED_APIS_SUPPORTED + + {NULL, 0} +}; + + + +#if DEPRECATED_APIS_SUPPORTED + +static void dyldAPIhalt(const char* apiName, const char* errorMsg) +{ + dyld::log("dyld: %s() error\n", apiName); + dyld::halt(errorMsg); +} + +// dyld's abstract type NSSymbol is implemented as const ImageLoader::Symbol* +inline NSSymbol SymbolToNSSymbol(const ImageLoader::Symbol* sym) +{ + return (NSSymbol)sym; +} +inline const ImageLoader::Symbol* NSSymbolToSymbol(NSSymbol sym) +{ + return (const ImageLoader::Symbol*)sym; +} + +// dyld's abstract type NSModule is implemented as ImageLoader* +inline NSModule ImageLoaderToNSModule(const ImageLoader* image) +{ + return (NSModule)image; +} +inline ImageLoader* NSModuleToImageLoader(NSModule module) +{ + ImageLoader* image = (ImageLoader*)module; + if ( dyld::validImage(image) ) + return image; + return NULL; +} + +// actual definition for opaque type +struct __NSObjectFileImage +{ + ImageLoader* image; + const void* imageBaseAddress; // not used with OFI created from files + size_t imageLength; // not used with OFI created from files +}; + + +VECTOR_NEVER_DESTRUCTED(NSObjectFileImage); +static std::vector sObjectFileImages; + + + +// +// __NSObjectFileImage are deleted in NSDestroyObjectFileImage() +// The contained image is delete in one of two places: +// NSUnLinkModule deletes the image if there is no __NSObjectFileImage with a reference to it +// NSDestroyObjectFileImage deletes the image if image is not in list of valid images +// + + + +static void setLastError(NSLinkEditErrors code, int errnum, const char* file, const char* message) +{ + dyld::setErrorMessage(message); + strncpy(sLastErrorFilePath, file, 1024); + sLastErrorFilePath[1023] = '\0'; + sLastErrorFileCode = code; + sLastErrorNo = errnum; +} + +#endif // DEPRECATED_APIS_SUPPORTED + +/* + *_dyld_NSGetExecutablePath is the dyld side of _NSGetExecutablePath which + * copies the path of the executable into the buffer and returns 0 if the path + * was successfully copied in the provided buffer. If the buffer is not large + * enough, -1 is returned and the expected buffer size is copied in *bufsize. + * Note that _NSGetExecutablePath will return "a path" to the executable not a + * "real path" to the executable. That is the path may be a symbolic link and + * not the real file. And with deep directories the total bufsize needed could + * be more than MAXPATHLEN. + */ +int _NSGetExecutablePath(char* buf, uint32_t *bufsize) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(...)\n", __func__); + const char* exePath = dyld::getExecutablePath(); + if(*bufsize < strlen(exePath) + 1){ + *bufsize = (uint32_t)(strlen(exePath) + 1); + return -1; + } + strcpy(buf, exePath); + return 0; +} + +uint32_t _dyld_image_count(void) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + return allImagesCount(); +} + +const struct mach_header* _dyld_get_image_header(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + return allImagesIndexedMachHeader(image_index); +} + +intptr_t _dyld_get_image_vmaddr_slide(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + const struct mach_header* mh = allImagesIndexedMachHeader(image_index); + if ( mh != NULL ) + return ImageLoaderMachO::computeSlide(mh); + else + return 0; +} + +intptr_t _dyld_get_image_slide(const struct mach_header* mh) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, mh); + return ImageLoaderMachO::computeSlide(mh); +} + + +const char* _dyld_get_image_name(uint32_t image_index) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%u)\n", __func__, image_index); + return allImagesIndexedPath(image_index); +} + +const struct mach_header * dyld_image_header_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + if ( dyld::addressInCache(address, &mh, &path) ) + return mh; +#endif + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) + return image->machHeader(); + return NULL; +} + + +void _dyld_register_func_for_add_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)func); + dyld::registerAddCallback(func); +} + +void _dyld_register_func_for_remove_image(void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)func); + dyld::registerRemoveCallback(func); +} + + + +// called by crt before main() by programs linked with 10.4 or earlier crt1.o +static void _dyld_make_delayed_module_initializer_calls() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + +#if SUPPORT_OLD_CRT_INITIALIZATION + if ( dyld::gRunInitializersOldWay ) + dyld::initializeMainExecutable(); +#endif +} + + + +#if DEPRECATED_APIS_SUPPORTED + +// +// _dyld_call_module_initializers_for_dylib() is the dyld side of +// __initialize_Cplusplus() which is in dylib1.o. +// It is intended to only be called inside -init rouintes. +// -init routines are called before module initializers (what C++ +// initializers use). Calling __initialize_Cplusplus() in a -init +// routine causes the module initializers for an image to be called +// which then allows C++ to be used inside a -init routine +// +static void _dyld_call_module_initializers_for_dylib(const struct mach_header* mh_dylib_header) +{ + if ( dyld::gLogAPIs ) + dyld::log("__initialize_Cplusplus()\n"); + + // for now, do nothing... +} + + +void _dyld_lookup_and_bind_fully(const char* symbolName, void** address, NSModule* module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", %p, %p)\n", __func__, symbolName, address, module); + ImageLoader* image; + const ImageLoader::Symbol* sym; + dyld::clearErrorMessage(); + if ( dyld::flatFindExportedSymbol(symbolName, &sym, (const ImageLoader**)&image) ) { + try { + image->bindAllLazyPointers(dyld::gLinkContext, true); + if ( address != NULL) + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + if ( module != NULL) + *module = ImageLoaderToNSModule(image); + } + catch (const char* msg) { + dyldAPIhalt(__func__, msg); + } + } + else { + // on failure to find symbol return NULLs + if ( address != NULL) + *address = NULL; + if ( module != NULL) + *module = NULL; + } +} + +// Note: This cannot have public name because dyld is built with a static copy of libc.a +// which calls dyld_lookup_and_bind() and expects to find dyld's symbols not host process +static void client_dyld_lookup_and_bind(const char* symbolName, void** address, NSModule* module) +{ + if ( dyld::gLogAPIs ) + dyld::log("_dyld_lookup_and_bind(\"%s\", %p, %p)\n", symbolName, address, module); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { + if ( address != NULL) + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + if ( module != NULL) + *module = ImageLoaderToNSModule(image); + } + else { + // on failure to find symbol return NULLs + if ( address != NULL) + *address = NULL; + if ( module != NULL) + *module = NULL; + } +} + +void _dyld_lookup_and_bind_with_hint(const char* symbolName, const char* library_name_hint, void** address, NSModule* module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\", %p, %p)\n", __func__, symbolName, library_name_hint, address, module); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + // Look for library whose path contains the hint. If that fails search everywhere + if ( dyld::flatFindExportedSymbolWithHint(symbolName, library_name_hint, &sym, &image) + || dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { + if ( address != NULL) + *address = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext); + if ( module != NULL) + *module = ImageLoaderToNSModule(image); + } + else { + // on failure to find symbol return NULLs + if ( address != NULL) + *address = NULL; + if ( module != NULL) + *module = NULL; + } +} + + +NSSymbol NSLookupAndBindSymbol(const char *symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\")\n", __func__, symbolName); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + if ( dyld::flatFindExportedSymbol(symbolName, &sym, &image) ) { + return SymbolToNSSymbol(sym); + } + // return NULL on failure + return NULL; +} + +NSSymbol NSLookupAndBindSymbolWithHint(const char* symbolName, const char* libraryNameHint) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); + if ( ! found ) { + // hint failed, do slow search of all images + found = dyld::flatFindExportedSymbol(symbolName, &sym, &image); + } + if ( found ) + return SymbolToNSSymbol(sym); + + // return NULL on failure and log + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\") => NULL \n", __func__, symbolName, libraryNameHint); + return NULL; +} + + + + +static __attribute__((noinline)) +const struct mach_header* addImage(void* callerAddress, const char* path, bool search, bool dontLoad, bool matchInstallName, bool abortOnError) +{ + ImageLoader* image = NULL; + std::vector rpathsFromCallerImage; + try { + dyld::clearErrorMessage(); + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + // like dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) { + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + dyld::LoadContext context; + context.useSearchPaths = search; + context.useFallbackPaths = search; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = matchInstallName; + context.dontLoad = dontLoad; + context.mustBeBundle = false; + context.mustBeDylib = true; + context.canBePIE = false; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = &callersRPaths; // rpaths from caller and main executable + + unsigned cacheIndex; + image = load(path, context, cacheIndex); + if ( image != NULL ) { + if ( context.matchByInstallName ) + image->setMatchInstallPath(true); + dyld::link(image, false, false, callersRPaths, cacheIndex); + dyld::runInitializers(image); + // images added with NSAddImage() can never be unloaded + image->setNeverUnload(); + } + } + catch (const char* msg) { + dyld::garbageCollectImages(); + if ( abortOnError) { + char pathMsg[strlen(msg)+strlen(path)+4]; + strcpy(pathMsg, msg); + strcat(pathMsg, " "); + strcat(pathMsg, path); + dyldAPIhalt("NSAddImage", pathMsg); + } + // not halting, so set error state for NSLinkEditError to find + setLastError(NSLinkEditOtherError, 0, path, msg); + free((void*)msg); // our free() will do nothing if msg is a string literal + image = NULL; + } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + if ( image == NULL ) + return NULL; + else + return image->machHeader(); +} + + +const struct mach_header* NSAddImage(const char* path, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", 0x%08X)\n", __func__, path, options); + const bool dontLoad = ( (options & NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED) != 0 ); + const bool search = ( (options & NSADDIMAGE_OPTION_WITH_SEARCHING) != 0 ); + const bool matchInstallName = ( (options & NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME) != 0 ); + const bool abortOnError = ( (options & (NSADDIMAGE_OPTION_RETURN_ON_ERROR|NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED)) == 0 ); + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + return addImage(callerAddress, path, search, dontLoad, matchInstallName, abortOnError); +} + +bool NSAddLibrary(const char* path) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\")\n", __func__, path); + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + return (addImage(callerAddress, path, false, false, false, false) != NULL); +} + +bool NSAddLibraryWithSearching(const char* path) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\")\n", __func__, path); + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + return (addImage(callerAddress, path, true, false, false, false) != NULL); +} + + + +//#define NSADDIMAGE_OPTION_NONE 0x0 +//#define NSADDIMAGE_OPTION_RETURN_ON_ERROR 0x1 +//#define NSADDIMAGE_OPTION_MATCH_FILENAME_BY_INSTALLNAME 0x8 + +bool NSIsSymbolNameDefinedInImage(const struct mach_header* mh, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\")\n", __func__, (void *)mh, symbolName); + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image != NULL ) { + if ( image->findExportedSymbol(symbolName, true, NULL) != NULL) + return true; + } + return false; +} + + +NSSymbol NSLookupSymbolInImage(const struct mach_header* mh, const char* symbolName, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, mh, symbolName, options); + const ImageLoader::Symbol* symbol = NULL; + dyld::clearErrorMessage(); + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image != NULL ) { + try { + if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_FULLY ) { + image->bindAllLazyPointers(dyld::gLinkContext, true); + } + else if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW ) { + image->bindAllLazyPointers(dyld::gLinkContext, false); + } + } + catch (const char* msg) { + if ( (options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR) == 0 ) { + dyldAPIhalt(__func__, msg); + } + } + symbol = image->findExportedSymbol(symbolName, true, NULL); + } + if ( dyld::gLogAPIs && (symbol == NULL) ) + dyld::log("%s(%p, \"%s\", 0x%08X) ==> NULL\n", __func__, mh, symbolName, options); + return SymbolToNSSymbol(symbol); +} + + +// Note: This cannot have public name because dyld is built with a static copy of libc.a +// which calls NSIsSymbolNameDefined() and expects to find dyld's symbols not host process +static bool client_NSIsSymbolNameDefined(const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("NSIsSymbolNameDefined(\"%s\")\n", symbolName); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + return dyld::flatFindExportedSymbol(symbolName, &sym, &image); +} + +bool NSIsSymbolNameDefinedWithHint(const char* symbolName, const char* libraryNameHint) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\")\n", __func__, symbolName, libraryNameHint); + const ImageLoader* image; + const ImageLoader::Symbol* sym; + bool found = dyld::flatFindExportedSymbolWithHint(symbolName, libraryNameHint, &sym, &image); + if ( ! found ) { + // hint failed, do slow search of all images + found = dyld::flatFindExportedSymbol(symbolName, &sym, &image); + } + if ( !found && dyld::gLogAPIs ) + dyld::log("%s(\"%s\", \"%s\") => false \n", __func__, symbolName, libraryNameHint); + return found; +} + +const char* NSNameOfSymbol(NSSymbol symbol) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)symbol); + const char* result = NULL; + ImageLoader* image = dyld::findImageContainingSymbol(symbol); + if ( image != NULL ) + result = image->getExportedSymbolName(NSSymbolToSymbol(symbol)); + return result; +} + +void* NSAddressOfSymbol(NSSymbol symbol) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)symbol); + if ( symbol == NULL ) + return NULL; + void* result = NULL; + ImageLoader* image = dyld::findImageContainingSymbol(symbol); + if ( image != NULL ) + result = (void*)image->getExportedSymbolAddress(NSSymbolToSymbol(symbol), dyld::gLinkContext); + return result; +} + +NSModule NSModuleForSymbol(NSSymbol symbol) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, (void *)symbol); + NSModule result = NULL; + ImageLoader* image = dyld::findImageContainingSymbol(symbol); + if ( image != NULL ) + result = ImageLoaderToNSModule(image); + return result; +} + + + + +bool _dyld_all_twolevel_modules_prebound(void) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + return FALSE; +} + +bool _dyld_bind_fully_image_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); + dyld::clearErrorMessage(); + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) { + try { + image->bindAllLazyPointers(dyld::gLinkContext, true); + return true; + } + catch (const char* msg) { + dyldAPIhalt(__func__, msg); + } + } + return false; +} + +bool _dyld_image_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); + ImageLoader *imageLoader = dyld::findImageContainingAddress(address); + return (NULL != imageLoader); +} + +static NSObjectFileImage createObjectImageFile(ImageLoader* image, const void* address = NULL, size_t len=0) +{ + NSObjectFileImage result = new __NSObjectFileImage(); + result->image = image; + result->imageBaseAddress = address; + result->imageLength = len; + sObjectFileImages.push_back(result); + return result; +} + +NSObjectFileImageReturnCode NSCreateObjectFileImageFromFile(const char* pathName, NSObjectFileImage *objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(\"%s\", ...)\n", __func__, pathName); + try { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + + dyld::LoadContext context; + context.useSearchPaths = false; + context.useFallbackPaths = false; + context.useLdLibraryPath = false; + context.implicitRPath = false; + context.matchByInstallName = false; + context.dontLoad = false; + context.mustBeBundle = true; + context.mustBeDylib = false; + context.canBePIE = false; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = NULL; // support not yet implemented + + unsigned cacheIndex; + ImageLoader* image = dyld::load(pathName, context, cacheIndex); + // Note: We DO NOT link the image! NSLinkModule will do that + if ( image != NULL ) { + if ( !image->isBundle() ) { + // the image must have been already loaded (since context.mustBeBundle will prevent it from being loaded) + return NSObjectFileImageInappropriateFile; + } + *objectFileImage = createObjectImageFile(image); + return NSObjectFileImageSuccess; + } + } + catch (const char* msg) { + //dyld::log("dyld: NSCreateObjectFileImageFromFile() error: %s\n", msg); + dyld::garbageCollectImages(); + free((void*)msg); + return NSObjectFileImageInappropriateFile; + } + return NSObjectFileImageFailure; +} + + +NSObjectFileImageReturnCode NSCreateObjectFileImageFromMemory(const void* address, size_t size, NSObjectFileImage *objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %lu, %p)\n", __func__, address, size, objectFileImage); + + try { + ImageLoader* image = dyld::loadFromMemory((const uint8_t*)address, size, NULL); + if ( ! image->isBundle() ) { + // this API can only be used with bundles... + dyld::garbageCollectImages(); + return NSObjectFileImageInappropriateFile; + } + // Note: We DO NOT link the image! NSLinkModule will do that + if ( image != NULL ) { + *objectFileImage = createObjectImageFile(image, address, size); + return NSObjectFileImageSuccess; + } + } + catch (const char* msg) { + free((void*)msg); + dyld::garbageCollectImages(); + //dyld::log("dyld: NSCreateObjectFileImageFromMemory() error: %s\n", msg); + } + return NSObjectFileImageFailure; +} + +static bool validOFI(NSObjectFileImage objectFileImage) +{ + const size_t ofiCount = sObjectFileImages.size(); + for (size_t i=0; i < ofiCount; ++i) { + if ( sObjectFileImages[i] == objectFileImage ) + return true; + } + return false; +} + +bool NSDestroyObjectFileImage(NSObjectFileImage objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objectFileImage); + + if ( validOFI(objectFileImage) ) { + // a failure during NSLinkModule will delete the image + if ( objectFileImage->image != NULL ) { + // if the image has never been linked or has been unlinked, the image is not in the list of valid images + // and we should delete it + bool linkedImage = dyld::validImage(objectFileImage->image); + if ( ! linkedImage ) { + ImageLoader::deleteImage(objectFileImage->image); + objectFileImage->image = NULL; + } + } + + // remove from list of ofi's + for (std::vector::iterator it=sObjectFileImages.begin(); it != sObjectFileImages.end(); it++) { + if ( *it == objectFileImage ) { + sObjectFileImages.erase(it); + break; + } + } + + // if object was created from a memory, release that memory + // NOTE: this is the way dyld has always done this. NSCreateObjectFileImageFromMemory() hands over ownership of the memory to dyld + if ( objectFileImage->imageBaseAddress != NULL ) { + bool freed = false; + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 6) ) { + size_t sz = (*dyld::gLibSystemHelpers->malloc_size)(objectFileImage->imageBaseAddress); + if ( sz != 0 ) { + (*dyld::gLibSystemHelpers->free)((void*)(objectFileImage->imageBaseAddress)); + freed = true; + } + } + if ( ! freed ) + vm_deallocate(mach_task_self(), (vm_address_t)objectFileImage->imageBaseAddress, objectFileImage->imageLength); + } + + // free ofi object + delete objectFileImage; + + return true; + } + return false; +} + +uint32_t NSSymbolDefinitionCountInObjectFileImage(NSObjectFileImage objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objectFileImage); + return objectFileImage->image->getExportedSymbolCount(); +} + +const char* NSSymbolDefinitionNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); + const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedExportedSymbol(ordinal); + return objectFileImage->image->getExportedSymbolName(sym); +} + +uint32_t NSSymbolReferenceCountInObjectFileImage(NSObjectFileImage objectFileImage) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, objectFileImage); + return objectFileImage->image->getImportedSymbolCount(); +} + +const char * NSSymbolReferenceNameInObjectFileImage(NSObjectFileImage objectFileImage, uint32_t ordinal, + bool* tentative_definition) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%d)\n", __func__, objectFileImage, ordinal); + const ImageLoader::Symbol* sym = objectFileImage->image->getIndexedImportedSymbol(ordinal); + if ( tentative_definition != NULL ) { + ImageLoader::ReferenceFlags flags = objectFileImage->image->getImportedSymbolInfo(sym); + if ( (flags & ImageLoader::kTentativeDefinition) != 0 ) + *tentative_definition = true; + else + *tentative_definition = false; + } + return objectFileImage->image->getImportedSymbolName(sym); +} + +void* NSGetSectionDataInObjectFileImage(NSObjectFileImage objectFileImage, + const char* segmentName, const char* sectionName, unsigned long* size) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%s, %s)\n", __func__, objectFileImage, segmentName, sectionName); + + void* start; + size_t length; + if ( objectFileImage->image->getSectionContent(segmentName, sectionName, &start, &length) ) { + if ( size != NULL ) + *size = length; + return start; + } + return NULL; +} + + + +bool NSIsSymbolDefinedInObjectFileImage(NSObjectFileImage objectFileImage, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p,%s)\n", __func__, objectFileImage, symbolName); + const ImageLoader::Symbol* sym = objectFileImage->image->findExportedSymbol(symbolName, true, NULL); + return ( sym != NULL ); +} + + + +NSModule NSLinkModule(NSObjectFileImage objectFileImage, const char* moduleName, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\", 0x%08X)\n", __func__, objectFileImage, moduleName, options); + + dyld::clearErrorMessage(); + try { + if ( (options & NSLINKMODULE_OPTION_CAN_UNLOAD) != 0 ) + objectFileImage->image->setCanUnload(); + + // NSLinkModule allows a bundle to be link multpile times + // each link causes the bundle to be copied to a new address + if ( objectFileImage->image->isLinked() ) { + // 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 ) { + objectFileImage->image->setPath(moduleName); + // 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 ) + objectFileImage->image->setHideExports(); + + // set up linking options + bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); + + // load libraries, rebase, bind, to make this image usable + dyld::link(objectFileImage->image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX); + + // bump reference count to keep this bundle from being garbage collected + objectFileImage->image->incrementDlopenReferenceCount(); + + // run initializers unless magic flag says not to + if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) + dyld::runInitializers(objectFileImage->image); + + return ImageLoaderToNSModule(objectFileImage->image); + } + catch (const char* msg) { + dyld::garbageCollectImages(); + if ( (options & NSLINKMODULE_OPTION_RETURN_ON_ERROR) == 0 ) + dyldAPIhalt(__func__, msg); + // not halting, so set error state for NSLinkEditError to find + setLastError(NSLinkEditOtherError, 0, moduleName, msg); + // dyld::link() deleted the image so lose our reference + objectFileImage->image = NULL; + free((void*)msg); + return NULL; + } +} + + +#if OLD_LIBSYSTEM_SUPPORT +// This is for compatibility with old libSystems (libdyld.a) which process ObjectFileImages outside dyld +static NSModule _dyld_link_module(NSObjectFileImage object_addr, size_t object_size, const char* moduleName, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\", 0x%08X)\n", "NSLinkModule", object_addr, moduleName, options); // note name/args translation + ImageLoader* image = NULL; + dyld::clearErrorMessage(); + try { + const char* imageName = moduleName; + image = dyld::loadFromMemory((const uint8_t*)object_addr, object_size, imageName); + + if ( image != NULL ) { + // support private bundles + if ( (options & NSLINKMODULE_OPTION_PRIVATE) != 0 ) + image->setHideExports(); + + // set up linking options + bool forceLazysBound = ( (options & NSLINKMODULE_OPTION_BINDNOW) != 0 ); + + // load libraries, rebase, bind, to make this image usable + dyld::link(image, forceLazysBound, false, ImageLoader::RPathChain(NULL,NULL), UINT32_MAX); + + // run initializers unless magic flag says not to + if ( (options & NSLINKMODULE_OPTION_DONT_CALL_MOD_INIT_ROUTINES) == 0 ) + dyld::runInitializers(image); + } + } + catch (const char* msg) { + if ( (options & NSLINKMODULE_OPTION_RETURN_ON_ERROR) == 0 ) + dyldAPIhalt("NSLinkModule", msg); + // not halting, so set error state for NSLinkEditError to find + setLastError(NSLinkEditOtherError, 0, moduleName, msg); + // if image was created for this bundle, destroy it + if ( image != NULL ) { + dyld::removeImage(image); + ImageLoader::deleteImage(image); + } + image = NULL; + free((void*)msg); + } + return ImageLoaderToNSModule(image); +} +#endif + +NSSymbol NSLookupSymbolInModule(NSModule module, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, \"%s\")\n", __func__, (void *)module, symbolName); + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return NULL; + return SymbolToNSSymbol(image->findExportedSymbol(symbolName, false, NULL)); +} + +const char* NSNameOfModule(NSModule module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, module); + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return NULL; + return image->getPath(); +} + +const char* NSLibraryNameForModule(NSModule module) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, module); + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return NULL; + return image->getPath(); +} + +bool NSUnLinkModule(NSModule module, uint32_t options) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, 0x%08X)\n", __func__, module, options); + if ( module == NULL ) + return false; + ImageLoader* image = NSModuleToImageLoader(module); + if ( image == NULL ) + return false; + dyld::runImageStaticTerminators(image); + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 13) ) { + __cxa_range_t ranges[image->segmentCount()]; + int rangeCount = 0; + for (unsigned int j=0; j < image->segmentCount(); ++j) { + if ( !image->segExecutable(j) ) + continue; + ranges[rangeCount].addr = (const void*)image->segActualLoadAddress(j); + ranges[rangeCount].length = image->segSize(j); + ++rangeCount; + } + (*dyld::gLibSystemHelpers->cxa_finalize_ranges)(ranges, rangeCount); + } + dyld::removeImage(image); + + if ( (options & NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) != 0 ) + image->setLeaveMapped(); + + // TODO: NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES + + // Only delete image if there is no ofi referencing it + // That means the ofi was destroyed after linking, so no one is left to delete this image + const size_t ofiCount = sObjectFileImages.size(); + bool found = false; + for (size_t i=0; i < ofiCount; ++i) { + NSObjectFileImage ofi = sObjectFileImages[i]; + if ( ofi->image == image ) + found = true; + } + if ( !found ) + ImageLoader::deleteImage(image); + + return true; +} + +// internal name and parameters do not match public name and parameters... +static void _dyld_install_handlers(void* undefined, void* multiple, void* linkEdit) +{ + if ( dyld::gLogAPIs ) + dyld::log("NSLinkEditErrorHandlers()\n"); + + dyld::registerUndefinedHandler((dyld::UndefinedHandler)undefined); + // no support for multiple or linkedit handlers +} + + + + +void NSLinkEditError(NSLinkEditErrors* c, int* errorNumber, const char** fileName, const char** errorString) +{ + // FIXME FIXME + *c = sLastErrorFileCode; + *errorNumber = sLastErrorNo; + *fileName = sLastErrorFilePath; + *errorString = dyld::getErrorMessage(); +} + + + +static void _dyld_register_binding_handler(void * (*bindingHandler)(const char *, const char *, void *), ImageLoader::BindingOptions bindingOptions) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + dyld::gLinkContext.bindingHandler = bindingHandler; + dyld::gLinkContext.bindingOptions = bindingOptions; +} + +#endif //DEPRECATED_APIS_SUPPORTED + + +// Call by fork() in libSystem after the kernel trap is done on the child side +void _dyld_fork_child() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + // The implementation of fork() in libSystem knows to reset the variable mach_task_self_ + // in libSystem for the child of a fork. But dyld is built with a static copy + // of libc.a and has its own copy of mach_task_self_ which we reset here. + // + // In mach_init.h mach_task_self() is #defined to mach_task_self_ and + // in mach_init() mach_task_self_ is initialized to task_self_trap(). + // + extern mach_port_t mach_task_self_; + mach_task_self_ = task_self_trap(); + + // If dyld is sending load/unload notices to CoreSymbolication, the shared memory + // page is not copied on fork. + // NULL the CoreSymbolication shared memory pointer to prevent a crash. + dyld::gProcessInfo->coreSymbolicationShmPage = NULL; + // for safety, make sure child starts with clean systemOrderFlag + dyld::gProcessInfo->systemOrderFlag = 0; +} + + +#if DEPRECATED_APIS_SUPPORTED +// returns true if prebinding was used in main executable +bool _dyld_launched_prebound() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + + // ¥¥¥Êif we deprecate prebinding, we may want to consider always returning true or false here + return dyld::mainExecutablePrebound(); +} + + +// +// _dyld_NSMakePrivateModulePublic() is the dyld side of the hack +// NSMakePrivateModulePublic() needed for the dlopen() to turn it's +// RTLD_LOCAL handles into RTLD_GLOBAL. It just simply turns off the private +// flag on the image for this module. If the module was found and it was +// private then everything worked and TRUE is returned else FALSE is returned. +// +static bool NSMakePrivateModulePublic(NSModule module) +{ + ImageLoader* image = NSModuleToImageLoader(module); + if ( image != NULL ) { + if ( image->hasHiddenExports() ) { + image->setHideExports(false); + return true; + } + } + return false; +} + +#endif // DEPRECATED_APIS_SUPPORTED + +int _dyld_func_lookup(const char* name, void** address) +{ + for (const dyld_func* p = dyld_funcs; p->name != NULL; ++p) { + if ( strcmp(p->name, name) == 0 ) { + if( p->implementation == unimplemented ) + dyld::log("unimplemented dyld function: %s\n", p->name); + *address = p->implementation; + return true; + } + } + *address = 0; + return false; +} + + +static void registerThreadHelpers(const dyld::LibSystemHelpers* helpers) +{ + dyld::gLibSystemHelpers = helpers; + +#if !SUPPORT_ZERO_COST_EXCEPTIONS + 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 +} + + +static void dlerrorClear() +{ + if ( dyld::gLibSystemHelpers != NULL ) { + // dlerror buffer leak + // dlerrorClear() should not force allocation, but zero it if already allocated + if ( dyld::gLibSystemHelpers->version >= 10 ) { + if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) + return; + } + + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); + buffer[0] = '\0'; + buffer[1] = '\0'; + } +} + +static void dlerrorSet(const char* msg) +{ + if ( dyld::gLibSystemHelpers != NULL ) { + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(strlen(msg)+2); + buffer[0] = '\1'; + strcpy(&buffer[1], msg); + } +} + + +bool dlopen_preflight(const char* path) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%s)\n", __func__, path); + + dlerrorClear(); + + CRSetCrashLogMessage("dyld: in dlopen_preflight()"); + + const bool leafName = (strchr(path, '/') == NULL); + const bool absolutePath = (path[0] == '/'); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + char canonicalPath[PATH_MAX]; + // 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 +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::isPathInCache(path) ) + return true; +#endif + + // dlopen_preflight() on image in shared cache leaves it loaded but not objc initialized + // if requested path is to something in the dyld shared cache, always succeed + if ( dyld::inSharedCache(path) ) + return true; + + bool result = false; + std::vector rpathsFromCallerImage; + try { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + // for dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) { + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + + ImageLoader* image = NULL; + dyld::LoadContext context; + context.useSearchPaths = true; + context.useFallbackPaths= leafName; // a partial path implies don't use fallback paths + context.useLdLibraryPath= leafName; // a leafname implies should search + context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching + context.matchByInstallName = true; + context.dontLoad = false; + context.mustBeBundle = false; + context.mustBeDylib = false; + context.canBePIE = true; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = &callersRPaths; // rpaths from caller and main executable + + unsigned cacheIndex; + image = load(path, context, cacheIndex); + if ( image != NULL ) { + dyld::preflight(image, callersRPaths, cacheIndex); // image object deleted by dyld::preflight() + result = true; + } + } + catch (const char* msg) { + const char* str = dyld::mkstringf("dlopen_preflight(%s): %s", path, msg); + dlerrorSet(str); + free((void*)str); + free((void*)msg); // our free() will do nothing if msg is a string literal + } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + CRSetCrashLogMessage(NULL); + return result; +} + +#if SUPPORT_ACCELERATE_TABLES +bool static callerIsNonOSApp(void* callerAddress, const char** shortName) +{ + *shortName = NULL; + const mach_header* unusedMh; + const char* unusedPath; + unsigned unusedIndex; + // any address in shared cache is not from app + if ( dyld::addressInCache(callerAddress, &unusedMh, &unusedPath, &unusedIndex) ) + return false; + + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + if ( callerImage == NULL ) + return false; + + *shortName = callerImage->getShortName(); + return ( strncmp(callerImage->getPath(), "/var/containers/", 16) == 0 ); +} +#endif + +void* dlopen(const char* path, int mode) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%s, 0x%08X)\n", __func__, ((path==NULL) ? "NULL" : path), mode); + +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::gLogAppAPIs ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + const char* shortName; + if ( callerIsNonOSApp(callerAddress, &shortName) ) { + dyld::log("%s: %s(%s, 0x%08X)\n", shortName, __func__, ((path==NULL) ? "NULL" : path), mode); + } + } +#endif + + dlerrorClear(); + + // passing NULL for path means return magic object + if ( path == NULL ) { + // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images + if ( (mode & RTLD_FIRST) != 0 ) + return RTLD_MAIN_ONLY; + else + return RTLD_DEFAULT; + } + + // acquire global dyld lock (dlopen is special - libSystem glue does not do locking) + bool lockHeld = false; + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 4) ) { + dyld::gLibSystemHelpers->acquireGlobalDyldLock(); + CRSetCrashLogMessage("dyld: in dlopen()"); + lockHeld = true; + } + + void* result = NULL; + const bool leafName = (strchr(path, '/') == NULL); + const bool absolutePath = (path[0] == '/'); +#if __IPHONE_OS_VERSION_MIN_REQUIRED + char canonicalPath[PATH_MAX]; + // 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 +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::dlopenFromCache(path, mode, &result) ) { + // Note: dlopenFromCache() releases the lock + if ( dyld::gLogAPIs ) + dyld::log(" %s(%s) ==> %p\n", __func__, path, result); + return result; + } +#endif + + ImageLoader* image = NULL; + std::vector rpathsFromCallerImage; + ImageLoader::RPathChain callersRPaths(NULL, &rpathsFromCallerImage); + try { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + if ( (mode & RTLD_NOLOAD) == 0 ) { + // for dlopen, use rpath from caller image and from main executable + if ( callerImage != NULL ) + callerImage->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + if ( callerImage != dyld::mainExecutable() ) + dyld::mainExecutable()->getRPaths(dyld::gLinkContext, rpathsFromCallerImage); + } + + dyld::LoadContext context; + context.useSearchPaths = true; + context.useFallbackPaths= leafName; // a partial path means no fallback paths + context.useLdLibraryPath= leafName; // a leafname implies should search + context.implicitRPath = !absolutePath; // a non-absolute path implies try rpath searching + context.matchByInstallName = true; + context.dontLoad = ( (mode & RTLD_NOLOAD) != 0 ); + context.mustBeBundle = false; + context.mustBeDylib = false; + context.canBePIE = true; + context.origin = callerImage != NULL ? callerImage->getPath() : NULL; // caller's image's path + context.rpath = &callersRPaths; // rpaths from caller and main executable + + unsigned cacheIndex; + image = load(path, context, cacheIndex); +#if SUPPORT_ACCELERATE_TABLES + if ( (image != NULL) && (cacheIndex != UINT32_MAX) ) { + // found in cache, but under a different path + const char* betterPath = dyld::getPathFromIndex(cacheIndex); + if ( (betterPath != NULL) && dyld::dlopenFromCache(betterPath, mode, &result) ) { + // Note: dlopenFromCache() releases the lock + if ( dyld::gLogAPIs ) + dyld::log(" %s(%s) ==> %p\n", __func__, path, result); + return result; + } + } +#endif + if ( image != NULL ) { + // bump reference count. Do this before link() so that if an initializer calls dlopen and fails + // this image is not garbage collected + image->incrementDlopenReferenceCount(); + // link in all dependents + if ( (mode & RTLD_NOLOAD) == 0 ) { + bool alreadyLinked = image->isLinked(); + bool forceLazysBound = ( (mode & RTLD_NOW) != 0 ); + dyld::link(image, forceLazysBound, false, callersRPaths, cacheIndex); + if ( ! alreadyLinked ) { + // only hide exports if image is not already in use + if ( (mode & RTLD_LOCAL) != 0 ) + image->setHideExports(true); + } + } + + // RTLD_NODELETE means don't unmap image even after dlclosed. This is what dlcompat did on Mac OS X 10.3 + // On other *nix OS's, it means dlclose() should do nothing, but the handle should be invalidated. + // The subtle differences are: + // 1) if the image has any termination routines, whether they are run during dlclose or when the process terminates + // 2) If someone does a supsequent dlopen() on the same image, whether the same address should be used. + if ( (mode & RTLD_NODELETE) != 0 ) + image->setLeaveMapped(); + + // release global dyld lock early, this enables initializers to do threaded operations + if ( lockHeld ) { + CRSetCrashLogMessage(NULL); + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + lockHeld = false; + } + + // RTLD_NOLOAD means dlopen should fail unless path is already loaded. + // don't run initializers when RTLD_NOLOAD is set. This only matters if dlopen() is + // called from within an initializer because it can cause initializers to run + // out of order. Most uses of RTLD_NOLOAD are "probes". If they want initialzers + // to run, then don't use RTLD_NOLOAD. + if ( (mode & RTLD_NOLOAD) == 0 ) { + // run initializers + dyld::runInitializers(image); + } + + // RTLD_FIRST means any dlsym() calls on the handle should only search that handle and not subsequent images + // this is tracked by setting the low bit of the handle, which is usually zero by malloc alignment + if ( (mode & RTLD_FIRST) != 0 ) + result = (void*)(((uintptr_t)image)|1); + else + result = image; + } + } + catch (const char* msg) { + if ( image != NULL ) { + // load() succeeded but, link() failed + // back down reference count and do GC + image->decrementDlopenReferenceCount(); + if ( image->dlopenCount() == 0 ) + dyld::garbageCollectImages(); + } + const char* str = dyld::mkstringf("dlopen(%s, %d): %s", path, mode, msg); + if ( dyld::gLogAPIs ) + dyld::log(" %s() failed, error: '%s'\n", __func__, str); + dlerrorSet(str); + free((void*)str); + free((void*)msg); // our free() will do nothing if msg is a string literal + result = NULL; + } + // free rpaths (getRPaths() malloc'ed each string) + for(std::vector::iterator it=rpathsFromCallerImage.begin(); it != rpathsFromCallerImage.end(); ++it) { + const char* str = *it; + free((void*)str); + } + + // when context.dontLoad is set, load() returns NULL instead of throwing an exception + if ( (mode & RTLD_NOLOAD) && (result == NULL) ) { + dlerrorSet("image not already loaded"); + } + + if ( lockHeld ) { + CRSetCrashLogMessage(NULL); + dyld::gLibSystemHelpers->releaseGlobalDyldLock(); + } + if ( dyld::gLogAPIs && (result != NULL) ) + dyld::log(" %s(%s) ==> %p\n", __func__, path, result); + return result; +} + + + +int dlclose(void* handle) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, handle); + + // silently accept magic handles for main executable + if ( handle == RTLD_MAIN_ONLY ) + return 0; + if ( handle == RTLD_DEFAULT ) + return 0; + + ImageLoader* image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits + if ( dyld::validImage(image) ) { + dlerrorClear(); + // decrement use count + if ( image->decrementDlopenReferenceCount() ) { + dlerrorSet("dlclose() called too many times"); + return -1; + } + // remove image if reference count went to zero + if ( image->dlopenCount() == 0 ) + dyld::garbageCollectImages(); + return 0; + } + else { + dlerrorSet("invalid handle passed to dlclose()"); + return -1; + } +} + + + +int dladdr(const void* address, Dl_info* info) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %p)\n", __func__, address, info); + + CRSetCrashLogMessage("dyld: in dladdr()"); +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::dladdrFromCache(address, info) ) { + CRSetCrashLogMessage(NULL); + return 1; // success + } +#endif + + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) { + info->dli_fname = image->getRealPath(); + info->dli_fbase = (void*)image->machHeader(); + if ( address == info->dli_fbase ) { + // special case lookup of header + info->dli_sname = "__dso_handle"; + info->dli_saddr = info->dli_fbase; + CRSetCrashLogMessage(NULL); + return 1; // success + } + // find closest symbol in the image + info->dli_sname = image->findClosestSymbol(address, (const void**)&info->dli_saddr); + // never return the mach_header symbol + if ( info->dli_saddr == info->dli_fbase ) { + info->dli_sname = NULL; + info->dli_saddr = NULL; + CRSetCrashLogMessage(NULL); + return 1; // success + } + if ( info->dli_sname != NULL ) { + 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 +} + + +char* dlerror() +{ + if ( dyld::gLogAPIs ) + dyld::log("%s()\n", __func__); + + if ( dyld::gLibSystemHelpers != NULL ) { + // if using newer libdyld.dylib and buffer if buffer not yet allocated, return NULL + if ( dyld::gLibSystemHelpers->version >= 10 ) { + if ( ! (*dyld::gLibSystemHelpers->hasPerThreadBufferFor_dlerror)() ) + return NULL; + } + + // first char of buffer is flag whether string (starting at second char) is valid + char* buffer = (*dyld::gLibSystemHelpers->getThreadBufferFor_dlerror)(2); + if ( buffer[0] != '\0' ) { // if valid buffer + buffer[0] = '\0'; // mark invalid, so next call to dlerror returns NULL + return &buffer[1]; // return message + } + } + return NULL; +} + +void* dlsym(void* handle, const char* symbolName) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %s)\n", __func__, handle, symbolName); + +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::gLogAppAPIs ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + const char* shortName; + if ( callerIsNonOSApp(callerAddress, &shortName) ) { + dyld::log("%s: %s(%p, %s)\n", shortName, __func__, handle, symbolName); + } + } +#endif + + CRSetCrashLogMessage("dyld: in dlsym()"); + dlerrorClear(); + + const ImageLoader* image; + const ImageLoader::Symbol* sym; + void* result; + + // dlsym() assumes symbolName passed in is same as in C source code + // dyld assumes all symbol names have an underscore prefix + char underscoredName[strlen(symbolName)+2]; + underscoredName[0] = '_'; + strcpy(&underscoredName[1], symbolName); + + // magic "search all" handle + if ( handle == RTLD_DEFAULT ) { + if ( dyld::flatFindExportedSymbol(underscoredName, &sym, &image) ) { + CRSetCrashLogMessage(NULL); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_DEFAULT, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_DEFAULT, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_DEFAULT, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } + + // magic "search only main executable" handle + else if ( handle == RTLD_MAIN_ONLY ) { + image = dyld::mainExecutable(); + sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way + if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, NULL, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_MAIN_ONLY, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_MAIN_ONLY, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } + + // magic "search what I would see" handle + else if ( handle == RTLD_NEXT ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + unsigned index; + if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) { + // if dylib in cache is calling dlsym(RTLD_NEXT,xxx) handle search differently + result = dyld::dlsymFromCache(RTLD_NEXT, underscoredName, index); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result); + return result; + } +#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); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext , callerImage, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_NEXT, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_NEXT, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_NEXT, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } + // magic "search me, then what I would see" handle + else if ( handle == RTLD_SELF ) { + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + unsigned index; + if ( dyld::addressInCache(callerAddress, &mh, &path, &index) ) { + // if dylib in cache is calling dlsym(RTLD_SELF,xxx) handle search differently + result = dyld::dlsymFromCache(RTLD_SELF, underscoredName, index); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result); + return result; + } +#endif + ImageLoader* callerImage = dyld::findImageContainingAddress(callerAddress); + sym = callerImage->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against + if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_SELF, %s) ==> %p\n", __func__, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(RTLD_SELF, %s): symbol not found", symbolName); + dlerrorSet(str); + free((void*)str); + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(RTLD_SELF, %s) ==> NULL\n", __func__, symbolName); + return NULL; + } +#if SUPPORT_ACCELERATE_TABLES + // check for mega dylib handle + else if ( dyld::isCacheHandle(handle) ) { + result = dyld::dlsymFromCache(handle, underscoredName, 0); + if ( dyld::gLogAPIs ) + dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result); + return result; + } +#endif + // real handle + image = (ImageLoader*)(((uintptr_t)handle) & (-4)); // clear mode bits + if ( dyld::validImage(image) ) { + if ( (((uintptr_t)handle) & 1) != 0 ) + sym = image->findExportedSymbol(underscoredName, true, &image); // search RTLD_FIRST way + else + sym = image->findExportedSymbolInImageOrDependentImages(underscoredName, dyld::gLinkContext, &image); // search image and what it links against + + if ( sym != NULL ) { + CRSetCrashLogMessage(NULL); + ImageLoader* callerImage = NULL; + if ( sDynamicInterposing ) { + // only take time to look up caller, if dynamic interposing in use + void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue + callerImage = dyld::findImageContainingAddress(callerAddress); + } + result = (void*)image->getExportedSymbolAddress(sym, dyld::gLinkContext, callerImage, false, underscoredName); + if ( dyld::gLogAPIs ) + dyld::log(" %s(%p, %s) ==> %p\n", __func__, handle, symbolName, result); + return result; + } + const char* str = dyld::mkstringf("dlsym(%p, %s): symbol not found", handle, symbolName); + dlerrorSet(str); + free((void*)str); + } + else { + dlerrorSet("invalid handle passed to dlsym()"); + } + CRSetCrashLogMessage(NULL); + if ( dyld::gLogAPIs ) + dyld::log(" %s(%p, %s) ==> NULL\n", __func__, handle, symbolName); + return NULL; +} + + + + + + + + + + +const struct dyld_all_image_infos* _dyld_get_all_image_infos() +{ + return dyld::gProcessInfo; +} + + +#if SUPPORT_ZERO_COST_EXCEPTIONS +static bool client_dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + //if ( dyld::gLogAPIs ) + // dyld::log("%s(%p, %p)\n", __func__, addr, info); + +#if SUPPORT_ACCELERATE_TABLES + if ( dyld::findUnwindSections(addr, info) ) + return true; +#endif + ImageLoader* image = dyld::findImageContainingAddress(addr); + if ( image != NULL ) { + image->getUnwindInfo(info); + return true; + } + return false; +} +#endif + + +const char* dyld_image_path_containing_address(const void* address) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p)\n", __func__, address); + +#if SUPPORT_ACCELERATE_TABLES + const mach_header* mh; + const char* path; + if ( dyld::addressInCache(address, &mh, &path) ) + return path; +#endif + + ImageLoader* image = dyld::findImageContainingAddress(address); + if ( image != NULL ) + return image->getRealPath(); + return NULL; +} + + + +bool dyld_shared_cache_some_image_overridden() +{ + return dyld::gSharedCacheOverridden; +} + + +void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) +{ + if ( mh == NULL ) + return; + if ( array == NULL ) + return; + if ( count == 0 ) + return; + ImageLoader* image = dyld::findImageByMachHeader(mh); + if ( image == NULL ) + return; + + // make pass at bound references in this image and update them + dyld::gLinkContext.dynamicInterposeArray = array; + dyld::gLinkContext.dynamicInterposeCount = count; + image->dynamicInterpose(dyld::gLinkContext); + dyld::gLinkContext.dynamicInterposeArray = NULL; + dyld::gLinkContext.dynamicInterposeCount = 0; + + // leave interposing info so any future (lazy) binding will get it too + image->addDynamicInterposingTuples(array, count); + + sDynamicInterposing = true; +} + + +bool _dyld_is_memory_immutable(const void* addr, size_t length) +{ + if ( dyld::gLogAPIs ) + dyld::log("%s(%p, %ld)\n", __func__, addr, length); + + uintptr_t checkStart = (uintptr_t)addr; + uintptr_t checkEnd = checkStart + length; + + // quick check to see if in r/o region of shared cache. If so return true. + const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader(); + if ( cache != nullptr ) { + const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset); + uintptr_t roStart = (uintptr_t)cache; + uintptr_t roEnd = roStart + (uintptr_t)mappings[0].size; + if ( (roStart < checkStart) && (checkEnd < roEnd) ) + return true; + } + + // Otherwise find if addr is in a dyld loaded image + ImageLoader* image = dyld::findImageContainingAddress(addr); + if ( image != NULL ) { + // already checked for r/o portion of cache + if ( image->inSharedCache() ) + return false; + if ( !image->neverUnload() ) + return false; + for (unsigned i=0, e=image->segmentCount(); i < e; ++i) { + if ( (image->segActualLoadAddress(i) < checkStart) && (checkEnd < image->segActualEndAddress(i)) ) { + return !image->segWriteable(i); + } + } + } + return false; +} + + + +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped) +{ + dyld::registerObjCNotifiers(mapped, init, unmapped); +} + + +bool _dyld_get_shared_cache_uuid(uuid_t uuid) +{ + return dyld::sharedCacheUUID(uuid); +} + +const void* _dyld_get_shared_cache_range(size_t* length) +{ + const DyldSharedCache* cache = (DyldSharedCache*)dyld::imMemorySharedCacheHeader(); + if ( cache != nullptr ) { + const dyld_cache_mapping_info* const mappings = (dyld_cache_mapping_info*)((char*)cache + cache->header.mappingOffset); + *length = (size_t)((mappings[2].address + mappings[2].size) - mappings[0].address); + return cache; + } + return nullptr; +} + + + diff --git a/dyld/src/dyldAPIsInLibSystem.cpp b/dyld/src/dyldAPIsInLibSystem.cpp new file mode 100644 index 0000000..6d72472 --- /dev/null +++ b/dyld/src/dyldAPIsInLibSystem.cpp @@ -0,0 +1,2130 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "mach-o/dyld_images.h" +#include "mach-o/dyld.h" +#include "mach-o/dyld_priv.h" +#include "dyld_cache_format.h" + +#include "ImageLoader.h" +#include "dyldLock.h" +#include "start_glue.h" + +#include "../dyld3/APIs.h" +#include "../dyld3/AllImages.h" + + +// this was in dyld_priv.h but it is no longer exported +extern "C" { + const struct dyld_all_image_infos* _dyld_get_all_image_infos() __attribute__((visibility("hidden"))); +} + + +extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso); +extern "C" void __cxa_finalize(const void *dso); +extern "C" void __cxa_finalize_ranges(const struct __cxa_range_t ranges[], int count); + +// +// private interface between libSystem.dylib and dyld +// +extern "C" int _dyld_func_lookup(const char* dyld_func_name, void **address); + + +extern bool gUseDyld3; + +#ifndef LC_VERSION_MIN_MACOSX + #define LC_VERSION_MIN_MACOSX 0x24 + struct version_min_command { + uint32_t cmd; /* LC_VERSION_MIN_MACOSX or + LC_VERSION_MIN_IPHONEOS */ + uint32_t cmdsize; /* sizeof(struct min_version_command) */ + uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + }; +#endif + +#ifndef LC_VERSION_MIN_IPHONEOS + #define LC_VERSION_MIN_IPHONEOS 0x25 +#endif + +#ifndef LC_VERSION_MIN_TVOS + #define LC_VERSION_MIN_TVOS 0x2F +#endif + +#ifndef LC_VERSION_MIN_WATCHOS + #define LC_VERSION_MIN_WATCHOS 0x30 +#endif + + +#ifndef LC_LOAD_UPWARD_DYLIB + #define LC_LOAD_UPWARD_DYLIB (0x23|LC_REQ_DYLD) /* load of dylib whose initializers run later */ +#endif + +#ifndef LC_BUILD_VERSION + #define LC_BUILD_VERSION 0x32 /* build for platform min OS version */ + + /* + * The build_version_command contains the min OS version on which this + * binary was built to run for its platform. The list of known platforms and + * tool values following it. + */ + struct build_version_command { + uint32_t cmd; /* LC_BUILD_VERSION */ + uint32_t cmdsize; /* sizeof(struct build_version_command) plus */ + /* ntools * sizeof(struct build_tool_version) */ + uint32_t platform; /* platform */ + uint32_t minos; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t sdk; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t ntools; /* number of tool entries following this */ + }; + + struct build_tool_version { + uint32_t tool; /* enum for the tool */ + uint32_t version; /* version number of the tool */ + }; + + /* Known values for the platform field above. */ + #define PLATFORM_MACOS 1 + #define PLATFORM_IOS 2 + #define PLATFORM_TVOS 3 + #define PLATFORM_WATCHOS 4 + #define PLATFORM_BRIDGEOS 5 + + /* Known values for the tool field above. */ + #define TOOL_CLANG 1 + #define TOOL_SWIFT 2 + #define TOOL_LD 3 +#endif + + +// deprecated APIs are still availble on Mac OS X, but not on iPhone OS +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define DEPRECATED_APIS_SUPPORTED 0 +#else + #define DEPRECATED_APIS_SUPPORTED 1 +#endif + +/* + * names_match() takes an install_name from an LC_LOAD_DYLIB command and a + * libraryName (which is -lx or -framework Foo argument passed to the static + * link editor for the same library) and determines if they match. This depends + * on conventional use of names including major versioning. + */ +static +bool +names_match( +const char *install_name, +const char* libraryName) +{ + const char *basename; + unsigned long n; + + /* + * Conventional install names have these forms: + * /System/Library/Frameworks/AppKit.framework/Versions/A/Appkit + * /Local/Library/Frameworks/AppKit.framework/Appkit + * /lib/libsys_s.A.dylib + * /usr/lib/libsys_s.dylib + */ + basename = strrchr(install_name, '/'); + if(basename == NULL) + basename = install_name; + else + basename++; + + /* + * By checking the base name matching the library name we take care + * of the -framework cases. + */ + if(strcmp(basename, libraryName) == 0) + return true; + + /* + * Now check the base name for "lib" if so proceed to check for the + * -lx case dealing with a possible .X.dylib and a .dylib extension. + */ + if(strncmp(basename, "lib", 3) ==0){ + n = strlen(libraryName); + if(strncmp(basename+3, libraryName, n) == 0){ + if(strncmp(basename+3+n, ".dylib", 6) == 0) + return true; + if(basename[3+n] == '.' && + basename[3+n+1] != '\0' && + strncmp(basename+3+n+2, ".dylib", 6) == 0) + return true; + } + } + return false; +} + +#if DEPRECATED_APIS_SUPPORTED + +void NSInstallLinkEditErrorHandlers( +const NSLinkEditErrorHandlers* handlers) +{ + if ( gUseDyld3 ) + return dyld3::NSInstallLinkEditErrorHandlers(handlers); + + DYLD_LOCK_THIS_BLOCK; + 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); + mcallback_t m = handlers->multiple; + p(handlers->undefined, m, handlers->linkEdit); +} + +const char* +NSNameOfModule( +NSModule module) +{ + if ( gUseDyld3 ) + return dyld3::NSNameOfModule(module); + + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSModule module) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSNameOfModule", (void**)&p); + return(p(module)); +} + +const char* +NSLibraryNameForModule( +NSModule module) +{ + if ( gUseDyld3 ) + return dyld3::NSLibraryNameForModule(module); + + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSModule module) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLibraryNameForModule", (void**)&p); + return(p(module)); +} + +bool +NSIsSymbolNameDefined( +const char* symbolName) +{ + if ( gUseDyld3 ) + return dyld3::NSIsSymbolNameDefined(symbolName); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolNameDefined", (void**)&p); + return(p(symbolName)); +} + +bool +NSIsSymbolNameDefinedWithHint( +const char* symbolName, +const char* libraryNameHint) +{ + if ( gUseDyld3 ) + return dyld3::NSIsSymbolNameDefinedWithHint(symbolName, libraryNameHint); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* symbolName, + const char* libraryNameHint) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedWithHint", (void**)&p); + return(p(symbolName, libraryNameHint)); +} + +bool +NSIsSymbolNameDefinedInImage( +const struct mach_header *image, +const char* symbolName) +{ + if ( gUseDyld3 ) + return dyld3::NSIsSymbolNameDefinedInImage(image, symbolName); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const struct mach_header *image, + const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolNameDefinedInImage", (void**)&p); + return(p(image, symbolName)); +} + +NSSymbol +NSLookupAndBindSymbol( +const char* symbolName) +{ + if ( gUseDyld3 ) + return dyld3::NSLookupAndBindSymbol(symbolName); + + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupAndBindSymbol", (void**)&p); + return(p(symbolName)); +} + +NSSymbol +NSLookupAndBindSymbolWithHint( +const char* symbolName, +const char* libraryNameHint) +{ + if ( gUseDyld3 ) + return dyld3::NSLookupAndBindSymbolWithHint(symbolName, libraryNameHint); + + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(const char* symbolName, + const char* libraryNameHint) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupAndBindSymbolWithHint", (void**)&p); + return(p(symbolName, libraryNameHint)); +} + +NSSymbol +NSLookupSymbolInModule( +NSModule module, +const char* symbolName) +{ + if ( gUseDyld3 ) + return dyld3::NSLookupSymbolInModule(module, symbolName); + + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(NSModule module, const char* symbolName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupSymbolInModule", (void**)&p); + return(p(module, symbolName)); +} + +NSSymbol +NSLookupSymbolInImage( +const struct mach_header *image, +const char* symbolName, +uint32_t options) +{ + if ( gUseDyld3 ) + return dyld3::NSLookupSymbolInImage(image, symbolName, options); + + DYLD_LOCK_THIS_BLOCK; + static NSSymbol (*p)(const struct mach_header *image, + const char* symbolName, + uint32_t options) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLookupSymbolInImage", (void**)&p); + return(p(image, symbolName, options)); +} + +const char* +NSNameOfSymbol( +NSSymbol symbol) +{ + if ( gUseDyld3 ) + return dyld3::NSNameOfSymbol(symbol); + + DYLD_LOCK_THIS_BLOCK; + static char * (*p)(NSSymbol symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSNameOfSymbol",(void**)&p); + return(p(symbol)); +} + +void * +NSAddressOfSymbol( +NSSymbol symbol) +{ + if ( gUseDyld3 ) + return dyld3::NSAddressOfSymbol(symbol); + + DYLD_LOCK_THIS_BLOCK; + static void * (*p)(NSSymbol symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddressOfSymbol", (void**)&p); + return(p(symbol)); +} + +NSModule +NSModuleForSymbol( +NSSymbol symbol) +{ + if ( gUseDyld3 ) + return dyld3::NSModuleForSymbol(symbol); + + DYLD_LOCK_THIS_BLOCK; + static NSModule (*p)(NSSymbol symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSModuleForSymbol", (void**)&p); + return(p(symbol)); +} + +bool +NSAddLibrary( +const char* pathName) +{ + if ( gUseDyld3 ) + return dyld3::NSAddLibrary(pathName); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* pathName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddLibrary", (void**)&p); + return(p(pathName)); +} + +bool +NSAddLibraryWithSearching( +const char* pathName) +{ + if ( gUseDyld3 ) + return dyld3::NSAddLibrary(pathName); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* pathName) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddLibraryWithSearching", (void**)&p); + return(p(pathName)); +} + +const struct mach_header * +NSAddImage( +const char* image_name, +uint32_t options) +{ + if ( gUseDyld3 ) + return dyld3::NSAddImage(image_name, options); + + DYLD_LOCK_THIS_BLOCK; + static const struct mach_header * (*p)(const char* image_name, + uint32_t options) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSAddImage", (void**)&p); + return(p(image_name, options)); +} +#endif // DEPRECATED_APIS_SUPPORTED + +/* + * This routine returns the current version of the named shared library the + * executable it was built with. The libraryName parameter is the same as the + * -lx or -framework Foo argument passed to the static link editor when building + * the executable (with -lx it would be "x" and with -framework Foo it would be + * "Foo"). If this the executable was not built against the specified library + * it returns -1. It should be noted that if this only returns the value the + * current version of the named shared library the executable was built with + * and not a list of current versions that dependent libraries and bundles the + * program is using were built with. + */ +int32_t NSVersionOfLinkTimeLibrary(const char* libraryName) +{ + if ( gUseDyld3 ) + return dyld3::NSVersionOfLinkTimeLibrary(libraryName); + + // Lazily call _NSGetMachExecuteHeader() and cache result +#if __LP64__ + static mach_header_64* mh = NULL; +#else + static mach_header* mh = NULL; +#endif + if ( mh == NULL ) + mh = _NSGetMachExecuteHeader(); +#if __LP64__ + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header_64)); +#else + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header)); +#endif + for(uint32_t i = 0; i < mh->ncmds; i++){ + switch ( lc->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + const dylib_command* dl = (dylib_command *)lc; + const char* install_name = (char*)dl + dl->dylib.name.offset; + if ( names_match(install_name, libraryName) ) + return dl->dylib.current_version; + break; + } + lc = (load_command*)((char*)lc + lc->cmdsize); + } + return (-1); +} + +/* + * This routine returns the current version of the named shared library the + * program it is running against. The libraryName parameter is the same as + * would be static link editor using the -lx or -framework Foo flags (with -lx + * it would be "x" and with -framework Foo it would be "Foo"). If the program + * is not using the specified library it returns -1. + */ +int32_t NSVersionOfRunTimeLibrary(const char* libraryName) +{ + if ( gUseDyld3 ) + return dyld3::NSVersionOfRunTimeLibrary(libraryName); + + uint32_t n = _dyld_image_count(); + for(uint32_t i = 0; i < n; i++){ + const mach_header* mh = _dyld_get_image_header(i); + if ( mh == NULL ) + continue; + if ( mh->filetype != MH_DYLIB ) + continue; +#if __LP64__ + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header_64)); +#else + const load_command* lc = (load_command*)((char*)mh + sizeof(mach_header)); +#endif + for(uint32_t j = 0; j < mh->ncmds; j++){ + if ( lc->cmd == LC_ID_DYLIB ) { + const dylib_command* dl = (dylib_command*)lc; + const char* install_name = (char *)dl + dl->dylib.name.offset; + if ( names_match(install_name, libraryName) ) + return dl->dylib.current_version; + } + lc = (load_command*)((char*)lc + lc->cmdsize); + } + } + return (-1); +} + + +#define PACKED_VERSION(major, minor, tiny) ((((major) & 0xffff) << 16) | (((minor) & 0xff) << 8) | ((tiny) & 0xff)) + + +static bool getVersionLoadCommandInfo(const mach_header* mh, uint32_t* platform, uint32_t* minOS, uint32_t* sdk) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return false; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return 0; + } + const version_min_command* versCmd; + const build_version_command* buildVersCmd; + switch ( cmd->cmd ) { + case LC_VERSION_MIN_IPHONEOS: + versCmd = (version_min_command*)cmd; + *platform = PLATFORM_IOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + return true; + case LC_VERSION_MIN_MACOSX: + versCmd = (version_min_command*)cmd; + *platform = PLATFORM_MACOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + return true; + case LC_VERSION_MIN_TVOS: + versCmd = (version_min_command*)cmd; + *platform = PLATFORM_TVOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + return true; + case LC_VERSION_MIN_WATCHOS: + versCmd = (version_min_command*)cmd; + *platform = PLATFORM_WATCHOS; + *minOS = versCmd->version; + *sdk = versCmd->sdk; + return true; + case LC_BUILD_VERSION: + buildVersCmd = (build_version_command*)cmd; + *platform = buildVersCmd->platform; + *minOS = buildVersCmd->minos; + *sdk = buildVersCmd->sdk; + return true; + } + cmd = nextCmd; + } + return false; +} + +#if !__WATCH_OS_VERSION_MIN_REQUIRED && !__TV_OS_VERSION_MIN_REQUIRED +static uint32_t deriveSDKVersFromDylibs(const mach_header* mh) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return 0; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const dylib_command* dylibCmd; + const load_command* cmd = startCmds; + const char* dylibName; + #if __IPHONE_OS_VERSION_MIN_REQUIRED + uint32_t foundationVers = 0; + #else + uint32_t libSystemVers = 0; + #endif + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + // sanity check size of command + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return 0; + } + switch ( cmd->cmd ) { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_LOAD_UPWARD_DYLIB: + dylibCmd = (dylib_command*)cmd; + // sanity check dylib command layout + if ( dylibCmd->dylib.name.offset > cmd->cmdsize ) + return 0; + dylibName = (char*)dylibCmd + dylibCmd->dylib.name.offset; + #if __IPHONE_OS_VERSION_MIN_REQUIRED + if ( strcmp(dylibName, "/System/Library/Frameworks/Foundation.framework/Foundation") == 0 ) + foundationVers = dylibCmd->dylib.current_version; + #else + if ( strcmp(dylibName, "/usr/lib/libSystem.B.dylib") == 0 ) + libSystemVers = dylibCmd->dylib.current_version; + #endif + break; + } + cmd = nextCmd; + } + + struct DylibToOSMapping { + uint32_t dylibVersion; + uint32_t osVersion; + }; + + #if __IPHONE_OS_VERSION_MIN_REQUIRED + static const DylibToOSMapping foundationMapping[] = { + { PACKED_VERSION(678,24,0), 0x00020000 }, + { PACKED_VERSION(678,26,0), 0x00020100 }, + { PACKED_VERSION(678,29,0), 0x00020200 }, + { PACKED_VERSION(678,47,0), 0x00030000 }, + { PACKED_VERSION(678,51,0), 0x00030100 }, + { PACKED_VERSION(678,60,0), 0x00030200 }, + { PACKED_VERSION(751,32,0), 0x00040000 }, + { PACKED_VERSION(751,37,0), 0x00040100 }, + { PACKED_VERSION(751,49,0), 0x00040200 }, + { PACKED_VERSION(751,58,0), 0x00040300 }, + { PACKED_VERSION(881,0,0), 0x00050000 }, + { PACKED_VERSION(890,1,0), 0x00050100 }, + { PACKED_VERSION(992,0,0), 0x00060000 }, + { PACKED_VERSION(993,0,0), 0x00060100 }, + { PACKED_VERSION(1038,14,0),0x00070000 }, + { PACKED_VERSION(0,0,0), 0x00070000 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. + }; + + if ( foundationVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=foundationMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( foundationVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } + } + + #else + // Note: versions are for the GM release. The last entry should + // always be zero. At the start of the next major version, + // a new last entry needs to be added and the previous zero + // updated to the GM dylib version. + static const DylibToOSMapping libSystemMapping[] = { + { PACKED_VERSION(88,1,3), 0x000A0400 }, + { PACKED_VERSION(111,0,0), 0x000A0500 }, + { PACKED_VERSION(123,0,0), 0x000A0600 }, + { PACKED_VERSION(159,0,0), 0x000A0700 }, + { PACKED_VERSION(169,3,0), 0x000A0800 }, + { PACKED_VERSION(1197,0,0), 0x000A0900 }, + { PACKED_VERSION(0,0,0), 0x000A0900 } + // We don't need to expand this table because all recent + // binaries have LC_VERSION_MIN_ load command. + }; + + if ( libSystemVers != 0 ) { + uint32_t lastOsVersion = 0; + for (const DylibToOSMapping* p=libSystemMapping; ; ++p) { + if ( p->dylibVersion == 0 ) + return p->osVersion; + if ( libSystemVers < p->dylibVersion ) + return lastOsVersion; + lastOsVersion = p->osVersion; + } + } + #endif + return 0; +} +#endif + + +#if __WATCH_OS_VERSION_MIN_REQUIRED +static uint32_t watchVersToIOSVers(uint32_t vers) +{ + return vers + 0x00070000; +} + +uint32_t dyld_get_program_sdk_watch_os_version() +{ + if ( gUseDyld3 ) + return dyld3::dyld_get_program_sdk_watch_os_version(); + + const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader(); + uint32_t platform; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) { + if ( platform == PLATFORM_WATCHOS ) + return sdk; + } + return 0; +} + +uint32_t dyld_get_program_min_watch_os_version() +{ + if ( gUseDyld3 ) + return dyld3::dyld_get_program_min_watch_os_version(); + + const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader(); + uint32_t platform; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) { + if ( platform == PLATFORM_WATCHOS ) + return minOS; // return raw minOS (not mapped to iOS version) + } + return 0; +} + +#endif + + + +#if TARGET_OS_BRIDGE +static uint32_t bridgeVersToIOSVers(uint32_t vers) +{ + return vers + 0x00090000; +} + +uint32_t dyld_get_program_sdk_bridge_os_version() +{ + const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader(); + uint32_t platform; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) { + if ( platform == PLATFORM_BRIDGEOS ) + return sdk; + } + return 0; +} + +uint32_t dyld_get_program_min_bridge_os_version() +{ + const mach_header* mh = (mach_header*)_NSGetMachExecuteHeader(); + uint32_t platform; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) { + if ( platform == PLATFORM_BRIDGEOS ) + return minOS; // return raw minOS (not mapped to iOS version) + } + return 0; +} + +#endif + +/* + * Returns the sdk version (encode as nibble XXXX.YY.ZZ) the + * specified binary was built against. + * + * First looks for LC_VERSION_MIN_* in binary and if sdk field is + * not zero, return that value. + * Otherwise, looks for the libSystem.B.dylib the binary linked + * against and uses a table to convert that to an sdk version. + */ +uint32_t dyld_get_sdk_version(const mach_header* mh) +{ + if ( gUseDyld3 ) + return dyld3::dyld_get_sdk_version(mh); + + uint32_t platform; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) { + switch (platform) { +#if TARGET_OS_BRIDGE + case PLATFORM_BRIDGEOS: + // new binary. sdk version looks like "2.0" but API wants "11.0" + return bridgeVersToIOSVers(sdk); + case PLATFORM_IOS: + // old binary. sdk matches API semantics so can return directly. + return sdk; +#elif __WATCH_OS_VERSION_MIN_REQUIRED + case PLATFORM_WATCHOS: + // new binary. sdk version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(sdk); + case PLATFORM_IOS: + // old binary. sdk matches API semantics so can return directly. + return sdk; +#elif __TV_OS_VERSION_MIN_REQUIRED + case PLATFORM_TVOS: + case PLATFORM_IOS: + return sdk; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + case PLATFORM_IOS: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#else + case PLATFORM_MACOS: + if ( sdk != 0 ) // old binaries might not have SDK set + return sdk; + break; +#endif + } + } + +#if __WATCH_OS_VERSION_MIN_REQUIRED || __TV_OS_VERSION_MIN_REQUIRED || TARGET_OS_BRIDGE + // All WatchOS and tv OS binaries should have version load command. + return 0; +#else + // MacOSX and iOS have old binaries without version load commmand. + return deriveSDKVersFromDylibs(mh); +#endif +} + +uint32_t dyld_get_program_sdk_version() +{ + if ( gUseDyld3 ) + return dyld3::dyld_get_program_sdk_version(); + + return dyld_get_sdk_version((mach_header*)_NSGetMachExecuteHeader()); +} + +uint32_t dyld_get_min_os_version(const struct mach_header* mh) +{ + if ( gUseDyld3 ) + return dyld3::dyld_get_min_os_version(mh); + + uint32_t platform; + uint32_t minOS; + uint32_t sdk; + + if ( getVersionLoadCommandInfo(mh, &platform, &minOS, &sdk) ) { + switch (platform) { +#if TARGET_OS_BRIDGE + case PLATFORM_BRIDGEOS: + // new binary. sdk version looks like "2.0" but API wants "11.0" + return bridgeVersToIOSVers(minOS); + case PLATFORM_IOS: + // old binary. sdk matches API semantics so can return directly. + return minOS; +#elif __WATCH_OS_VERSION_MIN_REQUIRED + case PLATFORM_WATCHOS: + // new binary. OS version looks like "2.0" but API wants "9.0" + return watchVersToIOSVers(minOS); + case PLATFORM_IOS: + // old binary. OS matches API semantics so can return directly. + return minOS; +#elif __TV_OS_VERSION_MIN_REQUIRED + case PLATFORM_TVOS: + case PLATFORM_IOS: + return minOS; +#elif __IPHONE_OS_VERSION_MIN_REQUIRED + case PLATFORM_IOS: + return minOS; +#else + case PLATFORM_MACOS: + return minOS; +#endif + } + } + return 0; +} + + +uint32_t dyld_get_program_min_os_version() +{ + if ( gUseDyld3 ) + return dyld3::dyld_get_program_min_os_version(); + + return dyld_get_min_os_version((mach_header*)_NSGetMachExecuteHeader()); +} + + +bool _dyld_get_image_uuid(const struct mach_header* mh, uuid_t uuid) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_image_uuid(mh, uuid); + + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return false; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds)) { + return false; + } + if ( cmd->cmd == LC_UUID ) { + const uuid_command* uuidCmd = (uuid_command*)cmd; + memcpy(uuid, uuidCmd->uuid, 16); + return true; + } + cmd = nextCmd; + } + bzero(uuid, 16); + return false; +} + + + +#if DEPRECATED_APIS_SUPPORTED +/* + * NSCreateObjectFileImageFromFile() creates an NSObjectFileImage for the + * specified file name if the file is a correct Mach-O file that can be loaded + * with NSloadModule(). For return codes of NSObjectFileImageFailure and + * NSObjectFileImageFormat an error message is printed to stderr. All + * other codes cause no printing. + */ +NSObjectFileImageReturnCode +NSCreateObjectFileImageFromFile( +const char* pathName, +NSObjectFileImage *objectFileImage) +{ + if ( gUseDyld3 ) + return dyld3::NSCreateObjectFileImageFromFile(pathName, objectFileImage); + + DYLD_LOCK_THIS_BLOCK; + static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSCreateObjectFileImageFromFile", (void**)&p); + return p(pathName, objectFileImage); +} + + +/* + * NSCreateObjectFileImageFromMemory() creates an NSObjectFileImage for the + * object file mapped into memory at address of size length if the object file + * is a correct Mach-O file that can be loaded with NSloadModule(). For return + * codes of NSObjectFileImageFailure and NSObjectFileImageFormat an error + * message is printed to stderr. All other codes cause no printing. + */ +NSObjectFileImageReturnCode +NSCreateObjectFileImageFromMemory( +const void* address, +size_t size, +NSObjectFileImage *objectFileImage) +{ + if ( gUseDyld3 ) + return dyld3::NSCreateObjectFileImageFromMemory(address, size, objectFileImage); + + DYLD_LOCK_THIS_BLOCK; + static NSObjectFileImageReturnCode (*p)(const void*, size_t, NSObjectFileImage*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSCreateObjectFileImageFromMemory", (void**)&p); + return p(address, size, objectFileImage); +} + +#if OBSOLETE_DYLD_API +/* + * NSCreateCoreFileImageFromFile() creates an NSObjectFileImage for the + * specified core file name if the file is a correct Mach-O core file. + * For return codes of NSObjectFileImageFailure and NSObjectFileImageFormat + * an error message is printed to stderr. All other codes cause no printing. + */ +NSObjectFileImageReturnCode +NSCreateCoreFileImageFromFile( +const char* pathName, +NSObjectFileImage *objectFileImage) +{ + DYLD_LOCK_THIS_BLOCK; + static NSObjectFileImageReturnCode (*p)(const char*, NSObjectFileImage*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSCreateCoreFileImageFromFile", (void**)&p); + return p(pathName, objectFileImage); +} +#endif + +bool +NSDestroyObjectFileImage( +NSObjectFileImage objectFileImage) +{ + if ( gUseDyld3 ) + return dyld3::NSDestroyObjectFileImage(objectFileImage); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(NSObjectFileImage) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSDestroyObjectFileImage", (void**)&p); + return p(objectFileImage); +} + + +NSModule +NSLinkModule( +NSObjectFileImage objectFileImage, +const char* moduleName, +uint32_t options) +{ + if ( gUseDyld3 ) + return dyld3::NSLinkModule(objectFileImage, moduleName, options); + + DYLD_LOCK_THIS_BLOCK; + static NSModule (*p)(NSObjectFileImage, const char*, unsigned long) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSLinkModule", (void**)&p); + + return p(objectFileImage, moduleName, options); +} + + + + +/* + * NSSymbolDefinitionCountInObjectFileImage() returns the number of symbol + * definitions in the NSObjectFileImage. + */ +uint32_t +NSSymbolDefinitionCountInObjectFileImage( +NSObjectFileImage objectFileImage) +{ + if ( gUseDyld3 ) + return dyld3::NSSymbolDefinitionCountInObjectFileImage(objectFileImage); + + DYLD_LOCK_THIS_BLOCK; + static uint32_t (*p)(NSObjectFileImage) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolDefinitionCountInObjectFileImage", (void**)&p); + + return p(objectFileImage); +} + +/* + * NSSymbolDefinitionNameInObjectFileImage() returns the name of the i'th + * symbol definitions in the NSObjectFileImage. If the ordinal specified is + * outside the range [0..NSSymbolDefinitionCountInObjectFileImage], NULL will + * be returned. + */ +const char* +NSSymbolDefinitionNameInObjectFileImage( +NSObjectFileImage objectFileImage, +uint32_t ordinal) +{ + if ( gUseDyld3 ) + return dyld3::NSSymbolDefinitionNameInObjectFileImage(objectFileImage, ordinal); + + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSObjectFileImage, uint32_t) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolDefinitionNameInObjectFileImage", (void**)&p); + + return p(objectFileImage, ordinal); +} + +/* + * NSSymbolReferenceCountInObjectFileImage() returns the number of references + * to undefined symbols the NSObjectFileImage. + */ +uint32_t +NSSymbolReferenceCountInObjectFileImage( +NSObjectFileImage objectFileImage) +{ + if ( gUseDyld3 ) + return dyld3::NSSymbolReferenceCountInObjectFileImage(objectFileImage); + + DYLD_LOCK_THIS_BLOCK; + static uint32_t (*p)(NSObjectFileImage) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolReferenceCountInObjectFileImage", (void**)&p); + + return p(objectFileImage); +} + +/* + * NSSymbolReferenceNameInObjectFileImage() returns the name of the i'th + * undefined symbol in the NSObjectFileImage. If the ordinal specified is + * outside the range [0..NSSymbolReferenceCountInObjectFileImage], NULL will be + * returned. + */ +const char* +NSSymbolReferenceNameInObjectFileImage( +NSObjectFileImage objectFileImage, +uint32_t ordinal, +bool *tentative_definition) /* can be NULL */ +{ + if ( gUseDyld3 ) + return dyld3::NSSymbolReferenceNameInObjectFileImage(objectFileImage, ordinal, tentative_definition); + + DYLD_LOCK_THIS_BLOCK; + static const char* (*p)(NSObjectFileImage, uint32_t, bool*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSSymbolReferenceNameInObjectFileImage", (void**)&p); + + return p(objectFileImage, ordinal, tentative_definition); +} + +/* + * NSIsSymbolDefinedInObjectFileImage() returns TRUE if the specified symbol + * name has a definition in the NSObjectFileImage and FALSE otherwise. + */ +bool +NSIsSymbolDefinedInObjectFileImage( +NSObjectFileImage objectFileImage, +const char* symbolName) +{ + if ( gUseDyld3 ) + return dyld3::NSIsSymbolDefinedInObjectFileImage(objectFileImage, symbolName); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(NSObjectFileImage, const char*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSIsSymbolDefinedInObjectFileImage", (void**)&p); + + return p(objectFileImage, symbolName); +} + +/* + * NSGetSectionDataInObjectFileImage() returns a pointer to the section contents + * in the NSObjectFileImage for the specified segmentName and sectionName if + * it exists and it is not a zerofill section. If not it returns NULL. If + * the parameter size is not NULL the size of the section is also returned + * indirectly through that pointer. + */ +void * +NSGetSectionDataInObjectFileImage( +NSObjectFileImage objectFileImage, +const char* segmentName, +const char* sectionName, +unsigned long *size) /* can be NULL */ +{ + if ( gUseDyld3 ) + return dyld3::NSGetSectionDataInObjectFileImage(objectFileImage, segmentName, sectionName, size); + + DYLD_LOCK_THIS_BLOCK; + static void* (*p)(NSObjectFileImage, const char*, const char*, unsigned long*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_NSGetSectionDataInObjectFileImage", (void**)&p); + + return p(objectFileImage, segmentName, sectionName, size); +} + + +void +NSLinkEditError( +NSLinkEditErrors *c, +int *errorNumber, +const char* *fileName, +const char* *errorString) +{ + if ( gUseDyld3 ) + return dyld3::NSLinkEditError(c, errorNumber, fileName, errorString); + + DYLD_LOCK_THIS_BLOCK; + static void (*p)(NSLinkEditErrors *c, + int *errorNumber, + const char* *fileName, + const char* *errorString) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_link_edit_error", (void**)&p); + if(p != NULL) + p(c, errorNumber, fileName, errorString); +} + +bool +NSUnLinkModule( +NSModule module, +uint32_t options) +{ + if ( gUseDyld3 ) + return dyld3::NSUnLinkModule(module, options); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(NSModule module, uint32_t options) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_unlink_module", (void**)&p); + + return p(module, options); +} + +#if OBSOLETE_DYLD_API +NSModule +NSReplaceModule( +NSModule moduleToReplace, +NSObjectFileImage newObjectFileImage, +uint32_t options) +{ + return(NULL); +} +#endif + + +#endif // DEPRECATED_APIS_SUPPORTED + +/* + *_NSGetExecutablePath copies the path of the executable into the buffer and + * returns 0 if the path was successfully copied in the provided buffer. If the + * buffer is not large enough, -1 is returned and the expected buffer size is + * copied in *bufsize. Note that _NSGetExecutablePath will return "a path" to + * the executable not a "real path" to the executable. That is the path may be + * a symbolic link and not the real file. And with deep directories the total + * bufsize needed could be more than MAXPATHLEN. + */ +int +_NSGetExecutablePath( +char *buf, +uint32_t *bufsize) +{ + if ( gUseDyld3 ) + return dyld3::_NSGetExecutablePath(buf, bufsize); + + DYLD_NO_LOCK_THIS_BLOCK; + static int (*p)(char *buf, uint32_t *bufsize) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld__NSGetExecutablePath", (void**)&p); + return(p(buf, bufsize)); +} + +#if DEPRECATED_APIS_SUPPORTED +void +_dyld_lookup_and_bind( +const char* symbol_name, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char*, void** , NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind", (void**)&p); + p(symbol_name, address, module); +} + +void +_dyld_lookup_and_bind_with_hint( +const char* symbol_name, +const char* library_name_hint, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char*, const char*, void**, NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind_with_hint", (void**)&p); + p(symbol_name, library_name_hint, address, module); +} + +#if OBSOLETE_DYLD_API +void +_dyld_lookup_and_bind_objc( +const char* symbol_name, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char* , void**, NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind_objc", (void**)&p); + p(symbol_name, address, module); +} +#endif + +void +_dyld_lookup_and_bind_fully( +const char* symbol_name, +void** address, +NSModule* module) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const char*, void**, NSModule*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_lookup_and_bind_fully", (void**)&p); + p(symbol_name, address, module); +} + +bool +_dyld_bind_fully_image_containing_address( +const void* address) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_bind_fully_image_containing_address", (void**)&p); + return p(address); +} +#endif // DEPRECATED_APIS_SUPPORTED + + +/* + * _dyld_register_func_for_add_image registers the specified function to be + * called when a new image is added (a bundle or a dynamic shared library) to + * the program. When this function is first registered it is called for once + * for each image that is currently part of the program. + */ +void +_dyld_register_func_for_add_image( +void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_register_func_for_add_image(func); + + DYLD_LOCK_THIS_BLOCK; + 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); + p(func); +} + +/* + * _dyld_register_func_for_remove_image registers the specified function to be + * called when an image is removed (a bundle or a dynamic shared library) from + * the program. + */ +void +_dyld_register_func_for_remove_image( +void (*func)(const struct mach_header *mh, intptr_t vmaddr_slide)) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_register_func_for_remove_image(func); + + DYLD_LOCK_THIS_BLOCK; + 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); + p(func); +} + +#if OBSOLETE_DYLD_API +/* + * _dyld_register_func_for_link_module registers the specified function to be + * called when a module is bound into the program. When this function is first + * registered it is called for once for each module that is currently bound into + * the program. + */ +void +_dyld_register_func_for_link_module( +void (*func)(NSModule module)) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(void (*func)(NSModule module)) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_link_module", (void**)&p); + p(func); +} + +/* + * _dyld_register_func_for_unlink_module registers the specified function to be + * called when a module is unbound from the program. + */ +void +_dyld_register_func_for_unlink_module( +void (*func)(NSModule module)) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(void (*func)(NSModule module)) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_unlink_module", (void**)&p); + p(func); +} + +/* + * _dyld_register_func_for_replace_module registers the specified function to be + * called when a module is to be replace with another module in the program. + */ +void +_dyld_register_func_for_replace_module( +void (*func)(NSModule oldmodule, NSModule newmodule)) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(void (*func)(NSModule oldmodule, + NSModule newmodule)) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_register_func_for_replace_module", (void**)&p); + p(func); +} + + +/* + * _dyld_get_objc_module_sect_for_module is passed a module and sets a + * pointer to the (__OBJC,__module) section and its size for the specified + * module. + */ +void +_dyld_get_objc_module_sect_for_module( +NSModule module, +void **objc_module, +unsigned long *size) +{ + DYLD_LOCK_THIS_BLOCK; + static void (*p)(NSModule module, + void **objc_module, + unsigned long *size) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_objc_module_sect_for_module", (void**)&p); + p(module, objc_module, size); +} + +#endif + +#if DEPRECATED_APIS_SUPPORTED +bool +_dyld_present(void) +{ + // this function exists for compatiblity only + return true; +} +#endif + +uint32_t +_dyld_image_count(void) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_image_count(); + + DYLD_NO_LOCK_THIS_BLOCK; + static uint32_t (*p)(void) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_image_count", (void**)&p); + return(p()); +} + +const struct mach_header * +_dyld_get_image_header(uint32_t image_index) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_image_header(image_index); + + DYLD_NO_LOCK_THIS_BLOCK; + static struct mach_header * (*p)(uint32_t image_index) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_header", (void**)&p); + return(p(image_index)); +} + +intptr_t +_dyld_get_image_vmaddr_slide(uint32_t image_index) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_image_vmaddr_slide(image_index); + + DYLD_NO_LOCK_THIS_BLOCK; + static unsigned long (*p)(uint32_t image_index) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_vmaddr_slide", (void**)&p); + return(p(image_index)); +} + +const char* +_dyld_get_image_name(uint32_t image_index) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_image_name(image_index); + + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)(uint32_t image_index) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_name", (void**)&p); + return(p(image_index)); +} + +// SPI in Mac OS X 10.6 +intptr_t _dyld_get_image_slide(const struct mach_header* mh) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_image_slide(mh); + + DYLD_NO_LOCK_THIS_BLOCK; + static intptr_t (*p)(const struct mach_header*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_slide", (void**)&p); + return(p(mh)); +} + + +#if DEPRECATED_APIS_SUPPORTED +bool +_dyld_image_containing_address(const void* address) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_image_containing_address(address); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_image_containing_address", (void**)&p); + return(p(address)); +} + +const struct mach_header * +_dyld_get_image_header_containing_address( +const void* address) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_image_header_containing_address(address); + + DYLD_LOCK_THIS_BLOCK; + static const struct mach_header * (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p); + return p(address); +} + +bool _dyld_launched_prebound(void) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(void) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_launched_prebound", (void**)&p); + return(p()); +} + +bool _dyld_all_twolevel_modules_prebound(void) +{ + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(void) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_all_twolevel_modules_prebound", (void**)&p); + return(p()); +} +#endif // DEPRECATED_APIS_SUPPORTED + + +#include +#include +#include +#include +#include +#include +#include "dyldLibSystemInterface.h" + + +// pthread key used to access per-thread dlerror message +static pthread_key_t dlerrorPerThreadKey; +static bool dlerrorPerThreadKeyInitialized = false; + +// data kept per-thread +struct dlerrorPerThreadData +{ + size_t sizeAllocated; + char message[1]; +}; + +// function called by dyld to get buffer to store dlerror message +static char* getPerThreadBufferFor_dlerror(size_t sizeRequired) +{ + // ok to create key lazily because this function is called within dyld lock, so there is no race condition + if (!dlerrorPerThreadKeyInitialized ) { + // create key and tell pthread package to call free() on any data associated with key if thread dies + pthread_key_create(&dlerrorPerThreadKey, &free); + dlerrorPerThreadKeyInitialized = true; + } + + const size_t size = (sizeRequired < 256) ? 256 : sizeRequired; + dlerrorPerThreadData* data = (dlerrorPerThreadData*)pthread_getspecific(dlerrorPerThreadKey); + if ( data == NULL ) { + //int mallocSize = offsetof(dlerrorPerThreadData, message[size]); + const size_t mallocSize = sizeof(dlerrorPerThreadData)+size; + data = (dlerrorPerThreadData*)malloc(mallocSize); + data->sizeAllocated = size; + pthread_setspecific(dlerrorPerThreadKey, data); + } + else if ( data->sizeAllocated < sizeRequired ) { + free(data); + //int mallocSize = offsetof(dlerrorPerThreadData, message[size]); + const size_t mallocSize = sizeof(dlerrorPerThreadData)+size; + data = (dlerrorPerThreadData*)malloc(mallocSize); + data->sizeAllocated = size; + pthread_setspecific(dlerrorPerThreadKey, data); + } + return data->message; +} + +// dlerror buffer leak +// Only allocate buffer if an actual error message needs to be set +static bool hasPerThreadBufferFor_dlerror() +{ + if (!dlerrorPerThreadKeyInitialized ) + return false; + + return (pthread_getspecific(dlerrorPerThreadKey) != NULL); +} + +// use non-lazy pointer to vproc_swap_integer so that lazy binding does not recurse +typedef vproc_err_t (*vswapproc)(vproc_t vp, vproc_gsk_t key,int64_t *inval, int64_t *outval); +static vswapproc swapProc = &vproc_swap_integer; + +static bool isLaunchdOwned() +{ + int64_t val = 0; + (*swapProc)(NULL, VPROC_GSK_IS_MANAGED, NULL, &val); + return ( val != 0 ); +} + +static void shared_cache_missing() +{ + // leave until dyld's that might call this are rare +} + +static void shared_cache_out_of_date() +{ + // leave until dyld's that might call this are rare +} + + +// the table passed to dyld containing thread helpers +static dyld::LibSystemHelpers sHelpers = { 13, &dyldGlobalLockAcquire, &dyldGlobalLockRelease, + &getPerThreadBufferFor_dlerror, &malloc, &free, &__cxa_atexit, + &shared_cache_missing, &shared_cache_out_of_date, + NULL, NULL, + &pthread_key_create, &pthread_setspecific, + &malloc_size, + &pthread_getspecific, + &__cxa_finalize, + address_of_start, + &hasPerThreadBufferFor_dlerror, + &isLaunchdOwned, + &vm_allocate, + &mmap, + &__cxa_finalize_ranges + }; + + +// +// during initialization of libSystem this routine will run +// and call dyld, registering the helper functions. +// +extern "C" void tlv_initializer(); +void _dyld_initializer() +{ + void (*p)(dyld::LibSystemHelpers*); + + if ( gUseDyld3 ) { + dyld3::gAllImages.applyInitialImages(); + } + else { + _dyld_func_lookup("__dyld_register_thread_helpers", (void**)&p); + if(p != NULL) + p(&sHelpers); + } + + tlv_initializer(); +} + + +char* dlerror() +{ + if ( gUseDyld3 ) + return dyld3::dlerror(); + + DYLD_LOCK_THIS_BLOCK; + static char* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlerror", (void**)&p); + return(p()); +} + +int dladdr(const void* addr, Dl_info* info) +{ + if ( gUseDyld3 ) + return dyld3::dladdr(addr, info); + + DYLD_LOCK_THIS_BLOCK; + static int (*p)(const void* , Dl_info*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dladdr", (void**)&p); + return(p(addr, info)); +} + +int dlclose(void* handle) +{ + if ( gUseDyld3 ) + return dyld3::dlclose(handle); + + DYLD_LOCK_THIS_BLOCK; + static int (*p)(void* handle) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlclose", (void**)&p); + return(p(handle)); +} + +void* dlopen(const char* path, int mode) +{ + if ( gUseDyld3 ) + return dyld3::dlopen(path, mode); + + // dlopen is special. locking is done inside dyld to allow initializer to run without lock + DYLD_NO_LOCK_THIS_BLOCK; + + static void* (*p)(const char* path, int) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlopen", (void**)&p); + void* result = p(path, mode); + // use asm block to prevent tail call optimization + // this is needed because dlopen uses __builtin_return_address() and depends on this glue being in the frame chain + // + __asm__ volatile(""); + + return result; +} + +bool dlopen_preflight(const char* path) +{ + if ( gUseDyld3 ) + return dyld3::dlopen_preflight(path); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(const char* path) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlopen_preflight", (void**)&p); + return(p(path)); +} + +void* dlsym(void* handle, const char* symbol) +{ + if ( gUseDyld3 ) + return dyld3::dlsym(handle, symbol); + + DYLD_LOCK_THIS_BLOCK; + static void* (*p)(void* handle, const char* symbol) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_dlsym", (void**)&p); + return(p(handle, symbol)); +} + +const struct dyld_all_image_infos* _dyld_get_all_image_infos() +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_all_image_infos(); + + DYLD_NO_LOCK_THIS_BLOCK; + static struct dyld_all_image_infos* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_all_image_infos", (void**)&p); + return p(); +} + +#if SUPPORT_ZERO_COST_EXCEPTIONS +bool _dyld_find_unwind_sections(void* addr, dyld_unwind_sections* info) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_find_unwind_sections(addr, info); + + DYLD_NO_LOCK_THIS_BLOCK; + static void* (*p)(void*, dyld_unwind_sections*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_find_unwind_sections", (void**)&p); + return p(addr, info); +} +#endif + + +#if __i386__ || __x86_64__ || __arm__ || __arm64__ +__attribute__((visibility("hidden"))) +void* _dyld_fast_stub_entry(void* loadercache, long lazyinfo) +{ + DYLD_NO_LOCK_THIS_BLOCK; + static void* (*p)(void*, long) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_fast_stub_entry", (void**)&p); + return p(loadercache, lazyinfo); +} +#endif + + +const char* dyld_image_path_containing_address(const void* addr) +{ + if ( gUseDyld3 ) + return dyld3::dyld_image_path_containing_address(addr); + + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_image_path_containing_address", (void**)&p); + return p(addr); +} + +const struct mach_header* dyld_image_header_containing_address(const void* addr) +{ + if ( gUseDyld3 ) + return dyld3::dyld_image_header_containing_address(addr); + + DYLD_NO_LOCK_THIS_BLOCK; + static const mach_header* (*p)(const void*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_image_header_containing_address", (void**)&p); + return p(addr); +} + + +bool dyld_shared_cache_some_image_overridden() +{ + if ( gUseDyld3 ) + return dyld3::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(); +} + +bool _dyld_get_shared_cache_uuid(uuid_t uuid) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_shared_cache_uuid(uuid); + + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)(uuid_t) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_shared_cache_uuid", (void**)&p); + return p(uuid); +} + +const void* _dyld_get_shared_cache_range(size_t* length) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_get_shared_cache_range(length); + + DYLD_NO_LOCK_THIS_BLOCK; + static const void* (*p)(size_t*) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_get_shared_cache_range", (void**)&p); + return p(length); +} + + +bool dyld_process_is_restricted() +{ + if ( gUseDyld3 ) + return dyld3::dyld_process_is_restricted(); + + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_process_is_restricted", (void**)&p); + return p(); +} + +const char* dyld_shared_cache_file_path() +{ + if ( gUseDyld3 ) + return dyld3::dyld_shared_cache_file_path(); + + DYLD_NO_LOCK_THIS_BLOCK; + static const char* (*p)() = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_shared_cache_file_path", (void**)&p); + return p(); +} + +void dyld_dynamic_interpose(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) +{ + if ( gUseDyld3 ) + return dyld3::dyld_dynamic_interpose(mh, array, count); + + DYLD_LOCK_THIS_BLOCK; + static void (*p)(const struct mach_header* mh, const struct dyld_interpose_tuple array[], size_t count) = NULL; + + if (p == NULL) + _dyld_func_lookup("__dyld_dynamic_interpose", (void**)&p); + p(mh, array, count); +} + + +// SPI called __fork +void _dyld_fork_child() +{ + if ( gUseDyld3 ) + return dyld3::_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(); +} + + + +static void* mapStartOfCache(const char* path, size_t length) +{ + struct stat statbuf; + if ( ::stat(path, &statbuf) == -1 ) + return NULL; + + if ( statbuf.st_size < length ) + return NULL; + + int cache_fd = ::open(path, O_RDONLY); + if ( cache_fd < 0 ) + return NULL; + + void* result = ::mmap(NULL, length, PROT_READ, MAP_PRIVATE, cache_fd, 0); + close(cache_fd); + + if ( result == MAP_FAILED ) + return NULL; + + return result; +} + + +static const dyld_cache_header* findCacheInDirAndMap(const uuid_t cacheUuid, const char* dirPath) +{ + DIR* dirp = ::opendir(dirPath); + if ( dirp != NULL) { + dirent entry; + dirent* entp = NULL; + char cachePath[PATH_MAX]; + while ( ::readdir_r(dirp, &entry, &entp) == 0 ) { + if ( entp == NULL ) + break; + if ( entp->d_type != DT_REG ) + continue; + if ( strlcpy(cachePath, dirPath, PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(cachePath, "/", PATH_MAX) >= PATH_MAX ) + continue; + if ( strlcat(cachePath, entp->d_name, PATH_MAX) >= PATH_MAX ) + continue; + if ( const dyld_cache_header* cacheHeader = (dyld_cache_header*)mapStartOfCache(cachePath, 0x00100000) ) { + if ( ::memcmp(cacheHeader->uuid, cacheUuid, 16) != 0 ) { + // wrong uuid, unmap and keep looking + ::munmap((void*)cacheHeader, 0x00100000); + } + else { + // found cache + closedir(dirp); + return cacheHeader; + } + } + } + closedir(dirp); + } + return NULL; +} + +int dyld_shared_cache_find_iterate_text(const uuid_t cacheUuid, const char* extraSearchDirs[], void (^callback)(const dyld_shared_cache_dylib_text_info* info)) +{ + if ( gUseDyld3 ) + return dyld3::dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback); + + const dyld_cache_header* cacheHeader = NULL; + bool needToUnmap = true; + + // get info from dyld about this process, to see if requested cache is already mapped into this process + const dyld_all_image_infos* allInfo = _dyld_get_all_image_infos(); + if ( (allInfo != NULL) && (allInfo->sharedCacheBaseAddress != 0) && (memcmp(allInfo->sharedCacheUUID, cacheUuid, 16) == 0) ) { + // requested cache is already mapped, just re-use it + cacheHeader = (dyld_cache_header*)(allInfo->sharedCacheBaseAddress); + needToUnmap = false; + } + else { + // look first is default location for cache files + #if __IPHONE_OS_VERSION_MIN_REQUIRED + const char* defaultSearchDir = IPHONE_DYLD_SHARED_CACHE_DIR; + #else + const char* defaultSearchDir = MACOSX_DYLD_SHARED_CACHE_DIR; + #endif + cacheHeader = findCacheInDirAndMap(cacheUuid, defaultSearchDir); + // if not there, look in extra search locations + if ( cacheHeader == NULL ) { + for (const char** p = extraSearchDirs; *p != NULL; ++p) { + cacheHeader = findCacheInDirAndMap(cacheUuid, *p); + if ( cacheHeader != NULL ) + break; + } + } + } + + if ( cacheHeader == NULL ) + return -1; + + if ( cacheHeader->mappingOffset < sizeof(dyld_cache_header) ) { + // old cache without imagesText array + if ( needToUnmap ) + ::munmap((void*)cacheHeader, 0x00100000); + return -1; + } + + // walk imageText table and call callback for each entry + const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)cacheHeader + cacheHeader->mappingOffset); + const uint64_t cacheUnslidBaseAddress = mappings[0].address; + const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)cacheHeader + cacheHeader->imagesTextOffset); + const dyld_cache_image_text_info* imagesTextEnd = &imagesText[cacheHeader->imagesTextCount]; + for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd; ++p) { + dyld_shared_cache_dylib_text_info dylibTextInfo; + dylibTextInfo.version = 2; + dylibTextInfo.loadAddressUnslid = p->loadAddress; + dylibTextInfo.textSegmentSize = p->textSegmentSize; + dylibTextInfo.path = (char*)cacheHeader + p->pathOffset; + ::memcpy(dylibTextInfo.dylibUuid, p->uuid, 16); + dylibTextInfo.textSegmentOffset = p->loadAddress - cacheUnslidBaseAddress; + callback(&dylibTextInfo); + } + + if ( needToUnmap ) + ::munmap((void*)cacheHeader, 0x00100000); + + return 0; +} + +int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, void (^callback)(const dyld_shared_cache_dylib_text_info* info)) +{ + if ( gUseDyld3 ) + return dyld3::dyld_shared_cache_iterate_text(cacheUuid, callback); + + const char* extraSearchDirs[] = { NULL }; + return dyld_shared_cache_find_iterate_text(cacheUuid, extraSearchDirs, callback); +} + + +bool _dyld_is_memory_immutable(const void* addr, size_t length) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_is_memory_immutable(addr, length); + + DYLD_NO_LOCK_THIS_BLOCK; + static bool (*p)(const void*, size_t) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_is_memory_immutable", (void**)&p); + return p(addr, length); +} + + +void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped, + _dyld_objc_notify_init init, + _dyld_objc_notify_unmapped unmapped) +{ + if ( gUseDyld3 ) + return dyld3::_dyld_objc_notify_register(mapped, init, unmapped); + + DYLD_LOCK_THIS_BLOCK; + static bool (*p)(_dyld_objc_notify_mapped, _dyld_objc_notify_init, _dyld_objc_notify_unmapped) = NULL; + + if(p == NULL) + _dyld_func_lookup("__dyld_objc_notify_register", (void**)&p); + p(mapped, init, unmapped); +} + + + + + diff --git a/dyld/src/dyldExceptions.c b/dyld/src/dyldExceptions.c new file mode 100644 index 0000000..487835e --- /dev/null +++ b/dyld/src/dyldExceptions.c @@ -0,0 +1,205 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mach-o/dyld_priv.h" +#include "dyldLibSystemInterface.h" + + +extern void _ZN4dyld3logEPKcz(const char*, ...); +extern struct LibSystemHelpers* _ZN4dyld17gLibSystemHelpersE; + + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define macho_header mach_header_64 + #define macho_segment_command segment_command_64 + #define macho_section section_64 + #define getsectdatafromheader getsectdatafromheader_64 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define macho_header mach_header + #define macho_segment_command segment_command + #define macho_section section +#endif + + +// +// 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 < 7) ) + 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*)_ZN4dyld17gLibSystemHelpersE->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 < 7) ) + return sPreMainCxaGlobals; + + return _ZN4dyld17gLibSystemHelpersE->pthread_getspecific(sCxaKey); +} + + + +#if !__USING_SJLJ_EXCEPTIONS__ +// +// When dyld uses zero-cost exceptions it just needs to implement +// _dyld_find_unwind_sections to return sections inside dyld proper. +// + +extern void* ehStart __asm("section$start$__TEXT$__eh_frame"); +extern void* ehEnd __asm("section$end$__TEXT$__eh_frame"); +extern void* uwStart __asm("section$start$__TEXT$__unwind_info"); +extern void* uwEnd __asm("section$end$__TEXT$__unwind_info"); + +extern void* textStart __asm("section$start$__TEXT$__text"); +extern void* textEnd __asm("section$end$__TEXT$__text"); + +extern void* __dso_handle; + +// called by libuwind code to find unwind information sections in dyld +bool _dyld_find_unwind_sections(void* addr, struct dyld_unwind_sections* info) +{ + if ( ((void*)&textStart < addr) && (addr < (void*)&textEnd) ) { + info->mh = (struct mach_header*)&__dso_handle; + info->dwarf_section = &ehStart; + info->dwarf_section_length = ((char*)&ehEnd - (char*)&ehStart); + info->compact_unwind_section = &uwStart; + info->compact_unwind_section_length = ((char*)&uwEnd - (char*)&uwStart); + return true; + } + else { + return false; + } +} + +#else +// +// When dyld uses setjump-longjump exceptions it needs to implement +// routines to push and pop a stack of _Unwind_FunctionContext. +// + +struct _Unwind_FunctionContext +{ + // next function in stack of handlers + struct _Unwind_FunctionContext* prev; + +}; + + +static pthread_key_t sThreadChainKey = 0; +static struct _Unwind_FunctionContext* sStaticThreadChain = NULL; + +// +// 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) +{ + 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)); +} + + +//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"); +//} + + +struct _Unwind_FunctionContext* __Unwind_SjLj_GetTopOfFunctionStack() +{ + //_ZN4dyld3logEPKcz("__Unwind_SjLj_GetTopOfFunctionStack(), key=%d, ", sThreadChainKey); + //printChain(); + if ( sThreadChainKey != 0 ) { + return (struct _Unwind_FunctionContext*)pthread_getspecific(sThreadChainKey); + } + else { + return sStaticThreadChain; + } +} + +void __Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext* fc) +{ + //_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(); +} + +#endif + + + diff --git a/dyld/src/dyldInitialization.cpp b/dyld/src/dyldInitialization.cpp new file mode 100644 index 0000000..692b271 --- /dev/null +++ b/dyld/src/dyldInitialization.cpp @@ -0,0 +1,282 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#define __STDC_LIMIT_MACROS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __x86_64__ + #include +#endif +#include "dyld.h" +#include "dyldSyscallInterface.h" + +// from dyld_gdb.cpp +extern void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]); +extern void syncProcessInfo(); + +#ifndef MH_PIE + #define MH_PIE 0x200000 +#endif + +// currently dyld has no initializers, but if some come back, set this to non-zero +#define DYLD_INITIALIZER_SUPPORT 0 + +#if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + #define macho_segment_command segment_command_64 + #define macho_section section_64 + #define RELOC_SIZE 3 +#else + #define LC_SEGMENT_COMMAND LC_SEGMENT + #define macho_segment_command segment_command + #define macho_section section + #define RELOC_SIZE 2 +#endif + +#if __x86_64__ + #define POINTER_RELOC X86_64_RELOC_UNSIGNED +#else + #define POINTER_RELOC GENERIC_RELOC_VANILLA +#endif + + +#if TARGET_IPHONE_SIMULATOR +const dyld::SyscallHelpers* gSyscallHelpers = NULL; +#endif + + +// +// Code to bootstrap dyld into a runnable state +// +// + +namespace dyldbootstrap { + + + +#if DYLD_INITIALIZER_SUPPORT + +typedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[]); + +extern const Initializer inits_start __asm("section$start$__DATA$__mod_init_func"); +extern const Initializer inits_end __asm("section$end$__DATA$__mod_init_func"); + +// +// For a regular executable, the crt code calls dyld to run the executables initializers. +// For a static executable, crt directly runs the initializers. +// dyld (should be static) but is a dynamic executable and needs this hack to run its own initializers. +// We pass argc, argv, etc in case libc.a uses those arguments +// +static void runDyldInitializers(const struct macho_header* mh, intptr_t slide, int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + for (const Initializer* p = &inits_start; p < &inits_end; ++p) { + (*p)(argc, argv, envp, apple); + } +} +#endif // DYLD_INITIALIZER_SUPPORT + + +// +// 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 ( (segCmd->fileoff == 0) && (segCmd->filesize != 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 +// +static void rebaseDyld(const struct macho_header* mh, intptr_t slide) +{ + // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) + // and get interesting pointers into dyld + const uint32_t cmd_count = mh->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)mh)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + const struct macho_segment_command* linkEditSeg = NULL; +#if __x86_64__ + const struct macho_segment_command* firstWritableSeg = NULL; +#endif + const struct dysymtab_command* dynamicSymbolTable = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; + if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) + linkEditSeg = seg; + 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 ) { + // rebase non-lazy pointers (which all point internal to dyld, since dyld uses no shared libraries) + const uint32_t pointerCount = (uint32_t)(sect->size / sizeof(uintptr_t)); + uintptr_t* const symbolPointers = (uintptr_t*)(sect->addr + slide); + for (uint32_t j=0; j < pointerCount; ++j) { + symbolPointers[j] += slide; + } + } + } +#if __x86_64__ + if ( (firstWritableSeg == NULL) && (seg->initprot & VM_PROT_WRITE) ) + firstWritableSeg = seg; +#endif + } + break; + case LC_DYSYMTAB: + dynamicSymbolTable = (struct dysymtab_command *)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + + // use reloc's to rebase all random data pointers +#if __x86_64__ + const uintptr_t relocBase = firstWritableSeg->vmaddr + slide; +#else + const uintptr_t relocBase = (uintptr_t)mh; +#endif + const relocation_info* const relocsStart = (struct relocation_info*)(linkEditSeg->vmaddr + slide + dynamicSymbolTable->locreloff - linkEditSeg->fileoff); + const relocation_info* const relocsEnd = &relocsStart[dynamicSymbolTable->nlocrel]; + for (const relocation_info* reloc=relocsStart; reloc < relocsEnd; ++reloc) { + if ( reloc->r_length != RELOC_SIZE ) + throw "relocation in dyld has wrong size"; + + if ( reloc->r_type != POINTER_RELOC ) + throw "relocation in dyld has wrong type"; + + // update pointer by amount dyld slid + *((uintptr_t*)(reloc->r_address + relocBase)) += slide; + } +} + + +extern "C" void mach_init(); +extern "C" void __guard_setup(const char* apple[]); + + +// +// 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, const struct macho_header* dyldsMachHeader, + uintptr_t* startGlue) +{ + // 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); + } + + // allow dyld to use mach messaging + mach_init(); + + // kernel sets up env pointer to be just past end of agv array + const char** envp = &argv[argc+1]; + + // kernel sets up apple pointer to be just past end of envp array + const char** apple = envp; + while(*apple != NULL) { ++apple; } + ++apple; + + // set up random value for stack canary + __guard_setup(apple); + +#if DYLD_INITIALIZER_SUPPORT + // run all C++ initializers inside dyld + runDyldInitializers(dyldsMachHeader, slide, argc, argv, envp, apple); +#endif + + // now that we are done bootstrapping dyld, call dyld's main + uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader); + return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue); +} + + +#if TARGET_IPHONE_SIMULATOR + +extern "C" uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers*, uintptr_t* startGlue); + + +uintptr_t start_sim(int argc, const char* argv[], const char* envp[], const char* apple[], + const macho_header* mainExecutableMH, const macho_header* dyldMH, uintptr_t dyldSlide, + const dyld::SyscallHelpers* sc, uintptr_t* startGlue) +{ + // if simulator dyld loaded slid, it needs to rebase itself + // we have to do this before using any global variables + if ( dyldSlide != 0 ) { + rebaseDyld(dyldMH, dyldSlide); + } + + // save table of syscall pointers + gSyscallHelpers = sc; + + // allow dyld to use mach messaging + mach_init(); + + // set up random value for stack canary + __guard_setup(apple); + + // setup gProcessInfo to point to host dyld's struct + dyld::gProcessInfo = (struct dyld_all_image_infos*)(sc->getProcessInfo()); + syncProcessInfo(); + + // now that we are done bootstrapping dyld, call dyld's main + uintptr_t appsSlide = slideOfMainExecutable(mainExecutableMH); + return dyld::_main(mainExecutableMH, appsSlide, argc, argv, envp, apple, startGlue); +} +#endif + + +} // end of namespace + + + + diff --git a/dyld/src/dyldLibSystemGlue.c b/dyld/src/dyldLibSystemGlue.c new file mode 100644 index 0000000..4b6a638 --- /dev/null +++ b/dyld/src/dyldLibSystemGlue.c @@ -0,0 +1,76 @@ +/* -*- 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 +#include +#include + + +// +// This is the temporary private interface between libSystem.B.dylib and dyld +// + + +int NXArgc = 0; +const char** NXArgv = NULL; +const char** environ = NULL; +const char* __progname = NULL; + + + +// +// 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**); + // ProgramVars + const void* mh; + int* NXArgcPtr; + const char*** NXArgvPtr; + const char*** environPtr; + const char** __prognamePtr; +}; + +static volatile struct __DATA__dyld myDyldSection __attribute__ ((section ("__DATA,__dyld"))) + = { 0, 0, NULL, &NXArgc, &NXArgv, &environ, &__progname }; + +#if __arm__ && __MAC_OS_X_VERSION_MIN_REQUIRED +// +// 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); +} + diff --git a/dyld/src/dyldLibSystemInterface.h b/dyld/src/dyldLibSystemInterface.h new file mode 100644 index 0000000..f39a275 --- /dev/null +++ b/dyld/src/dyldLibSystemInterface.h @@ -0,0 +1,86 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-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@ + */ + + +#ifndef __DYLDLIBSYSTEMHELPERS__ +#define __DYLDLIBSYSTEMHELPERS__ + +#include + +struct __cxa_range_t { const void* addr; size_t length; }; + +#if __cplusplus +namespace dyld { +#endif + // + // This file contains the synchronization utilities used by dyld to be thread safe. + // This struct is implemented in in libSystem (where pthreads is available) + // and passed to dyld to use. + // + struct LibSystemHelpers + { + uintptr_t version; + void (*acquireGlobalDyldLock)(); + void (*releaseGlobalDyldLock)(); + char* (*getThreadBufferFor_dlerror)(size_t sizeRequired); + // addded in version 2 + void* (*malloc)(size_t); + void (*free)(void*); + int (*cxa_atexit)(void (*)(void*), void*, void*); + // addded in version 3 + void (*dyld_shared_cache_missing)(); + void (*dyld_shared_cache_out_of_date)(); + // addded in version 4 + void (*acquireDyldInitializerLock)(); + void (*releaseDyldInitializerLock)(); + // added in version 5 + int (*pthread_key_create)(pthread_key_t*, void (*destructor)(void*)); + int (*pthread_setspecific)(pthread_key_t, const void*); + // added in version 6 + size_t (*malloc_size)(const void *ptr); + // added in version 7 + void* (*pthread_getspecific)(pthread_key_t); + // added in version 8 + void (*cxa_finalize)(const void*); + // added in version 9 + void* startGlueToCallExit; + // added in version 10 + bool (*hasPerThreadBufferFor_dlerror)(); + // added in version 11 + bool (*isLaunchdOwned)(); + // added in version 12 + kern_return_t (*vm_alloc)(vm_map_t task, vm_address_t* addr, vm_size_t size, int flags); + void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + // added in version 13 + void (*cxa_finalize_ranges)(const struct __cxa_range_t ranges[], int count); + }; +#if __cplusplus +} +#endif + + + + +#endif // __DYLDLIBSYSTEMHELPERS__ + diff --git a/dyld/src/dyldLock.cpp b/dyld/src/dyldLock.cpp new file mode 100644 index 0000000..18dfe14 --- /dev/null +++ b/dyld/src/dyldLock.cpp @@ -0,0 +1,59 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +#include "dyldLock.h" + + + +static pthread_mutex_t sGlobalMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER; + +// Need a way to determine if a gdb call to dlopen() would block +int __attribute__((visibility("hidden"))) _dyld_global_lock_held = 0; + + +LockHelper::LockHelper() +{ + dyldGlobalLockAcquire(); +} + +LockHelper::~LockHelper() +{ + dyldGlobalLockRelease(); +} + +void dyldGlobalLockAcquire() +{ + pthread_mutex_lock(&sGlobalMutex); + ++_dyld_global_lock_held; +} + +void dyldGlobalLockRelease() +{ + --_dyld_global_lock_held; + pthread_mutex_unlock(&sGlobalMutex); +} + + diff --git a/dyld/src/dyldLock.h b/dyld/src/dyldLock.h new file mode 100644 index 0000000..461012d --- /dev/null +++ b/dyld/src/dyldLock.h @@ -0,0 +1,68 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __DYLDLOCK__ +#define __DYLDLOCK__ + +// +// This file contains the synchronization utilities used to make dyld APIs be thread safe. +// +// The implementation of all dyld API's must hold acquire global lock (in libSystem) +// before calling into dyld proper, and release the lock after returning from dyld. +// This is done using the macros DYLD_LOCK_THIS_BLOCK. +// Example: +// +// void dyld_api() { +// DYLD_LOCK_THIS_BLOCK; +// // free to do stuff here +// // that accesses dyld internal data structures +// } +// +// void dyld_api_state_free() { +// DYLD_NO_LOCK_THIS_BLOCK +// // can only do stuff here +// // that does not require locking +// } +// + +#define DYLD_LOCK_THIS_BLOCK LockHelper _dyld_lock; +#define DYLD_NO_LOCK_THIS_BLOCK + +// used by dyld wrapper functions in libSystem +class __attribute__((visibility("hidden"))) LockHelper +{ +public: + LockHelper(); + ~LockHelper(); +}; + + +// to initialize +extern void dyldGlobalLockInitialize() __attribute__((visibility("hidden"))); +extern void dyldGlobalLockAcquire() __attribute__((visibility("hidden"))); +extern void dyldGlobalLockRelease() __attribute__((visibility("hidden"))); + +#endif // __DYLDLOCK__ + diff --git a/dyld/src/dyldNew.cpp b/dyld/src/dyldNew.cpp new file mode 100644 index 0000000..cf64f58 --- /dev/null +++ b/dyld/src/dyldNew.cpp @@ -0,0 +1,183 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#include +#include +#include +#include +#include + +extern "C" void* __dso_handle; + +#include "dyld.h" +#include "dyldLibSystemInterface.h" + +// +// dyld initially allocates all memory from a pool inside dyld. +// Once libSystem.dylib is initialized, dyld uses libSystem's malloc/free. +// + +#if __LP64__ + // room for about ~1000 initial dylibs + #define DYLD_POOL_CHUNK_SIZE 200*1024 +#else + // room for about ~900 initial dylibs + #define DYLD_POOL_CHUNK_SIZE 150*1024 +#endif + +struct dyld_static_pool { + dyld_static_pool* previousPool; + uint8_t* current; + uint8_t* end; + uint8_t pool[1]; +}; + +// allocate initial pool independently of pool header to take less space on disk +static uint8_t initialPoolContent[DYLD_POOL_CHUNK_SIZE] __attribute__((__aligned__(16))); +static dyld_static_pool initialPool = { NULL, initialPoolContent, &initialPoolContent[DYLD_POOL_CHUNK_SIZE] }; +static dyld_static_pool* currentPool = &initialPool; + + +void* malloc(size_t size) +{ + if ( (dyld::gLibSystemHelpers != NULL) && dyld::gProcessInfo->libSystemInitialized ) { + void* p = dyld::gLibSystemHelpers->malloc(size); + //dyld::log("malloc(%lu) => %p from libSystem\n", size, p); + return p; + } + else { + if ( size > DYLD_POOL_CHUNK_SIZE ) { + dyld::log("dyld malloc overflow: size=%zu\n", size); + exit(1); + } + size = (size+sizeof(void*)-1) & (-sizeof(void*)); // pointer align + uint8_t* result = currentPool->current; + currentPool->current += size; + if ( currentPool->current > currentPool->end ) { + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, DYLD_POOL_CHUNK_SIZE, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + dyld::log("out of address space for dyld memory pool\n"); + exit(1); + } + dyld_static_pool* newPool = (dyld_static_pool*)addr; + newPool->previousPool = NULL; + newPool->current = newPool->pool; + newPool->end = (uint8_t*)(addr + DYLD_POOL_CHUNK_SIZE); + newPool->previousPool = currentPool; + currentPool = newPool; + if ( (currentPool->current + size) > currentPool->end ) { + dyld::log("dyld memory pool exhausted: size=%lu\n", size); + exit(1); + } + result = currentPool->current; + currentPool->current += size; + } + //dyld::log("%p = malloc(%3lu) from pool %p, free space = %lu\n", result, size, currentPool, (long)(currentPool->end - currentPool->current)); + return result; + } +} + + +void free(void* ptr) +{ + // ignore any pointer within dyld (i.e. stuff from pool or static strings) + if ( (dyld::gLibSystemHelpers != NULL) && ((ptr < &__dso_handle) || (ptr >= &initialPoolContent[DYLD_POOL_CHUNK_SIZE])) ) { + // ignore stuff in any dynamically alloated dyld pools + for (dyld_static_pool* p = currentPool; p != NULL; p = p->previousPool) { + if ( (p->pool <= ptr) && (ptr < p->end) ) { + // do nothing, pool entries can't be reclaimed + //dyld::log("free(%p) from dynamic pool\n", ptr); + return; + } + } + + //dyld::log("free(%p) from libSystem\n", ptr); + return dyld::gLibSystemHelpers->free(ptr); + } + else { + // do nothing, pool entries can't be reclaimed + //dyld::log("free(%p) from static pool\n", ptr); + } +} + + +void* calloc(size_t count, size_t size) +{ + if ( dyld::gLibSystemHelpers != NULL ) { + void* result = dyld::gLibSystemHelpers->malloc(size*count); + bzero(result, size*count); + return result; + } + else { + // Check for overflow of integer multiplication + size_t total = count * size; + if ( total/count != size ) { + dyld::log("dyld calloc overflow: count=%zu, size=%zu\n", count, size); + exit(1); + } + return malloc(total); + } +} + + +void* realloc(void *ptr, size_t size) +{ + void* result = malloc(size); + memcpy(result, ptr, size); + return result; +} + +// void* reallocf(void *ptr, size_t size); +// void* valloc(size_t size); + +// needed __libc_init() +extern "C" int _malloc_lock; +int _malloc_lock = 0; + + +// dyld calls this which uses libSystem.dylib's vm_allocate if available +int vm_alloc(vm_address_t* addr, vm_size_t size, uint32_t flags) +{ + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 12) ) { + return dyld::gLibSystemHelpers->vm_alloc(mach_task_self(), addr, size, flags); + } + else { + return ::vm_allocate(mach_task_self(), addr, size, flags); + } +} + +void* xmmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + if ( (dyld::gLibSystemHelpers != NULL) && (dyld::gLibSystemHelpers->version >= 12) ) { + return dyld::gLibSystemHelpers->mmap(addr, len, prot, flags, fd, offset); + } + else { + return ::mmap(addr, len, prot, flags, fd, offset); + } +} + + + diff --git a/dyld/src/dyldStartup.s b/dyld/src/dyldStartup.s new file mode 100644 index 0000000..8f71e0c --- /dev/null +++ b/dyld/src/dyldStartup.s @@ -0,0 +1,341 @@ +/* + * Copyright (c) 1999-2011 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@ + */ +/* + * C runtime startup for interface to the dynamic linker. + * This is the same as the entry point in crt0.o with the addition of the + * address of the mach header passed as the an extra first argument. + * + * Kernel sets up stack frame to look like: + * + * | STRING AREA | + * +-------------+ + * | 0 | +* +-------------+ + * | apple[n] | + * +-------------+ + * : + * +-------------+ + * | apple[0] | + * +-------------+ + * | 0 | + * +-------------+ + * | env[n] | + * +-------------+ + * : + * : + * +-------------+ + * | env[0] | + * +-------------+ + * | 0 | + * +-------------+ + * | arg[argc-1] | + * +-------------+ + * : + * : + * +-------------+ + * | arg[0] | + * +-------------+ + * | argc | + * +-------------+ + * sp-> | mh | address of where the a.out's file offset 0 is in memory + * +-------------+ + * + * Where arg[i] and env[i] point into the STRING AREA + */ + + +#include + + .globl __dyld_start + +#ifdef __i386__ + +#if !TARGET_IPHONE_SIMULATOR + .text + .align 4, 0x90 + .globl __dyld_start +__dyld_start: + popl %edx # edx = mh of app + pushl $0 # push a zero for debugger end of frames marker + movl %esp,%ebp # pointer to base of kernel frame + andl $-16,%esp # force SSE alignment + subl $32,%esp # room for locals and outgoing parameters + + call L__dyld_start_picbase +L__dyld_start_picbase: + popl %ebx # set %ebx to runtime value of 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 + # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + movl %edx,(%esp) # param1 = app_mh + movl 4(%ebp),%eax + movl %eax,4(%esp) # param2 = argc + lea 8(%ebp),%eax + movl %eax,8(%esp) # param3 = argv + movl %ebx,12(%esp) # param4 = slide + movl %ecx,16(%esp) # param5 = actual load address + lea 28(%esp),%eax + movl %eax,20(%esp) # param6 = &startGlue + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + movl 28(%esp),%edx + cmpl $0,%edx + jne Lnew + + # clean up stack and jump to "start" in main executable + movl %ebp,%esp # restore the unaligned stack pointer + addl $4,%esp # remove debugger end frame marker + movl $0,%ebp # restore ebp back to zero + jmp *%eax # jump to the entry point + + # LC_MAIN case, set up stack for call to main() +Lnew: movl 4(%ebp),%ebx + movl %ebx,(%esp) # main param1 = argc + leal 8(%ebp),%ecx + movl %ecx,4(%esp) # main param2 = argv + leal 0x4(%ecx,%ebx,4),%ebx + movl %ebx,8(%esp) # main param3 = env +Lapple: movl (%ebx),%ecx # look for NULL ending env[] array + add $4,%ebx + testl %ecx,%ecx + jne Lapple # once found, next pointer is "apple" parameter now in %ebx + movl %ebx,12(%esp) # main param4 = apple + pushl %edx # simulate return address into _start in libdyld + jmp *%eax # jump to main(argc,argv,env,apple) with return address set to _start +#endif + +#if !TARGET_IPHONE_SIMULATOR + .data +__dyld_start_static_picbase: + .long L__dyld_start_picbase +Lmh: .long ___dso_handle +#endif + + +#endif /* __i386__ */ + + + +#if __x86_64__ +#if !TARGET_IPHONE_SIMULATOR + .data + .align 3 +__dyld_start_static: + .quad __dyld_start +#endif + + +#if !TARGET_IPHONE_SIMULATOR + .text + .align 2,0x90 + .globl __dyld_start +__dyld_start: + popq %rdi # param1 = mh of app + pushq $0 # push a zero for debugger end of frames marker + movq %rsp,%rbp # pointer to base of kernel frame + andq $-16,%rsp # force SSE alignment + subq $16,%rsp # room for local variables + + # call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + movl 8(%rbp),%esi # param2 = argc into %esi + leaq 16(%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 + leaq ___dso_handle(%rip),%r8 # param5 = dyldsMachHeader + leaq -8(%rbp),%r9 + call __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + movq -8(%rbp),%rdi + cmpq $0,%rdi + jne Lnew + + # clean up stack and jump to "start" in main executable + movq %rbp,%rsp # restore the unaligned stack pointer + addq $8,%rsp # remove the mh argument, and debugger end frame marker + movq $0,%rbp # restore ebp back to zero + jmp *%rax # jump to the entry point + + # LC_MAIN case, set up stack for call to main() +Lnew: addq $16,%rsp # remove local variables + pushq %rdi # simulate return address into _start in libdyld + movq 8(%rbp),%rdi # main param1 = argc into %rdi + leaq 16(%rbp),%rsi # main param2 = &argv[0] into %rsi + leaq 0x8(%rsi,%rdi,8),%rdx # main param3 = &env[0] into %rdx + movq %rdx,%rcx +Lapple: movq (%rcx),%r8 + add $8,%rcx + testq %r8,%r8 # look for NULL ending env[] array + jne Lapple # main param4 = apple into %rcx + jmp *%rax # jump to main(argc,argv,env,apple) with return address set to _start + +#endif /* TARGET_IPHONE_SIMULATOR */ +#endif /* __x86_64__ */ + + + +#if __arm__ + .syntax unified + .data + .align 2 +__dyld_start_static_picbase: + .long L__dyld_start_picbase + + + // 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: + mov r8, sp // save stack pointer + sub sp, #16 // make room for outgoing parameters + bic sp, sp, #15 // force 16-byte alignment + + // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + + ldr r3, L__dyld_start_picbase_ptr +L__dyld_start_picbase: + sub r0, pc, #8 // load actual PC + ldr r3, [r0, r3] // load expected PC + sub r3, r0, r3 // r3 = slide + + ldr r0, [r8] // r0 = mach_header + ldr r1, [r8, #4] // r1 = argc + add r2, r8, #8 // r2 = argv + + ldr r4, Lmh +L3: add r4, r4, pc + str r4, [sp, #0] // [sp] = dyld_mh + add r4, sp, #12 + str r4, [sp, #4] // [sp+4] = &startGlue + + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + ldr r5, [sp, #12] + cmp r5, #0 + bne Lnew + + // traditional case, 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 + + // LC_MAIN case, set up stack for call to main() +Lnew: mov lr, r5 // simulate return address into _start in libdyld + mov r5, r0 // save address of main() for later use + ldr r0, [r8, #4] // main param1 = argc + add r1, r8, #8 // main param2 = argv + add r2, r1, r0, lsl #2 + add r2, r2, #4 // main param3 = &env[0] + mov r3, r2 +Lapple: ldr r4, [r3] + add r3, #4 + cmp r4, #0 + bne Lapple // main param4 = apple + bx r5 + + .align 2 +L__dyld_start_picbase_ptr: + .long __dyld_start_static_picbase-L__dyld_start_picbase +Lmh: .long ___dso_handle-L3-8 + +#endif /* __arm__ */ + + + + +#if __arm64__ + .data + .align 3 +__dso_static: + .quad ___dso_handle + + .text + .align 2 + .globl __dyld_start +__dyld_start: + mov x28, sp + and sp, x28, #~15 // force 16-byte alignment of stack + mov x0, #0 + mov x1, #0 + stp x1, x0, [sp, #-16]! // make aligned terminating frame + mov fp, sp // set up fp to point to terminating frame + sub sp, sp, #16 // make room for local variables + ldr x0, [x28] // get app's mh into x0 + ldr x1, [x28, #8] // get argc into x1 (kernel passes 32-bit int argc as 64-bits on stack to keep alignment) + add x2, x28, #16 // get argv into x2 + adrp x4,___dso_handle@page + add x4,x4,___dso_handle@pageoff // get dyld's mh in to x4 + adrp x3,__dso_static@page + ldr x3,[x3,__dso_static@pageoff] // get unslid start of dyld + sub x3,x4,x3 // x3 now has slide of dyld + mov x5,sp // x5 has &startGlue + + // call dyldbootstrap::start(app_mh, argc, argv, slide, dyld_mh, &startGlue) + bl __ZN13dyldbootstrap5startEPK12macho_headeriPPKclS2_Pm + mov x16,x0 // save entry point address in x16 + ldr x1, [sp] + cmp x1, #0 + b.ne Lnew + + // LC_UNIXTHREAD way, clean up stack and jump to result + add sp, x28, #8 // restore unaligned stack pointer without app mh + br x16 // jump to the program's entry point + + // LC_MAIN case, set up stack for call to main() +Lnew: mov lr, x1 // simulate return address into _start in libdyld.dylib + ldr x0, [x28, #8] // main param1 = argc + add x1, x28, #16 // main param2 = argv + add x2, x1, x0, lsl #3 + add x2, x2, #8 // main param3 = &env[0] + mov x3, x2 +Lapple: ldr x4, [x3] + add x3, x3, #8 + cmp x4, #0 + b.ne Lapple // main param4 = apple + br x16 + +#endif // __arm64__ + + +// When iOS 10.0 simulator runs on 10.11, abort_with_payload() does not exist, +// so it falls back and uses dyld_fatal_error(). +#if TARGET_IPHONE_SIMULATOR + .text + .align 2 + .globl _dyld_fatal_error +_dyld_fatal_error: + int3 + nop +#endif + + + + + diff --git a/dyld/src/dyldSyscallInterface.h b/dyld/src/dyldSyscallInterface.h new file mode 100644 index 0000000..c8b90d3 --- /dev/null +++ b/dyld/src/dyldSyscallInterface.h @@ -0,0 +1,108 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifndef __DYLD_SYSCALL_HELPERS__ +#define __DYLD_SYSCALL_HELPERS__ + +#include +#include + +#if __cplusplus +namespace dyld { +#endif + + // + // This file contains the table of function pointers the host dyld supplies + // to the iOS simulator dyld. + // + struct SyscallHelpers + { + uintptr_t version; + int (*open)(const char* path, int oflag, int extra); + int (*close)(int fd); + ssize_t (*pread)(int fd, void* buf, size_t nbyte, off_t offset); + ssize_t (*write)(int fd, const void* buf, size_t nbyte); + void* (*mmap)(void* addr, size_t len, int prot, int flags, int fd, off_t offset); + int (*munmap)(void* addr, size_t len); + int (*madvise)(void* addr, size_t len, int advice); + int (*stat)(const char* path, struct stat* buf); + int (*fcntl)(int fildes, int cmd, void* result); + int (*ioctl)(int fildes, unsigned long request, void* result); + int (*issetugid)(void); + char* (*getcwd)(char* buf, size_t size); + char* (*realpath)(const char* file_name, char* resolved_name); + kern_return_t (*vm_allocate)(vm_map_t target_task, vm_address_t *address, vm_size_t size, int flags); + kern_return_t (*vm_deallocate)(vm_map_t target_task, vm_address_t address, vm_size_t size); + kern_return_t (*vm_protect)(vm_map_t target_task, vm_address_t address, vm_size_t size, boolean_t max, vm_prot_t prot); + void (*vlog)(const char* format, va_list list); + void (*vwarn)(const char* format, va_list list); + int (*pthread_mutex_lock)(pthread_mutex_t* m); + int (*pthread_mutex_unlock)(pthread_mutex_t* m); + mach_port_t (*mach_thread_self)(void); + kern_return_t (*mach_port_deallocate)(ipc_space_t task, mach_port_name_t name); + mach_port_name_t(*task_self_trap)(void); + kern_return_t (*mach_timebase_info)(mach_timebase_info_t info); + bool (*OSAtomicCompareAndSwapPtrBarrier)(void* old, void* nw, void * volatile *value); + void (*OSMemoryBarrier)(void); + void* (*getProcessInfo)(void); // returns dyld_all_image_infos*; + int* (*errnoAddress)(); + uint64_t (*mach_absolute_time)(); + // Added in version 2 + kern_return_t (*thread_switch)(mach_port_name_t, int, mach_msg_timeout_t); + // Added in version 3 + DIR* (*opendir)(const char* path); + int (*readdir_r)(DIR* dirp, struct dirent* entry, struct dirent **result); + int (*closedir)(DIR* dirp); + // Added in version 4 + void (*coresymbolication_load_notifier)(void *connection, uint64_t load_timestamp, const char *image_path, const struct mach_header *mach_header); + void (*coresymbolication_unload_notifier)(void *connection, uint64_t unload_timestamp, const char *image_path, const struct mach_header *mach_header); + // Added in version 5 + int (*proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t buffersize); + int (*getpid)(); + kern_return_t (*mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly); + kern_return_t (*mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*); + kern_return_t (*mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t); + // Added in version 6 + void (*abort_with_payload)(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags); + // Add in version 7 + kern_return_t (*task_register_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); + kern_return_t (*task_unregister_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt); + kern_return_t (*task_get_dyld_image_infos)(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt); + kern_return_t (*task_register_dyld_shared_cache_image_info)(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache); + kern_return_t (*task_register_dyld_set_dyld_state)(task_t task, uint8_t dyld_state); + kern_return_t (*task_register_dyld_get_process_state)(task_t task, dyld_kernel_process_info_t *dyld_process_state); + kern_return_t (*task_info)(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt); + kern_return_t (*thread_info)(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt); + bool (*kdebug_is_enabled)(uint32_t code); + int (*kdebug_trace)(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4); + }; + extern const struct SyscallHelpers* gSyscallHelpers; + + +#if __cplusplus +} +#endif + +#endif diff --git a/dyld/src/dyld_gdb.cpp b/dyld/src/dyld_gdb.cpp new file mode 100644 index 0000000..5f4e7b5 --- /dev/null +++ b/dyld/src/dyld_gdb.cpp @@ -0,0 +1,243 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include + +#include + +#include "mach-o/dyld_gdb.h" +#include "mach-o/dyld_images.h" +#include "mach-o/dyld_process_info.h" +#include "ImageLoader.h" +#include "dyld.h" + +extern "C" void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]); + +#if __IPHONE_OS_VERSION_MIN_REQUIRED + #define INITIAL_UUID_IMAGE_COUNT 4 +#else + #define INITIAL_UUID_IMAGE_COUNT 32 +#endif + +VECTOR_NEVER_DESTRUCTED(dyld_image_info); +VECTOR_NEVER_DESTRUCTED(dyld_uuid_info); + +static std::vector sImageInfos; +static std::vector sImageUUIDs; + +size_t allImagesCount() +{ + return sImageInfos.size(); +} + +const mach_header* allImagesIndexedMachHeader(uint32_t index) +{ + if ( index < sImageInfos.size() ) + return sImageInfos[index].imageLoadAddress; + else + return NULL; +} + +const char* allImagesIndexedPath(uint32_t index) +{ + if ( index < sImageInfos.size() ) + return sImageInfos[index].imageFilePath; + else + return NULL; +} + + +void addImagesToAllImages(uint32_t infoCount, const dyld_image_info info[]) +{ + // make initial size large enough that we probably won't need to re-alloc it + 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::gProcessInfo->infoArray = NULL; + + // append all new images + for (uint32_t i=0; i < infoCount; ++i) + sImageInfos.push_back(info[i]); + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); + dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time(); + + // set infoArray back to base address of vector (other process can now read) + dyld::gProcessInfo->infoArray = &sImageInfos[0]; +} + +#if TARGET_IPHONE_SIMULATOR +// called once in dyld_sim start up to copy image list from host dyld to sImageInfos +void syncProcessInfo() +{ + // may want to set version field of gProcessInfo if it might be different than host + if ( sImageInfos.size() == 0 ) { + sImageInfos.reserve(INITIAL_IMAGE_COUNT); + if ( dyld::gProcessInfo->infoArray != NULL ) { + for (uint32_t i=0; i < dyld::gProcessInfo->infoArrayCount; ++i) { + sImageInfos.push_back(dyld::gProcessInfo->infoArray[i]); + } + dyld::gProcessInfo->infoArray = &sImageInfos[0]; + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); + } + } + dyld::gProcessInfo->notification(dyld_image_info_change, 0, NULL); +} +#endif + +const char* notifyGDB(enum dyld_image_states state, uint32_t infoCount, const dyld_image_info info[]) +{ + // tell gdb that about the new images + uint64_t t0 = mach_absolute_time(); + dyld::gProcessInfo->notification(dyld_image_adding, infoCount, info); + uint64_t t1 = mach_absolute_time(); + ImageLoader::fgTotalDebuggerPausedTime += (t1-t0); + + // record initial count of images + // so CrashReporter can note which images were dynamically loaded + if ( dyld::gProcessInfo->initialImageCount == 0 ) + dyld::gProcessInfo->initialImageCount = dyld::gProcessInfo->infoArrayCount; + return NULL; +} + + + +void addNonSharedCacheImageUUID(const dyld_uuid_info& info) +{ + // set uuidArray to NULL to denote it is in-use + dyld::gProcessInfo->uuidArray = NULL; + + // append all new images + sImageUUIDs.push_back(info); + dyld::gProcessInfo->uuidArrayCount = sImageUUIDs.size(); + + // set uuidArray back to base address of vector (other process can now read) + dyld::gProcessInfo->uuidArray = &sImageUUIDs[0]; +} + +void removeImageFromAllImages(const struct mach_header* loadAddress) +{ + dyld_image_info goingAway; + + // set infoArray to NULL to denote it is in-use + dyld::gProcessInfo->infoArray = NULL; + + // remove image from infoArray + for (std::vector::iterator it=sImageInfos.begin(); it != sImageInfos.end(); it++) { + if ( it->imageLoadAddress == loadAddress ) { + goingAway = *it; + sImageInfos.erase(it); + break; + } + } + dyld::gProcessInfo->infoArrayCount = (uint32_t)sImageInfos.size(); + + // set infoArray back to base address of vector + dyld::gProcessInfo->infoArray = &sImageInfos[0]; + + + // set uuidArrayCount to NULL to denote it is in-use + dyld::gProcessInfo->uuidArray = NULL; + + // remove image from infoArray + for (std::vector::iterator it=sImageUUIDs.begin(); it != sImageUUIDs.end(); it++) { + if ( it->imageLoadAddress == loadAddress ) { + sImageUUIDs.erase(it); + break; + } + } + dyld::gProcessInfo->uuidArrayCount = sImageUUIDs.size(); + dyld::gProcessInfo->infoArrayChangeTimestamp = mach_absolute_time(); + + // set infoArray back to base address of vector + dyld::gProcessInfo->uuidArray = &sImageUUIDs[0]; + + // tell gdb that about the new images + dyld::gProcessInfo->notification(dyld_image_removing, 1, &goingAway); +} + + +#if TARGET_IPHONE_SIMULATOR + namespace dyld { + struct dyld_all_image_infos* gProcessInfo = NULL; + } +#else + + static void gdb_image_notifier(enum dyld_image_mode mode, uint32_t infoCount, const dyld_image_info info[]) + { + uint64_t machHeaders[infoCount]; + for (uint32_t i=0; i < infoCount; ++i) { + machHeaders[i] = (uintptr_t)(info[i].imageLoadAddress); + } + switch ( mode ) { + case dyld_image_adding: + _dyld_debugger_notification(dyld_notify_adding, infoCount, machHeaders); + break; + case dyld_image_removing: + _dyld_debugger_notification(dyld_notify_removing, infoCount, machHeaders); + break; + default: + break; + } + // do nothing + // gdb sets a break point here to catch notifications + //dyld::log("dyld: gdb_image_notifier(%s, %d, ...)\n", mode ? "dyld_image_removing" : "dyld_image_adding", infoCount); + //for (uint32_t i=0; i < infoCount; ++i) + // dyld::log("dyld: %d loading at %p %s\n", i, info[i].imageLoadAddress, info[i].imageFilePath); + //for (uint32_t i=0; i < dyld::gProcessInfo->infoArrayCount; ++i) + // dyld::log("dyld: %d loading at %p %s\n", i, dyld::gProcessInfo->infoArray[i].imageLoadAddress, dyld::gProcessInfo->infoArray[i].imageFilePath); + } + + // only used with accelerator tables and ASan which images need to be re-loaded + void resetAllImages() + { + sImageInfos.clear(); + sImageUUIDs.clear(); + _dyld_debugger_notification(dyld_notify_remove_all, 0, NULL); + } + + extern void* __dso_handle; + #define STR(s) # s + #define XSTR(s) STR(s) + + struct dyld_all_image_infos dyld_all_image_infos __attribute__ ((section ("__DATA,__all_image_info"))) + = { + 15, 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, 0, NULL, NULL, NULL, 0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}, + 0, 0, "/usr/lib/dyld", {0}, {0} + }; + + struct dyld_shared_cache_ranges dyld_shared_cache_ranges; + + namespace dyld { + struct dyld_all_image_infos* gProcessInfo = &dyld_all_image_infos; + } +#endif + diff --git a/dyld/src/dyld_process_info.cpp b/dyld/src/dyld_process_info.cpp new file mode 100644 index 0000000..fc47bce --- /dev/null +++ b/dyld/src/dyld_process_info.cpp @@ -0,0 +1,726 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_process_info.h" +#include "dyld_process_info_internal.h" +#include "dyld_images.h" +#include "dyld_priv.h" + +// this was in dyld_priv.h but it is no longer exported +extern "C" { + const struct dyld_all_image_infos* _dyld_get_all_image_infos(); +} + +namespace { + +void withRemoteBuffer(task_t task, vm_address_t remote_address, size_t remote_size, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer, size_t size)) { + kern_return_t r = KERN_SUCCESS; + mach_vm_address_t local_address = 0; + mach_vm_address_t local_size = remote_size; + while (1) { + vm_prot_t cur_protection, max_protection; + r = mach_vm_remap(mach_task_self(), + &local_address, + local_size, + 0, // mask + VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | VM_FLAGS_RESILIENT_CODESIGN, + task, + remote_address, + TRUE, // Copy semantics: changes to this memory by the target process will not be visible in this process + &cur_protection, + &max_protection, + VM_INHERIT_DEFAULT); + //Do this here to allow chaining of multiple embedded blocks with a single error out; + if (kr != NULL) { + *kr = r; + } + if (r == KERN_SUCCESS) { + // We got someting, call the block and then exit + block(reinterpret_cast(local_address), local_size); + vm_deallocate(mach_task_self(), local_address, local_size); + break; + } + if (!allow_truncation) { + break; + } + // We did not get something, but we are allowed to truncate and try again + uint64_t trunc_size = ((remote_address + local_size - 1) & PAGE_MASK) + 1; + if (local_size <= trunc_size) { + //Even if we truncate it will be in the same page, time to accept defeat + break; + } else { + local_size -= trunc_size; + } + } +} + +template +void withRemoteObject(task_t task, vm_address_t remote_address, kern_return_t *kr, void (^block)(T t)) +{ + withRemoteBuffer(task, remote_address, sizeof(T), false, kr, ^(void *buffer, size_t size) { + block(*reinterpret_cast(buffer)); + }); +} +}; + +// +// Opaque object returned by _dyld_process_info_create() +// + +struct __attribute__((visibility("hidden"))) dyld_process_info_base { + static dyld_process_info_base* make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr); + static dyld_process_info_base* makeSuspended(task_t task, kern_return_t* kr); + + uint32_t& retainCount() const { return _retainCount; } + dyld_process_cache_info* cacheInfo() const { return (dyld_process_cache_info*)(((char*)this) + _cacheInfoOffset); } + dyld_process_state_info* stateInfo() const { return (dyld_process_state_info*)(((char*)this) + _stateInfoOffset); } + void forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const; + void forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const; + +private: + struct ImageInfo { + uuid_t uuid; + uint64_t loadAddress; + const char* path; + uint32_t segmentStartIndex; + uint32_t segmentsCount; + }; + + struct SegmentInfo { + const char* name; + uint64_t addr; + uint64_t size; + }; + + dyld_process_info_base(unsigned imageCount, size_t totalSize); + void* operator new (size_t, void* buf) { return buf; } + + static bool inCache(uint64_t addr) { return (addr > SHARED_REGION_BASE) && (addr < SHARED_REGION_BASE+SHARED_REGION_SIZE); } + kern_return_t addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal); + kern_return_t addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath); + + bool invalid() { return ((char*)_stringRevBumpPtr < (char*)_curSegment); } + const char* copyPath(task_t task, uint64_t pathAddr, kern_return_t* kr); + const char* addString(const char*, size_t); + const char* copySegmentName(const char*); + + void addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size); + + void inspectLocalImageLoadCommands(uint64_t imageAddress, void* func); + kern_return_t inspectRemoteImageLoadCommands(task_t task, uint64_t imageAddress, void* func); + + mutable uint32_t _retainCount; + const uint32_t _cacheInfoOffset; + const uint32_t _stateInfoOffset; + const uint32_t _imageInfosOffset; + const uint32_t _segmentInfosOffset; + ImageInfo* const _firstImage; + ImageInfo* _curImage; + SegmentInfo* const _firstSegment; + SegmentInfo* _curSegment; + uint32_t _curSegmentIndex; + char* _stringRevBumpPtr; + + // dyld_process_cache_info cacheInfo; + // dyld_process_state_info stateInfo; + // ImageInfo images[]; + // SegmentInfo segments[]; + // char stringPool[] +}; + +dyld_process_info_base::dyld_process_info_base(unsigned imageCount, size_t totalSize) + : _retainCount(1), _cacheInfoOffset(sizeof(dyld_process_info_base)), + _stateInfoOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info)), + _imageInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info)), + _segmentInfosOffset(sizeof(dyld_process_info_base) + sizeof(dyld_process_cache_info) + sizeof(dyld_process_state_info) + imageCount*sizeof(ImageInfo)), + _firstImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), + _curImage((ImageInfo*)(((uint8_t*)this) + _imageInfosOffset)), + _firstSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), + _curSegment((SegmentInfo*)(((uint8_t*)this) + _segmentInfosOffset)), + _curSegmentIndex(0), + _stringRevBumpPtr((char*)(this)+totalSize) +{ +} + + +dyld_process_info_base* dyld_process_info_base::make(task_t task, const dyld_all_image_infos_64& allImageInfo, const dyld_image_info_64 imageArray[], kern_return_t* kr) +{ + // figure out how many path strings will need to be copied and their size + const dyld_all_image_infos* myInfo = _dyld_get_all_image_infos(); + bool sameCacheAsThisProcess = !allImageInfo.processDetachedFromSharedRegion + && !myInfo->processDetachedFromSharedRegion + && ((memcmp(myInfo->sharedCacheUUID, allImageInfo.sharedCacheUUID, 16) == 0) + && (myInfo->sharedCacheSlide == allImageInfo.sharedCacheSlide)); + unsigned countOfPathsNeedingCopying = 0; + if ( sameCacheAsThisProcess ) { + for (int i=0; i < allImageInfo.infoArrayCount; ++i) { + if ( !inCache(imageArray[i].imageFilePath) ) + ++countOfPathsNeedingCopying; + } + } + else { + countOfPathsNeedingCopying = allImageInfo.infoArrayCount+1; + } + unsigned imageCountWithDyld = allImageInfo.infoArrayCount+1; + + // allocate result object + size_t allocationSize = sizeof(dyld_process_info_base) + + sizeof(dyld_process_cache_info) + + sizeof(dyld_process_state_info) + + sizeof(ImageInfo)*(imageCountWithDyld) + + sizeof(SegmentInfo)*imageCountWithDyld*5 + + countOfPathsNeedingCopying*PATH_MAX; + void* storage = malloc(allocationSize); + dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCountWithDyld, allocationSize); // placement new() + + // fill in base info + dyld_process_cache_info* cacheInfo = obj->cacheInfo(); + memcpy(cacheInfo->cacheUUID, allImageInfo.sharedCacheUUID, 16); + cacheInfo->cacheBaseAddress = allImageInfo.sharedCacheBaseAddress; + cacheInfo->privateCache = allImageInfo.processDetachedFromSharedRegion; + // if no cache is used, allImageInfo has all zeros for cache UUID + cacheInfo->noCache = true; + for (int i=0; i < 16; ++i) { + if ( cacheInfo->cacheUUID[i] != 0 ) { + cacheInfo->noCache = false; + } + } + + dyld_process_state_info* stateInfo = obj->stateInfo(); + stateInfo->timestamp = allImageInfo.infoArrayChangeTimestamp; + stateInfo->imageCount = imageCountWithDyld; + stateInfo->initialImageCount = (uint32_t)(allImageInfo.initialImageCount+1); + if ( allImageInfo.infoArray != 0 ) + stateInfo->dyldState = dyld_process_state_dyld_initialized; + if ( allImageInfo.libSystemInitialized != 0 ) { + stateInfo->dyldState = dyld_process_state_libSystem_initialized; + if ( allImageInfo.initialImageCount != allImageInfo.infoArrayCount ) + stateInfo->dyldState = dyld_process_state_program_running; + } + if ( allImageInfo.errorMessage != 0 ) + stateInfo->dyldState = allImageInfo.terminationFlags ? dyld_process_state_terminated_before_inits : dyld_process_state_dyld_terminated; + + // fill in info for dyld + if ( allImageInfo.dyldPath != 0 ) { + if ( kern_return_t r = obj->addDyldImage(task, allImageInfo.dyldImageLoadAddress, allImageInfo.dyldPath, NULL) ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + } + + // fill in info for each image + for (uint32_t i=0; i < allImageInfo.infoArrayCount; ++i) { + if ( kern_return_t r = obj->addImage(task, sameCacheAsThisProcess, imageArray[i].imageLoadAddress, imageArray[i].imageFilePath, NULL) ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + } + + // sanity check internal data did not overflow + if ( obj->invalid() ) + goto fail; + + return obj; + +fail: + free(obj); + return NULL; +} + +dyld_process_info_base* dyld_process_info_base::makeSuspended(task_t task, kern_return_t* kr) +{ + pid_t pid; + kern_return_t result = pid_for_task(task, &pid); + if ( result != KERN_SUCCESS ) { + if ( kr != NULL ) + *kr = result; + return NULL; + } + + __block unsigned imageCount = 0; // main executable and dyld + __block uint64_t mainExecutableAddress = 0; + __block uint64_t dyldAddress = 0; + char dyldPathBuffer[PATH_MAX+1]; + char mainExecutablePathBuffer[PATH_MAX+1]; + __block char * dyldPath = &dyldPathBuffer[0]; + __block char * mainExecutablePath = &mainExecutablePathBuffer[0]; + mach_vm_size_t size; + for (mach_vm_address_t address = 0; ; address += size) { + vm_region_basic_info_data_64_t info; + mach_port_t objectName; + unsigned int infoCount = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(task, &address, &size, VM_REGION_BASIC_INFO, + (vm_region_info_t)&info, &infoCount, &objectName); + if ( result != KERN_SUCCESS ) + break; + if ( info.protection != (VM_PROT_READ|VM_PROT_EXECUTE) ) + continue; + // read start of vm region to verify it is a mach header + withRemoteObject(task, address, NULL, ^(mach_header_64 mhBuffer){ + if ( (mhBuffer.magic != MH_MAGIC) && (mhBuffer.magic != MH_MAGIC_64) ) + return; + // now know the region is the start of a mach-o file + if ( mhBuffer.filetype == MH_EXECUTE ) { + mainExecutableAddress = address; + int len = proc_regionfilename(pid, mainExecutableAddress, mainExecutablePath, PATH_MAX); + if ( len != 0 ) { + mainExecutablePath[len] = '\0'; + } + ++imageCount; + } + else if ( mhBuffer.filetype == MH_DYLINKER ) { + dyldAddress = address; + int len = proc_regionfilename(pid, dyldAddress, dyldPath, PATH_MAX); + if ( len != 0 ) { + dyldPath[len] = '\0'; + } + ++imageCount; + } + }); + //fprintf(stderr, "vm region: addr=0x%llX, size=0x%llX, prot=0x%X\n", (uint64_t)address, (uint64_t)size, info.protection); + } + //fprintf(stderr, "dyld: addr=0x%llX, path=%s\n", dyldAddress, dyldPathBuffer); + //fprintf(stderr, "app: addr=0x%llX, path=%s\n", mainExecutableAddress, mainExecutablePathBuffer); + + // allocate result object + size_t allocationSize = sizeof(dyld_process_info_base) + + sizeof(dyld_process_cache_info) + + sizeof(dyld_process_state_info) + + sizeof(ImageInfo)*(imageCount) + + sizeof(SegmentInfo)*imageCount*5 + + imageCount*PATH_MAX; + void* storage = malloc(allocationSize); + dyld_process_info_base* obj = new (storage) dyld_process_info_base(imageCount, allocationSize); // placement new() + + // fill in base info + dyld_process_cache_info* cacheInfo = obj->cacheInfo(); + bzero(cacheInfo->cacheUUID, 16); + cacheInfo->cacheBaseAddress = 0; + cacheInfo->noCache = true; + cacheInfo->privateCache = false; + + dyld_process_state_info* stateInfo = obj->stateInfo(); + stateInfo->timestamp = 0; + stateInfo->imageCount = imageCount; + stateInfo->initialImageCount = imageCount; + stateInfo->dyldState = dyld_process_state_not_started; + + // fill in info for dyld + if ( dyldAddress != 0 ) { + if ( kern_return_t r = obj->addDyldImage(task, dyldAddress, 0, dyldPath) ) { + if ( kr != NULL ) + *kr = r; + free(obj); + return NULL; + } + } + + // fill in info for each image + if ( mainExecutableAddress != 0 ) { + if ( kern_return_t r = obj->addImage(task, false, mainExecutableAddress, 0, mainExecutablePath) ) { + if ( kr != NULL ) + *kr = r; + free(obj); + return NULL; + } + } + + return obj; +} + + + +const char* dyld_process_info_base::addString(const char* str, size_t maxlen) +{ + size_t len = strnlen(str, maxlen) + 1; + _stringRevBumpPtr -= len; + strlcpy(_stringRevBumpPtr, str, len); + return _stringRevBumpPtr; +} + +const char* dyld_process_info_base::copyPath(task_t task, uint64_t stringAddressInTask, kern_return_t* kr) +{ + __block const char* retval = NULL; + withRemoteBuffer(task, stringAddressInTask, PATH_MAX, true, kr, ^(void *buffer, size_t size) { + retval = addString(static_cast(buffer), size); + }); + + return retval; +} + +kern_return_t dyld_process_info_base::addImage(task_t task, bool sameCacheAsThisProcess, uint64_t imageAddress, uint64_t imagePath, const char* imagePathLocal) +{ + _curImage->loadAddress = imageAddress; + _curImage->segmentStartIndex = _curSegmentIndex; + if ( imagePathLocal != NULL ) { + _curImage->path = addString(imagePathLocal, PATH_MAX); + } + else if ( sameCacheAsThisProcess && inCache(imagePath) ) { + _curImage->path = (const char*)imagePath; + } + else { + kern_return_t kr; + _curImage->path = copyPath(task, imagePath, &kr); + if ( kr ) + return kr; + } + if ( sameCacheAsThisProcess && inCache(imageAddress) ) { + addInfoFromLoadCommands((mach_header*)imageAddress, imageAddress, 32*1024); + } + else { + __block kern_return_t kr = KERN_SUCCESS; + withRemoteObject(task, imageAddress, &kr, ^(mach_header_64 mhBuffer) { + size_t headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds; + withRemoteBuffer(task, imageAddress, headerPagesSize, false, &kr, ^(void * buffer, size_t size) { + addInfoFromLoadCommands((mach_header*)buffer, imageAddress, size); + }); + }); + if (kr != KERN_SUCCESS) { + return kr; + } + } + _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; + _curImage++; + return KERN_SUCCESS; +} + + +kern_return_t dyld_process_info_base::addDyldImage(task_t task, uint64_t dyldAddress, uint64_t dyldPathAddress, const char* localPath) +{ + __block kern_return_t kr = KERN_SUCCESS; + _curImage->loadAddress = dyldAddress; + _curImage->segmentStartIndex = _curSegmentIndex; + if ( localPath != NULL ) { + _curImage->path = addString(localPath, PATH_MAX); + } + else { + _curImage->path = copyPath(task, dyldPathAddress, &kr); + if ( kr ) + return kr; + } + + withRemoteObject(task, dyldAddress, &kr, ^(mach_header_64 mhBuffer) { + size_t headerPagesSize = sizeof(mach_header_64) + mhBuffer.sizeofcmds; + withRemoteBuffer(task, dyldAddress, headerPagesSize, false, &kr, ^(void * buffer, size_t size) { + addInfoFromLoadCommands((mach_header*)buffer, dyldAddress, size); + }); + }); + if (kr != KERN_SUCCESS) { + return kr; + } + + _curImage->segmentsCount = _curSegmentIndex - _curImage->segmentStartIndex; + _curImage++; + return KERN_SUCCESS; +} + + +void dyld_process_info_base::addInfoFromLoadCommands(const mach_header* mh, uint64_t addressInTask, size_t size) +{ + const load_command* startCmds = NULL; + if ( mh->magic == MH_MAGIC_64 ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header_64)); + else if ( mh->magic == MH_MAGIC ) + startCmds = (load_command*)((char *)mh + sizeof(mach_header)); + else + return; // not a mach-o file, or wrong endianness + + const load_command* const cmdsEnd = (load_command*)((char*)startCmds + mh->sizeofcmds); + const load_command* cmd = startCmds; + for(uint32_t i = 0; i < mh->ncmds; ++i) { + const load_command* nextCmd = (load_command*)((char *)cmd + cmd->cmdsize); + if ( (cmd->cmdsize < 8) || (nextCmd > cmdsEnd) || (nextCmd < startCmds) ) { + return; // malformed load command + } + if ( cmd->cmd == LC_UUID ) { + const uuid_command* uuidCmd = (uuid_command*)cmd; + memcpy(_curImage->uuid, uuidCmd->uuid, 16); + } + else if ( cmd->cmd == LC_SEGMENT ) { + const segment_command* segCmd = (segment_command*)cmd; + _curSegment->name = copySegmentName(segCmd->segname); + _curSegment->addr = segCmd->vmaddr; + _curSegment->size = segCmd->vmsize; + _curSegment++; + _curSegmentIndex++; + } + else if ( cmd->cmd == LC_SEGMENT_64 ) { + const segment_command_64* segCmd = (segment_command_64*)cmd; + _curSegment->name = copySegmentName(segCmd->segname); + _curSegment->addr = segCmd->vmaddr; + _curSegment->size = segCmd->vmsize; + _curSegment++; + _curSegmentIndex++; + } + cmd = nextCmd; + } +} + +const char* dyld_process_info_base::copySegmentName(const char* name) +{ + // don't copy names of standard segments into string pool + static const char* stdSegNames[] = {"__TEXT", "__DATA", "__LINKEDIT", "__DATA_DIRTY", "__DATA_CONST", "__OBJC", NULL }; + for (const char** s=stdSegNames; *s != NULL; ++s) { + if ( strcmp(name, *s) == 0 ) + return *s; + } + // copy custom segment names into string pool + return addString(name, 16); +} + +void dyld_process_info_base::forEachImage(void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) const +{ + for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { + callback(p->loadAddress, p->uuid, p->path); + } +} + +void dyld_process_info_base::forEachSegment(uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) const +{ + for (const ImageInfo* p = _firstImage; p < _curImage; ++p) { + if ( p->loadAddress == machHeaderAddress ) { + uint64_t slide = 0; + for (int i=0; i < p->segmentsCount; ++i) { + const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; + if ( strcmp(seg->name, "__TEXT") == 0 ) { + slide = machHeaderAddress - seg->addr; + break; + } + } + for (int i=0; i < p->segmentsCount; ++i) { + const SegmentInfo* seg = &_firstSegment[p->segmentStartIndex+i]; + callback(seg->addr + slide, seg->size, seg->name); + } + break; + } + } +} + + + + + +// Implementation that works with existing dyld data structures +static dyld_process_info _dyld_process_info_create_inner(task_t task, uint64_t timestamp, kern_return_t* kr) +{ + if ( kr != NULL ) + *kr = KERN_SUCCESS; + + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if ( kern_return_t r = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + + //The kernel will return MACH_VM_MIN_ADDRESS for an executable that has not had dyld loaded + if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS) + return NULL; + + if ( task_dyld_info.all_image_info_size > sizeof(dyld_all_image_infos_64) ) + return NULL; + + // read all_image_infos struct + dyld_all_image_infos_64 allImageInfo64; + mach_vm_size_t readSize = task_dyld_info.all_image_info_size; + if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + if ( allImageInfo64.infoArrayCount == 0 ) { + // could be task was launch suspended or still launching, wait a moment to see + usleep(1000 * 50); // 50ms + if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64, &readSize) ) { + if ( kr != NULL ) + *kr = r; + return NULL; + } + // if infoArrayCount is still zero, then target was most likely launched suspended + if ( allImageInfo64.infoArrayCount == 0 ) + return dyld_process_info_base::makeSuspended(task, kr); + } + + // bail out of dyld is too old + if ( allImageInfo64.version < 15 ) { + if ( kr != NULL ) + *kr = KERN_INVALID_HOST; + return NULL; + } + + // normalize by expanding 32-bit all_image_infos into 64-bit one + uint32_t imageCount = allImageInfo64.infoArrayCount; + size_t imageArraySize = imageCount * sizeof(dyld_image_info_64); + if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { + const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64; + dyld_all_image_infos_64 info64; + bzero(&info64, sizeof(info64)); + info64.version = allImageInfo32->version; + info64.infoArrayCount = allImageInfo32->infoArrayCount; + info64.infoArray = allImageInfo32->infoArray; + info64.processDetachedFromSharedRegion = allImageInfo32->processDetachedFromSharedRegion; + info64.libSystemInitialized = allImageInfo32->libSystemInitialized; + info64.dyldImageLoadAddress = allImageInfo32->dyldImageLoadAddress; + info64.initialImageCount = allImageInfo32->initialImageCount; + info64.uuidArrayCount = allImageInfo32->uuidArrayCount; + info64.uuidArray = allImageInfo32->uuidArray; + info64.dyldAllImageInfosAddress = allImageInfo32->dyldAllImageInfosAddress; + info64.sharedCacheSlide = allImageInfo32->sharedCacheSlide; + info64.infoArrayChangeTimestamp = allImageInfo32->infoArrayChangeTimestamp; + info64.sharedCacheBaseAddress = allImageInfo32->sharedCacheBaseAddress; + info64.dyldPath = allImageInfo32->dyldPath; + memcpy((void*)(info64.sharedCacheUUID), (void*)(allImageInfo32->sharedCacheUUID), 16); + allImageInfo64 = info64; + imageCount = allImageInfo64.infoArrayCount; + imageArraySize = imageCount * sizeof(dyld_image_info_32); + } + + // don't do any (more) work if target process's dyld timestamp has not changed since previous query + if ( (timestamp != 0) && (timestamp == allImageInfo64.infoArrayChangeTimestamp) ) { + if ( kr != NULL ) + *kr = KERN_SUCCESS; + return NULL; + } + + // For the moment we are going to truncate any image list longer than 8192 because some programs do + // terrible things that corrupt their own image lists and we need to stop clients from crashing + // reading them. We can try to do something more advanced in the future. rdar://27446361 + imageCount = MIN(imageCount, 8192); + + // read image array + if ( allImageInfo64.infoArray == 0 ) { + // dyld is in middle of updating image list, try again + return NULL; + } + dyld_image_info_64 imageArray64[imageCount]; + if ( kern_return_t r = mach_vm_read_overwrite(task, allImageInfo64.infoArray, imageArraySize, (vm_address_t)&imageArray64, &readSize) ) { + // if image array moved, try whole thing again + if ( kr != NULL ) { + *kr = r; + } + return NULL; + } + // normalize by expanding 32-bit image_infos into 64-bit ones + if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { + const dyld_image_info_32* imageArray32 = (dyld_image_info_32*)&imageArray64; + dyld_image_info_64 tempArray[imageCount]; + for (uint32_t i=0; i < imageCount; ++i) { + tempArray[i].imageLoadAddress = imageArray32[i].imageLoadAddress; + tempArray[i].imageFilePath = imageArray32[i].imageFilePath; + tempArray[i].imageFileModDate = imageArray32[i].imageFileModDate; + } + memcpy(imageArray64, tempArray, sizeof(dyld_image_info_64)*imageCount); + } + + // create object based on local copy of all image infos and image array + dyld_process_info result = dyld_process_info_base::make(task, allImageInfo64, imageArray64, kr); + + // verify nothing has changed by re-reading all_image_infos struct and checking timestamp + if ( result != NULL ) { + dyld_all_image_infos_64 allImageInfo64again; + readSize = task_dyld_info.all_image_info_size; + if ( kern_return_t r = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&allImageInfo64again, &readSize) ) { + if ( kr != NULL ) + *kr = r; + free((void*)result); + return NULL; + } + uint64_t doneTimeStamp = allImageInfo64again.infoArrayChangeTimestamp; + if ( task_dyld_info.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) { + const dyld_all_image_infos_32* allImageInfo32 = (dyld_all_image_infos_32*)&allImageInfo64again; + doneTimeStamp = allImageInfo32->infoArrayChangeTimestamp; + } + if ( allImageInfo64.infoArrayChangeTimestamp != doneTimeStamp ) { + // image list has changed since we started reading it + // throw out what we have and start over + free((void*)result); + result = nullptr; + } + } + + return result; +} + + +dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kr) +{ + // Other process may be loading and unloading as we read its memory, which can cause a read failure + // Retry if something fails + for (int i=0; i < 100; ++i) { + if ( dyld_process_info result = _dyld_process_info_create_inner( task, timestamp, kr) ) + return result; + + } + return NULL; +} + +void _dyld_process_info_get_state(dyld_process_info info, dyld_process_state_info* stateInfo) +{ + *stateInfo = *info->stateInfo(); +} + +void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo) +{ + *cacheInfo = *info->cacheInfo(); +} + +void _dyld_process_info_retain(dyld_process_info info) +{ + info->retainCount() += 1; +} + +void _dyld_process_info_release(dyld_process_info info) +{ + info->retainCount() -= 1; + if ( info->retainCount() == 0 ) + free((void*)info); +} + +void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)) +{ + info->forEachImage(callback); +} + + +void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)) +{ + info->forEachSegment(machHeaderAddress, callback); +} + + + diff --git a/dyld/src/dyld_process_info_internal.h b/dyld/src/dyld_process_info_internal.h new file mode 100644 index 0000000..cd8c607 --- /dev/null +++ b/dyld/src/dyld_process_info_internal.h @@ -0,0 +1,140 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2016 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_PROCESS_INFO_INTERNAL_H_ +#define _DYLD_PROCESS_INFO_INTERNAL_H_ + + +#include +#include +#include +#include + + +struct dyld_all_image_infos_32 { + uint32_t version; + uint32_t infoArrayCount; + uint32_t infoArray; + uint32_t notification; + bool processDetachedFromSharedRegion; + bool libSystemInitialized; + uint32_t dyldImageLoadAddress; + uint32_t jitInfo; + uint32_t dyldVersion; + uint32_t errorMessage; + uint32_t terminationFlags; + uint32_t coreSymbolicationShmPage; + uint32_t systemOrderFlag; + uint32_t uuidArrayCount; + uint32_t uuidArray; + uint32_t dyldAllImageInfosAddress; + uint32_t initialImageCount; + uint32_t errorKind; + uint32_t errorClientOfDylibPath; + uint32_t errorTargetDylibPath; + uint32_t errorSymbol; + uint32_t sharedCacheSlide; + uint8_t sharedCacheUUID[16]; + uint32_t sharedCacheBaseAddress; + uint64_t infoArrayChangeTimestamp; + uint32_t dyldPath; + uint32_t notifyMachPorts[8]; + uint32_t reserved[5]; + uint32_t compact_dyld_image_info_addr; + uint32_t compact_dyld_image_info_size; +}; + +struct dyld_all_image_infos_64 { + uint32_t version; + uint32_t infoArrayCount; + uint64_t infoArray; + uint64_t notification; + bool processDetachedFromSharedRegion; + bool libSystemInitialized; + uint32_t paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS! + uint64_t dyldImageLoadAddress; + uint64_t jitInfo; + uint64_t dyldVersion; + uint64_t errorMessage; + uint64_t terminationFlags; + uint64_t coreSymbolicationShmPage; + uint64_t systemOrderFlag; + uint64_t uuidArrayCount; + uint64_t uuidArray; + uint64_t dyldAllImageInfosAddress; + uint64_t initialImageCount; + uint64_t errorKind; + uint64_t errorClientOfDylibPath; + uint64_t errorTargetDylibPath; + uint64_t errorSymbol; + uint64_t sharedCacheSlide; + uint8_t sharedCacheUUID[16]; + uint64_t sharedCacheBaseAddress; + uint64_t infoArrayChangeTimestamp; + uint64_t dyldPath; + uint32_t notifyMachPorts[8]; + uint64_t reserved[9]; + uint64_t compact_dyld_image_info_addr; + uint64_t compact_dyld_image_info_size; +}; + +struct dyld_image_info_32 { + uint32_t imageLoadAddress; + uint32_t imageFilePath; + uint32_t imageFileModDate; +}; +struct dyld_image_info_64 { + uint64_t imageLoadAddress; + uint64_t imageFilePath; + uint64_t imageFileModDate; +}; + +#define DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE (32*1024) +#define DYLD_PROCESS_INFO_NOTIFY_LOAD_ID 0x1000 +#define DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID 0x2000 +#define DYLD_PROCESS_INFO_NOTIFY_MAIN_ID 0x3000 + + +struct dyld_process_info_image_entry { + uuid_t uuid; + uint64_t loadAddress; + uint32_t pathStringOffset; + uint32_t pathLength; +}; + +struct dyld_process_info_notify_header { + mach_msg_header_t header; + uint32_t version; + uint32_t imageCount; + uint32_t imagesOffset; + uint32_t stringsOffset; + uint64_t timestamp; +}; + + + + +#endif // _DYLD_PROCESS_INFO_INTERNAL_H_ + + diff --git a/dyld/src/dyld_process_info_notify.cpp b/dyld/src/dyld_process_info_notify.cpp new file mode 100644 index 0000000..6d5eee3 --- /dev/null +++ b/dyld/src/dyld_process_info_notify.cpp @@ -0,0 +1,506 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2016 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dyld_process_info.h" +#include "dyld_process_info_internal.h" +#include "dyld_images.h" +#include "dyld_priv.h" + +#include "LaunchCache.h" +#include "Loading.h" +#include "AllImages.h" + + +typedef void (^Notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path); +typedef void (^NotifyExit)(); +typedef void (^NotifyMain)(); + + +// +// Object used for monitoring another processes dyld loads +// +struct __attribute__((visibility("hidden"))) dyld_process_info_notify_base +{ + static dyld_process_info_notify_base* make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr); + ~dyld_process_info_notify_base(); + + uint32_t& retainCount() const { return _retainCount; } + void setNotifyMain(NotifyMain notifyMain) const { _notifyMain = notifyMain; } + + // override new and delete so we don't need to link with libc++ + static void* operator new(size_t sz) { return malloc(sz); } + static void operator delete(void* p) { return free(p); } + +private: + dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task); + kern_return_t makePorts(); + kern_return_t pokeSendPortIntoTarget(); + kern_return_t unpokeSendPortInTarget(); + void setMachSourceOnQueue(); + + mutable uint32_t _retainCount; + dispatch_queue_t _queue; + Notify _notify; + NotifyExit _notifyExit; + mutable NotifyMain _notifyMain; + task_t _targetTask; + dispatch_source_t _machSource; + uint64_t _portAddressInTarget; + mach_port_t _sendPortInTarget; // target is process being watched for image loading/unloading + mach_port_t _receivePortInMonitor; // monitor is process being notified of image loading/unloading +}; + + +dyld_process_info_notify_base::dyld_process_info_notify_base(dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, task_t task) + : _retainCount(1), _queue(queue), _notify(notify), _notifyExit(notifyExit), _notifyMain(NULL), _targetTask(task), _machSource(NULL), _portAddressInTarget(0), _sendPortInTarget(0), _receivePortInMonitor(0) +{ + dispatch_retain(_queue); +} + +dyld_process_info_notify_base::~dyld_process_info_notify_base() +{ + if ( _machSource ) { + dispatch_release(_machSource); + _machSource = NULL; + } + if ( _portAddressInTarget ) { + unpokeSendPortInTarget(); + _portAddressInTarget = 0; + } + if ( _sendPortInTarget ) { + _sendPortInTarget = 0; + } + dispatch_release(_queue); + if ( _receivePortInMonitor != 0 ) { + mach_port_deallocate(mach_task_self(), _receivePortInMonitor); + _receivePortInMonitor = 0; + } +} + + +dyld_process_info_notify_base* dyld_process_info_notify_base::make(task_t task, dispatch_queue_t queue, Notify notify, NotifyExit notifyExit, kern_return_t* kr) +{ + dyld_process_info_notify_base* obj = new dyld_process_info_notify_base(queue, notify, notifyExit, task); + + if ( kern_return_t r = obj->makePorts() ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + + obj->setMachSourceOnQueue(); + + if ( kern_return_t r = obj->pokeSendPortIntoTarget() ) { + if ( kr != NULL ) + *kr = r; + goto fail; + } + + if ( kr != NULL ) + *kr = KERN_SUCCESS; + return obj; + +fail: + delete obj; + return NULL; +} + + +kern_return_t dyld_process_info_notify_base::makePorts() +{ + // Allocate a port to listen on in this monitoring task + if ( kern_return_t r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_receivePortInMonitor) ) + return r; + + // Add send rights for replying + if ( kern_return_t r = mach_port_insert_right(mach_task_self(), _receivePortInMonitor, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) ) + return r; + + // Allocate a name in the target. We need a new name to add send rights to + if ( kern_return_t r = mach_port_allocate(_targetTask, MACH_PORT_RIGHT_DEAD_NAME, &_sendPortInTarget) ) + return r; + + // Deallocate the dead name + if ( kern_return_t r = mach_port_mod_refs(_targetTask, _sendPortInTarget, MACH_PORT_RIGHT_DEAD_NAME, -1) ) + return r; + + // Make the dead name a send right to our listening port + if ( kern_return_t r = mach_port_insert_right(_targetTask, _sendPortInTarget, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND) ) + return r; + + // Notify us if the target dies + mach_port_t previous = MACH_PORT_NULL; + if ( kern_return_t r = mach_port_request_notification(_targetTask, _sendPortInTarget, MACH_NOTIFY_DEAD_NAME, 0, _receivePortInMonitor, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous)) + return r; + + //fprintf(stderr, "_sendPortInTarget=%d, _receivePortInMonitor=%d\n", _sendPortInTarget, _receivePortInMonitor); + return KERN_SUCCESS; +} + + + +void dyld_process_info_notify_base::setMachSourceOnQueue() +{ + NotifyExit exitHandler = _notifyExit; + _machSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, _receivePortInMonitor, 0, _queue); + dispatch_source_set_event_handler(_machSource, ^{ + uint8_t messageBuffer[DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE]; + mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer; + + kern_return_t r = mach_msg(h, MACH_RCV_MSG, 0, sizeof(messageBuffer), _receivePortInMonitor, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + if ( r == KERN_SUCCESS ) { + //fprintf(stderr, "received message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size); + if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_LOAD_ID || h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID ) { + // run notifier block for each [un]load image + const dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)messageBuffer; + const dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&messageBuffer[header->imagesOffset]; + const char* const stringPool = (char*)&messageBuffer[header->stringsOffset]; + for (unsigned i=0; i < header->imageCount; ++i) { + bool isUnload = (h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID); + _notify(isUnload, header->timestamp, entries[i].loadAddress, entries[i].uuid, stringPool + entries[i].pathStringOffset); + } + // reply to dyld, so it can continue + mach_msg_header_t replyHeader; + replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND); + replyHeader.msgh_id = 0; + replyHeader.msgh_local_port = MACH_PORT_NULL; + replyHeader.msgh_remote_port = h->msgh_remote_port; + replyHeader.msgh_reserved = 0; + replyHeader.msgh_size = sizeof(replyHeader); + mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL); + } + else if ( h->msgh_id == DYLD_PROCESS_INFO_NOTIFY_MAIN_ID ) { + if ( _notifyMain != NULL ) { + _notifyMain(); + } + // reply to dyld, so it can continue + mach_msg_header_t replyHeader; + replyHeader.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND); + replyHeader.msgh_id = 0; + replyHeader.msgh_local_port = MACH_PORT_NULL; + replyHeader.msgh_remote_port = h->msgh_remote_port; + replyHeader.msgh_reserved = 0; + replyHeader.msgh_size = sizeof(replyHeader); + mach_msg(&replyHeader, MACH_SEND_MSG | MACH_SEND_TIMEOUT, replyHeader.msgh_size, 0, MACH_PORT_NULL, 100, MACH_PORT_NULL); + } + else if ( h->msgh_id == MACH_NOTIFY_PORT_DELETED ) { + mach_port_t deadPort = ((mach_port_deleted_notification_t *)h)->not_port; + //fprintf(stderr, "received message id=MACH_NOTIFY_PORT_DELETED, size=%d, deadPort=%d\n", h->msgh_size, deadPort); + if ( deadPort == _sendPortInTarget ) { + // target process died. Clean up ports + _sendPortInTarget = 0; + mach_port_deallocate(mach_task_self(), _receivePortInMonitor); + _receivePortInMonitor = 0; + _portAddressInTarget = 0; + // notify that target is gone + exitHandler(); + } + } + else { + fprintf(stderr, "received unknown message id=0x%X, size=%d\n", h->msgh_id, h->msgh_size); + } + } + }); + dispatch_resume(_machSource); +} + + +kern_return_t dyld_process_info_notify_base::pokeSendPortIntoTarget() +{ + // get location on all_image_infos in target task + task_dyld_info_data_t taskDyldInfo; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t r = task_info(_targetTask, TASK_DYLD_INFO, (task_info_t)&taskDyldInfo, &count); + if ( r ) + return r; + + // remap the page containing all_image_infos into this process r/w + mach_vm_address_t mappedAddress = 0; + mach_vm_size_t mappedSize = taskDyldInfo.all_image_info_size; + vm_prot_t curProt = VM_PROT_NONE; + vm_prot_t maxProt = VM_PROT_NONE; + r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, + _targetTask, taskDyldInfo.all_image_info_addr, false, &curProt, &maxProt, VM_INHERIT_NONE); + if ( r ) + return r; + if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) ) + return KERN_PROTECTION_FAILURE; + + // atomically set port into all_image_info_struct + static_assert(sizeof(mach_port_t) == sizeof(uint32_t), "machport size not 32-bits"); + + mach_vm_address_t mappedAddressToPokePort = 0; + if ( taskDyldInfo.all_image_info_format == TASK_DYLD_ALL_IMAGE_INFO_32 ) + mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_32,notifyMachPorts); + else + mappedAddressToPokePort = mappedAddress + offsetof(dyld_all_image_infos_64,notifyMachPorts); + + // use first available slot + bool slotFound = false; + for (int slotIndex=0; slotIndex < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slotIndex) { + if ( OSAtomicCompareAndSwap32Barrier(0, _sendPortInTarget, (volatile int32_t*)mappedAddressToPokePort) ) { + slotFound = true; + break; + } + mappedAddressToPokePort += sizeof(uint32_t); + } + if ( !slotFound ) { + mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize); + return KERN_UREFS_OVERFLOW; + } + _portAddressInTarget = taskDyldInfo.all_image_info_addr + mappedAddressToPokePort - mappedAddress; + //fprintf(stderr, "poked port %d into target at address 0x%llX\n", _sendPortInTarget, _portAddressInTarget); + mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize); + return r; +} + + + +kern_return_t dyld_process_info_notify_base::unpokeSendPortInTarget() +{ + // remap the page containing all_image_infos into this process r/w + mach_vm_address_t mappedAddress = 0; + mach_vm_size_t mappedSize = sizeof(mach_port_t); + vm_prot_t curProt = VM_PROT_NONE; + vm_prot_t maxProt = VM_PROT_NONE; + kern_return_t r = mach_vm_remap(mach_task_self(), &mappedAddress, mappedSize, 0, VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, + _targetTask, _portAddressInTarget, false, &curProt, &maxProt, VM_INHERIT_NONE); + if ( r ) + return r; + if ( curProt != (VM_PROT_READ|VM_PROT_WRITE) ) + return KERN_PROTECTION_FAILURE; + + OSAtomicCompareAndSwap32Barrier(_sendPortInTarget, 0, (volatile int32_t*)mappedAddress); + + //fprintf(stderr, "cleared port %d from target\n", _sendPortInTarget); + mach_vm_deallocate(mach_task_self(), mappedAddress, mappedSize); + return r; +} + + + +dyld_process_info_notify _dyld_process_info_notify(task_t task, dispatch_queue_t queue, + void (^notify)(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path), + void (^notifyExit)(), + kern_return_t* kr) +{ + return dyld_process_info_notify_base::make(task, queue, notify, notifyExit, kr); +} + +void _dyld_process_info_notify_main(dyld_process_info_notify object, void (^notifyMain)()) +{ + object->setNotifyMain(notifyMain); +} + +void _dyld_process_info_notify_retain(dyld_process_info_notify object) +{ + object->retainCount() += 1; +} + +void _dyld_process_info_notify_release(dyld_process_info_notify object) +{ + object->retainCount() -= 1; + if ( object->retainCount() == 0 ) + delete object; +} + + + + + + + +namespace dyld3 { + + +static mach_port_t sNotifyReplyPorts[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; +static bool sZombieNotifiers[DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT]; + +static void notifyMonitoringDyld(bool unloading, unsigned portSlot, const launch_cache::DynArray& imageInfos) +{ + if ( sZombieNotifiers[portSlot] ) + return; + + unsigned entriesSize = (unsigned)imageInfos.count()*sizeof(dyld_process_info_image_entry); + unsigned pathsSize = 0; + for (uintptr_t i=0; i < imageInfos.count(); ++i) { + launch_cache::Image image(imageInfos[i].imageData); + pathsSize += (strlen(image.path()) + 1); + } + unsigned totalSize = (sizeof(dyld_process_info_notify_header) + MAX_TRAILER_SIZE + entriesSize + pathsSize + 127) & -128; // align + if ( totalSize > DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE ) { + // Putting all image paths into one message would make buffer too big. + // Instead split into two messages. Recurse as needed until paths fit in buffer. + unsigned imageHalfCount = (unsigned)imageInfos.count()/2; + const launch_cache::DynArray firstHalf(imageHalfCount, (loader::ImageInfo*)&imageInfos[0]); + const launch_cache::DynArray secondHalf(imageInfos.count() - imageHalfCount, (loader::ImageInfo*)&imageInfos[imageHalfCount]); + notifyMonitoringDyld(unloading, portSlot, firstHalf); + notifyMonitoringDyld(unloading, portSlot, secondHalf); + return; + } + // build buffer to send + dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo(); + uint8_t buffer[totalSize]; + dyld_process_info_notify_header* header = (dyld_process_info_notify_header*)buffer; + header->version = 1; + header->imageCount = (uint32_t)imageInfos.count(); + header->imagesOffset = sizeof(dyld_process_info_notify_header); + header->stringsOffset = sizeof(dyld_process_info_notify_header) + entriesSize; + header->timestamp = allImageInfo->infoArrayChangeTimestamp; + dyld_process_info_image_entry* entries = (dyld_process_info_image_entry*)&buffer[header->imagesOffset]; + char* const pathPoolStart = (char*)&buffer[header->stringsOffset]; + char* pathPool = pathPoolStart; + for (uintptr_t i=0; i < imageInfos.count(); ++i) { + launch_cache::Image image(imageInfos[i].imageData); + strcpy(pathPool, image.path()); + uint32_t len = (uint32_t)strlen(pathPool); + memcpy(entries->uuid, image.uuid(), sizeof(uuid_t)); + entries->loadAddress = (uint64_t)imageInfos[i].loadAddress; + entries->pathStringOffset = (uint32_t)(pathPool - pathPoolStart); + entries->pathLength = len; + pathPool += (len +1); + ++entries; + } + // lazily alloc reply port + if ( sNotifyReplyPorts[portSlot] == 0 ) { + if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[portSlot]) ) + mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[portSlot], sNotifyReplyPorts[portSlot], MACH_MSG_TYPE_MAKE_SEND); + //log("allocated reply port %d\n", sNotifyReplyPorts[portSlot]); + } + //log("found port to send to\n"); + mach_msg_header_t* h = (mach_msg_header_t*)buffer; + h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE + h->msgh_id = unloading ? DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID : DYLD_PROCESS_INFO_NOTIFY_LOAD_ID; + h->msgh_local_port = sNotifyReplyPorts[portSlot]; + h->msgh_remote_port = allImageInfo->notifyPorts[portSlot]; + h->msgh_reserved = 0; + h->msgh_size = (mach_msg_size_t)sizeof(buffer); + //log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", portSlot, allImageInfo->notifyPorts[portSlot], h->msgh_size, sNotifyReplyPorts[portSlot], h->msgh_id); + kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[portSlot], 2000, MACH_PORT_NULL); + //log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size); + if ( sendResult == MACH_SEND_INVALID_DEST ) { + // sender is not responding, detatch + //log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[portSlot], sNotifyReplyPorts[portSlot]); + mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[portSlot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]); + allImageInfo->notifyPorts[portSlot] = 0; + sNotifyReplyPorts[portSlot] = 0; + } + else if ( sendResult == MACH_RCV_TIMED_OUT ) { + // client took too long, ignore him from now on + sZombieNotifiers[portSlot] = true; + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[portSlot]); + sNotifyReplyPorts[portSlot] = 0; + } +} + +void AllImages::notifyMonitorMain() +{ + dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo(); + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( (allImageInfo->notifyPorts[slot] != 0 ) && !sZombieNotifiers[slot] ) { + if ( sNotifyReplyPorts[slot] == 0 ) { + if ( !mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &sNotifyReplyPorts[slot]) ) + mach_port_insert_right(mach_task_self(), sNotifyReplyPorts[slot], sNotifyReplyPorts[slot], MACH_MSG_TYPE_MAKE_SEND); + //dyld::log("allocated reply port %d\n", sNotifyReplyPorts[slot]); + } + //dyld::log("found port to send to\n"); + uint8_t messageBuffer[sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE]; + mach_msg_header_t* h = (mach_msg_header_t*)messageBuffer; + h->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND); // MACH_MSG_TYPE_MAKE_SEND_ONCE + h->msgh_id = DYLD_PROCESS_INFO_NOTIFY_MAIN_ID; + h->msgh_local_port = sNotifyReplyPorts[slot]; + h->msgh_remote_port = allImageInfo->notifyPorts[slot]; + h->msgh_reserved = 0; + h->msgh_size = (mach_msg_size_t)sizeof(messageBuffer); + //dyld::log("sending to port[%d]=%d, size=%d, reply port=%d, id=0x%X\n", slot, allImageInfo->notifyPorts[slot], h->msgh_size, sNotifyReplyPorts[slot], h->msgh_id); + kern_return_t sendResult = mach_msg(h, MACH_SEND_MSG | MACH_RCV_MSG | MACH_RCV_TIMEOUT, h->msgh_size, h->msgh_size, sNotifyReplyPorts[slot], 2000, MACH_PORT_NULL); + //dyld::log("send result = 0x%X, msg_id=%d, msg_size=%d\n", sendResult, h->msgh_id, h->msgh_size); + if ( sendResult == MACH_SEND_INVALID_DEST ) { + // sender is not responding, detatch + //dyld::log("process requesting notification gone. deallocation send port %d and receive port %d\n", allImageInfo->notifyPorts[slot], sNotifyReplyPorts[slot]); + mach_port_deallocate(mach_task_self(), allImageInfo->notifyPorts[slot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + allImageInfo->notifyPorts[slot] = 0; + sNotifyReplyPorts[slot] = 0; + } + else if ( sendResult == MACH_RCV_TIMED_OUT ) { + // client took too long, ignore him from now on + sZombieNotifiers[slot] = true; + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + sNotifyReplyPorts[slot] = 0; + } + } + } +} + +void AllImages::notifyMonitorLoads(const launch_cache::DynArray& newImages) +{ + // notify each monitoring process + dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo(); + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( allImageInfo->notifyPorts[slot] != 0 ) { + notifyMonitoringDyld(false, slot, newImages); + } + else if ( sNotifyReplyPorts[slot] != 0 ) { + // monitoring process detached from this process, so release reply port + //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + sNotifyReplyPorts[slot] = 0; + sZombieNotifiers[slot] = false; + } + } +} + +void AllImages::notifyMonitorUnloads(const launch_cache::DynArray& unloadingImages) +{ + // notify each monitoring process + dyld_all_image_infos* allImageInfo = gAllImages.oldAllImageInfo(); + for (int slot=0; slot < DYLD_MAX_PROCESS_INFO_NOTIFY_COUNT; ++slot) { + if ( allImageInfo->notifyPorts[slot] != 0 ) { + notifyMonitoringDyld(true, slot, unloadingImages); + } + else if ( sNotifyReplyPorts[slot] != 0 ) { + // monitoring process detached from this process, so release reply port + //dyld::log("deallocated reply port %d\n", sNotifyReplyPorts[slot]); + mach_port_deallocate(mach_task_self(), sNotifyReplyPorts[slot]); + sNotifyReplyPorts[slot] = 0; + sZombieNotifiers[slot] = false; + } + } +} + +} // namespace dyld3 + + + + diff --git a/dyld/src/dyld_sim.exp b/dyld/src/dyld_sim.exp new file mode 100644 index 0000000..ccc744a --- /dev/null +++ b/dyld/src/dyld_sim.exp @@ -0,0 +1,31 @@ +# +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +# + +# +# Be sure the following are not dead stripped +# + +# Used by various tools to see build number of dyld +_dyld_simVersionString +_dyld_simVersionNumber + diff --git a/dyld/src/dyld_stub_binder.s b/dyld/src/dyld_stub_binder.s new file mode 100644 index 0000000..5cc5658 --- /dev/null +++ b/dyld/src/dyld_stub_binder.s @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2008-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +#include + + +#ifdef __i386__ + +#define MH_PARAM_OUT 0 +#define LP_PARAM_OUT 4 +#define XMMM0_SAVE 16 /* 16-byte align */ +#define XMMM1_SAVE 32 +#define XMMM2_SAVE 48 +#define XMMM3_SAVE 64 +#define EAX_SAVE 84 +#define ECX_SAVE 88 +#define EDX_SAVE 92 +#define LP_LOCAL 96 +#define MH_LOCAL 100 +#define STACK_SIZE 100 /* must be 4 mod 16 so that stack winds up 16-byte aliged */ +#define LP_OLD_BP_SAVE 104 + +/* + * sp+4 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + .text + .align 4,0x90 + .globl dyld_stub_binder + .globl _misaligned_stack_error +dyld_stub_binder: + subl $STACK_SIZE,%esp # makes stack 16-byte aligned + movl %eax,EAX_SAVE(%esp) + movl LP_OLD_BP_SAVE(%esp),%eax # get lazy-pointer meta-parameter + movl %eax,LP_LOCAL(%esp) + movl %ebp,LP_OLD_BP_SAVE(%esp) # store epb back chain + movl %esp,%ebp # set epb to be this frame + add $LP_OLD_BP_SAVE,%ebp + movl %ecx,ECX_SAVE(%esp) + movl %edx,EDX_SAVE(%esp) + .align 0,0x90 +_misaligned_stack_error_: + movdqa %xmm0,XMMM0_SAVE(%esp) + movdqa %xmm1,XMMM1_SAVE(%esp) + movdqa %xmm2,XMMM2_SAVE(%esp) + movdqa %xmm3,XMMM3_SAVE(%esp) +dyld_stub_binder_: + movl MH_LOCAL(%esp),%eax # call dyld::fastBindLazySymbol(loadercache, lazyinfo) + movl %eax,MH_PARAM_OUT(%esp) + movl LP_LOCAL(%esp),%eax + movl %eax,LP_PARAM_OUT(%esp) + call __Z21_dyld_fast_stub_entryPvl + movdqa XMMM0_SAVE(%esp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%esp),%xmm1 + movdqa XMMM2_SAVE(%esp),%xmm2 + movdqa XMMM3_SAVE(%esp),%xmm3 + movl ECX_SAVE(%esp),%ecx + movl EDX_SAVE(%esp),%edx + movl %eax,%ebp # move target address to epb + movl EAX_SAVE(%esp),%eax # restore eax + addl $STACK_SIZE+4,%esp # cut back stack + xchg %ebp, (%esp) # restore ebp and set target to top of stack + ret # jump to target + + +#endif /* __i386__ */ + + +#if __x86_64__ + +#define RET_ADDR_RBP 24 +#define LP_PARAM_RBP 16 +#define MH_PARAM_RBP 8 +#define OLD_RBP_RBP 0 + +#define RDI_SAVE_RBP -8 +#define RSI_SAVE_RBP -16 +#define RDX_SAVE_RBP -24 +#define RCX_SAVE_RBP -32 +#define RBX_SAVE_RBP -40 +#define RAX_SAVE_RBP -48 +#define R8_SAVE_RBP -56 +#define R9_SAVE_RBP -64 +#define STATIC_STACK_SIZE 256 // extra padding to allow it to be 64-byte aligned + +#define XMM0_SAVE_RSP 0x00 +#define XMM1_SAVE_RSP 0x10 +#define XMM2_SAVE_RSP 0x20 +#define XMM3_SAVE_RSP 0x30 +#define XMM4_SAVE_RSP 0x40 +#define XMM5_SAVE_RSP 0x50 +#define XMM6_SAVE_RSP 0x60 +#define XMM7_SAVE_RSP 0x70 + + + /* + * sp+16 return address + * sp+8 lazy binding info offset + * sp+0 address of ImageLoader cache + */ + .align 2,0x90 + .globl dyld_stub_binder +dyld_stub_binder: + pushq %rbp + test $0xF,%rsp # at this point stack should be 16-byte aligned + jne _stack_not_16_byte_aligned_error + movq %rsp,%rbp + subq $STATIC_STACK_SIZE,%rsp + movq %rdi,RDI_SAVE_RBP(%rbp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE_RBP(%rbp) + movq %rdx,RDX_SAVE_RBP(%rbp) + movq %rcx,RCX_SAVE_RBP(%rbp) + movq %rbx,RBX_SAVE_RBP(%rbp) + movq %rax,RAX_SAVE_RBP(%rbp) + movq %r8, R8_SAVE_RBP(%rbp) + movq %r9, R9_SAVE_RBP(%rbp) + + cmpl $0, _inited(%rip) + jne Linited + movl $0x01,%eax + cpuid # get cpu features to check on xsave instruction support + andl $0x08000000,%ecx # check OSXSAVE bit + movl %ecx,_hasXSave(%rip) + cmpl $0, %ecx + jne LxsaveInfo + movl $1, _inited(%rip) + jmp Lsse + +LxsaveInfo: + movl $0x0D,%eax + movl $0x00,%ecx + cpuid # get xsave parameter info + movl %eax,_features_lo32(%rip) + movl %edx,_features_hi32(%rip) + movl %ecx,_bufferSize32(%rip) + movl $1, _inited(%rip) + +Linited: + cmpl $0, _hasXSave(%rip) + jne Lxsave + +Lsse: + subq $128, %rsp + movdqa %xmm0, XMM0_SAVE_RSP(%rsp) + movdqa %xmm1, XMM1_SAVE_RSP(%rsp) + movdqa %xmm2, XMM2_SAVE_RSP(%rsp) + movdqa %xmm3, XMM3_SAVE_RSP(%rsp) + movdqa %xmm4, XMM4_SAVE_RSP(%rsp) + movdqa %xmm5, XMM5_SAVE_RSP(%rsp) + movdqa %xmm6, XMM6_SAVE_RSP(%rsp) + movdqa %xmm7, XMM7_SAVE_RSP(%rsp) + jmp Lbind + +Lxsave: + movl _bufferSize32(%rip),%eax + movq %rsp, %rdi + subq %rax, %rdi # stack alloc buffer + andq $-64, %rdi # 64-byte align stack + movq %rdi, %rsp + # xsave requires buffer to be zero'ed out + movq $0, %rcx + movq %rdi, %r8 + movq %rdi, %r9 + addq %rax, %r9 +Lz: movq %rcx, (%r8) + addq $8, %r8 + cmpq %r8,%r9 + ja Lz + + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + # note: do not use xsaveopt, it assumes you are using the same + # buffer as previous xsaves, and this thread is on the same cpu. + xsave (%rsp) + +Lbind: + movq MH_PARAM_RBP(%rbp),%rdi # call fastBindLazySymbol(loadercache, lazyinfo) + movq LP_PARAM_RBP(%rbp),%rsi + call __Z21_dyld_fast_stub_entryPvl + movq %rax,%r11 # copy jump target + + cmpl $0, _hasXSave(%rip) + jne Lxrstror + + movdqa XMM0_SAVE_RSP(%rsp),%xmm0 + movdqa XMM1_SAVE_RSP(%rsp),%xmm1 + movdqa XMM2_SAVE_RSP(%rsp),%xmm2 + movdqa XMM3_SAVE_RSP(%rsp),%xmm3 + movdqa XMM4_SAVE_RSP(%rsp),%xmm4 + movdqa XMM5_SAVE_RSP(%rsp),%xmm5 + movdqa XMM6_SAVE_RSP(%rsp),%xmm6 + movdqa XMM7_SAVE_RSP(%rsp),%xmm7 + jmp Ldone + +Lxrstror: + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + xrstor (%rsp) + +Ldone: + movq RDI_SAVE_RBP(%rbp),%rdi + movq RSI_SAVE_RBP(%rbp),%rsi + movq RDX_SAVE_RBP(%rbp),%rdx + movq RCX_SAVE_RBP(%rbp),%rcx + movq RBX_SAVE_RBP(%rbp),%rbx + movq RAX_SAVE_RBP(%rbp),%rax + movq R8_SAVE_RBP(%rbp),%r8 + movq R9_SAVE_RBP(%rbp),%r9 + movq %rbp,%rsp + popq %rbp + addq $16,%rsp # remove meta-parameters + jmp *%r11 # jmp to target + +_stack_not_16_byte_aligned_error: + movdqa %xmm0, 0(%rsp) + int3 + + .data +# Cached info from cpuid. These must be lazily evaluated. +# You cannot initalize these from _dyld_initializer() because +# that function is called from another dylib... +_inited: .long 0 +_features_lo32: .long 0 +_features_hi32: .long 0 +_bufferSize32: .long 0 +_hasXSave: .long 0 + +#endif + + +#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 + +#if __ARM_ARCH_7K__ + vpush {d0, d1, d2, d3, d4, d5, d6, d7} + sub sp, sp, #8 // Align stack to 16 bytes. +#endif + // call dyld::fastBindLazySymbol(loadercache, lazyinfo) + bl __Z21_dyld_fast_stub_entryPvl + mov ip, r0 // move the symbol`s address into ip + +#if __ARM_ARCH_7K__ + add sp, sp, #8 + vpop {d0, d1, d2, d3, d4, d5, d6, d7} +#endif + + ldmfd sp!, {r0,r1,r2,r3,r7,lr} // restore registers + add sp, sp, #8 // remove meta-parameters + + bx ip // jump to the symbol`s address that was bound + +#endif /* __arm__ */ + + +#if __arm64__ + /* + * sp+0 lazy binding info offset + * sp+8 address of ImageLoader cache + */ + .text + .align 2 + .globl dyld_stub_binder +dyld_stub_binder: + stp fp, lr, [sp, #-16]! + mov fp, sp + sub sp, sp, #240 + stp x0,x1, [fp, #-16] ; x0-x7 are int parameter registers + stp x2,x3, [fp, #-32] + stp x4,x5, [fp, #-48] + stp x6,x7, [fp, #-64] + stp x8,x9, [fp, #-80] ; x8 is used for struct returns + stp q0,q1, [fp, #-128] ; q0-q7 are vector/fp parameter registers + stp q2,q3, [fp, #-160] + stp q4,q5, [fp, #-192] + stp q6,q7, [fp, #-224] + + ldr x0, [fp, #24] ; move address ImageLoader cache to 1st parameter + ldr x1, [fp, #16] ; move lazy info offset 2nd parameter + ; call dyld::fastBindLazySymbol(loadercache, lazyinfo) + bl __Z21_dyld_fast_stub_entryPvl + mov x16,x0 ; save target function address in lr + + ; restore parameter registers + ldp x0,x1, [fp, #-16] + ldp x2,x3, [fp, #-32] + ldp x4,x5, [fp, #-48] + ldp x6,x7, [fp, #-64] + ldp x8,x9, [fp, #-80] + ldp q0,q1, [fp, #-128] + ldp q2,q3, [fp, #-160] + ldp q4,q5, [fp, #-192] + ldp q6,q7, [fp, #-224] + + mov sp, fp + ldp fp, lr, [sp], #16 + add sp, sp, #16 ; remove meta-parameters + br x16 + +#endif + diff --git a/dyld/src/glue.c b/dyld/src/glue.c new file mode 100644 index 0000000..99a6644 --- /dev/null +++ b/dyld/src/glue.c @@ -0,0 +1,820 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2004-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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if TARGET_IPHONE_SIMULATOR + #include "dyldSyscallInterface.h" + #include "dyld_images.h" + #include + #include + #include + #if __LP64__ + #define LC_SEGMENT_COMMAND LC_SEGMENT_64 + typedef struct segment_command_64 macho_segment_command; + typedef struct mach_header_64 macho_header; + typedef struct nlist_64 macho_nlist; + #else + #define LC_SEGMENT_COMMAND LC_SEGMENT + typedef struct segment_command macho_segment_command; + typedef struct mach_header macho_header; + typedef struct nlist macho_nlist; + #endif +#endif + +// from _simple.h in libc +typedef struct _SIMPLE* _SIMPLE_STRING; +extern void _simple_vdprintf(int __fd, const char *__fmt, va_list __ap); +extern void _simple_dprintf(int __fd, const char *__fmt, ...); +extern _SIMPLE_STRING _simple_salloc(void); +extern int _simple_vsprintf(_SIMPLE_STRING __b, const char *__fmt, va_list __ap); +extern void _simple_sfree(_SIMPLE_STRING __b); +extern char * _simple_string(_SIMPLE_STRING __b); + +// dyld::log(const char* format, ...) +extern void _ZN4dyld3logEPKcz(const char*, ...); + +// dyld::halt(const char* msg); +extern void _ZN4dyld4haltEPKc(const char* msg) __attribute__((noreturn)); + +extern void dyld_fatal_error(const char* errString) __attribute__((noreturn)); + + +// abort called by C++ unwinding code +void abort() +{ + _ZN4dyld4haltEPKc("dyld calling abort()\n"); +} + +// std::terminate called by C++ unwinding code +void _ZSt9terminatev() +{ + _ZN4dyld4haltEPKc("dyld std::terminate()\n"); +} + +// std::unexpected called by C++ unwinding code +void _ZSt10unexpectedv() +{ + _ZN4dyld4haltEPKc("dyld std::unexpected()\n"); +} + +// __cxxabiv1::__terminate(void (*)()) called to terminate process +void _ZN10__cxxabiv111__terminateEPFvvE() +{ + _ZN4dyld4haltEPKc("dyld std::__terminate()\n"); +} + +// __cxxabiv1::__unexpected(void (*)()) called to terminate process +void _ZN10__cxxabiv112__unexpectedEPFvvE() +{ + _ZN4dyld4haltEPKc("dyld std::__unexpected()\n"); +} + +// std::__terminate() called by C++ unwinding code +void _ZSt11__terminatePFvvE(void (*func)()) +{ + _ZN4dyld4haltEPKc("dyld std::__terminate()\n"); +} + +// std::__unexpected() called by C++ unwinding code +void _ZSt12__unexpectedPFvvE(void (*func)()) +{ + _ZN4dyld4haltEPKc("dyld std::__unexpected()\n"); +} + +// terminate_handler get_terminate() +void* _ZSt13get_terminatev() +{ + return NULL; +} + +// unexpected_handler get_unexpected() +void* _ZSt14get_unexpectedv() +{ + return NULL; +} + +// new_handler get_new_handler() +void* _ZSt15get_new_handlerv() +{ + return NULL; +} + + + +// __cxxabiv1::__terminate_handler +void* _ZN10__cxxabiv119__terminate_handlerE = &_ZSt9terminatev; + +// __cxxabiv1::__unexpected_handler +void* _ZN10__cxxabiv120__unexpected_handlerE = &_ZSt10unexpectedv; + +// libc uses assert() +void __assert_rtn(const char* func, const char* file, int line, const char* failedexpr) +{ + if (func == NULL) + _ZN4dyld3logEPKcz("Assertion failed: (%s), file %s, line %d.\n", failedexpr, file, line); + else + _ZN4dyld3logEPKcz("Assertion failed: (%s), function %s, file %s, line %d.\n", failedexpr, func, file, line); + abort(); +} + + +int myfprintf(FILE* file, const char* format, ...) __asm("_fprintf"); + +// called by libuwind code before aborting +size_t fwrite(const void* ptr, size_t size, size_t nitme, FILE* stream) +{ + return myfprintf(stream, "%s", (char*)ptr); +} + +// called by libuwind code before aborting +int fprintf(FILE* file, const char* format, ...) +{ + va_list list; + va_start(list, format); + _simple_vdprintf(STDERR_FILENO, format, list); + va_end(list); + return 0; +} + +// called by LIBC_ABORT +void abort_report_np(const char* format, ...) +{ + va_list list; + const char *str; + _SIMPLE_STRING s = _simple_salloc(); + if ( s != NULL ) { + va_start(list, format); + _simple_vsprintf(s, format, list); + va_end(list); + str = _simple_string(s); + } + else { + // _simple_salloc failed, but at least format may have useful info by itself + str = format; + } + _ZN4dyld4haltEPKc(str); + // _ZN4dyld4haltEPKc doesn't return, so we can't call _simple_sfree +} + + +// real cthread_set_errno_self() has error handling that pulls in +// pthread_exit() which pulls in fprintf() +extern int* __error(void); +void cthread_set_errno_self(int err) +{ + int* ep = __error(); + *ep = err; +} + +/* + * We have our own localtime() to avoid needing the notify API which is used + * by the code in libc.a for localtime() which is used by arc4random(). + */ +struct tm* localtime(const time_t* t) +{ + 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; + + +void __guard_setup(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 !TARGET_IPHONE_SIMULATOR +#if __LP64__ + __stack_chk_guard = ((long)arc4random() << 32) | arc4random(); +#else + __stack_chk_guard = arc4random(); +#endif +#endif +} + +extern void _ZN4dyld4haltEPKc(const char*); +void __stack_chk_fail() +{ + _ZN4dyld4haltEPKc("stack buffer overrun"); +} + + +// 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 +FILE* __stderrp = NULL; +FILE* __stdoutp = NULL; + +// 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()"); +} + +// to work with libc++ +void _ZNKSt3__120__vector_base_commonILb1EE20__throw_length_errorEv() +{ + _ZN4dyld4haltEPKc("std::vector<>::_throw_length_error()"); +} + +// libc.a sometimes missing memset +#undef memset +void* memset(void* b, int c, size_t len) +{ + uint8_t* p = (uint8_t*)b; + for(size_t i=len; i > 0; --i) + *p++ = c; + return b; +} + + +// wrap calls to stat() with check for EAGAIN +int _ZN4dyld7my_statEPKcP4stat(const char* path, struct stat* buf) +{ + int result; + do { + result = stat(path, buf); + } while ((result == -1) && (errno == EAGAIN)); + + return result; +} + +// dyld should retry open() if it gets an EGAIN +int _ZN4dyld7my_openEPKcii(const char* path, int flag, int other) +{ + int result; + do { + result = open(path, flag, other); + } while ((result == -1) && (errno == EAGAIN)); + + return result; +} + + +// +// The dyld in the iOS simulator cannot do syscalls, so it calls back to +// host dyld. +// + +#if TARGET_IPHONE_SIMULATOR + +#include + +int myopen(const char* path, int oflag, int extra) __asm("_open"); +int myopen(const char* path, int oflag, int extra) { + return gSyscallHelpers->open(path, oflag, extra); +} + +int close(int fd) { + return gSyscallHelpers->close(fd); +} + +ssize_t pread(int fd, void* buf, size_t nbytes, off_t offset) { + return gSyscallHelpers->pread(fd, buf , nbytes, offset); +} + +ssize_t write(int fd, const void *buf, size_t nbytes) { + return gSyscallHelpers->write(fd, buf , nbytes); +} + +void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset) { + return gSyscallHelpers->mmap(addr, len, prot, flags, fd, offset); +} + +int munmap(void* addr, size_t len) { + return gSyscallHelpers->munmap(addr, len); +} + +int madvise(void* addr, size_t len, int advice) { + return gSyscallHelpers->madvise(addr, len, advice); +} + +int stat(const char* path, struct stat* buf) { + return gSyscallHelpers->stat(path, buf); +} + +int myfcntl(int fd, int cmd, void* result) __asm("_fcntl"); +int myfcntl(int fd, int cmd, void* result) { + return gSyscallHelpers->fcntl(fd, cmd, result); +} + +int myioctl(int fd, unsigned long request, void* result) __asm("_ioctl"); +int myioctl(int fd, unsigned long request, void* result) { + return gSyscallHelpers->ioctl(fd, request, result); +} + +int issetugid() { + return gSyscallHelpers->issetugid(); +} + +char* getcwd(char* buf, size_t size) { + return gSyscallHelpers->getcwd(buf, size); +} + +char* realpath(const char* file_name, char* resolved_name) { + return gSyscallHelpers->realpath(file_name, resolved_name); +} + + + +kern_return_t vm_allocate(vm_map_t target_task, vm_address_t *address, + vm_size_t size, int flags) { + return gSyscallHelpers->vm_allocate(target_task, address, size, flags); +} + +kern_return_t vm_deallocate(vm_map_t target_task, vm_address_t address, + vm_size_t size) { + return gSyscallHelpers->vm_deallocate(target_task, address, size); +} + +kern_return_t vm_protect(vm_map_t target_task, vm_address_t address, + vm_size_t size, boolean_t max, vm_prot_t prot) { + return gSyscallHelpers->vm_protect(target_task, address, size, max, prot); +} + + +void _ZN4dyld3logEPKcz(const char* format, ...) { + va_list list; + va_start(list, format); + gSyscallHelpers->vlog(format, list); + va_end(list); +} + +#if __i386__ +void _ZN4dyld4vlogEPKcPc(const char* format, va_list list) { +#else +void _ZN4dyld4vlogEPKcP13__va_list_tag(const char* format, va_list list) { +#endif + gSyscallHelpers->vlog(format, list); +} + + + +void _ZN4dyld4warnEPKcz(const char* format, ...) { + va_list list; + va_start(list, format); + gSyscallHelpers->vwarn(format, list); + va_end(list); +} + + +int pthread_mutex_lock(pthread_mutex_t* m) { + return gSyscallHelpers->pthread_mutex_lock(m); +} + +int pthread_mutex_unlock(pthread_mutex_t* m) { + return gSyscallHelpers->pthread_mutex_unlock(m); +} + +mach_port_t mach_thread_self() { + return gSyscallHelpers->mach_thread_self(); +} + +kern_return_t mach_port_deallocate(ipc_space_t task, mach_port_name_t name) { + return gSyscallHelpers->mach_port_deallocate(task, name); +} + +mach_port_name_t task_self_trap() { + return gSyscallHelpers->task_self_trap(); +} + +kern_return_t mach_timebase_info(mach_timebase_info_t info) { + return gSyscallHelpers->mach_timebase_info(info); +} + +bool OSAtomicCompareAndSwapPtrBarrier(void* old, void* new, void * volatile *value) { + return gSyscallHelpers->OSAtomicCompareAndSwapPtrBarrier(old, new, value); +} + +void OSMemoryBarrier() { + return gSyscallHelpers->OSMemoryBarrier(); +} + +uint64_t mach_absolute_time(void) { + return gSyscallHelpers->mach_absolute_time(); +} + +kern_return_t thread_switch(mach_port_name_t thread_name, + int option, mach_msg_timeout_t option_time) { + if ( gSyscallHelpers->version < 2 ) + return KERN_FAILURE; + return gSyscallHelpers->thread_switch(thread_name, option, option_time); +} + +DIR* opendir(const char* path) { + if ( gSyscallHelpers->version < 3 ) + return NULL; + return gSyscallHelpers->opendir(path); +} + +int readdir_r(DIR* dirp, struct dirent* entry, struct dirent **result) { + if ( gSyscallHelpers->version < 3 ) + return EPERM; + return gSyscallHelpers->readdir_r(dirp, entry, result); +} + +int closedir(DIR* dirp) { + if ( gSyscallHelpers->version < 3 ) + return EPERM; + return gSyscallHelpers->closedir(dirp); +} + +void xcoresymbolication_load_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh) +{ + // if host dyld supports this notifier, call into host dyld + if ( gSyscallHelpers->version >= 4 ) + return gSyscallHelpers->coresymbolication_load_notifier(connection, timestamp, path, mh); +} + +void xcoresymbolication_unload_notifier(void* connection, uint64_t timestamp, const char* path, const struct mach_header* mh) +{ + // if host dyld supports this notifier, call into host dyld + if ( gSyscallHelpers->version >= 4 ) + return gSyscallHelpers->coresymbolication_unload_notifier(connection, timestamp, path, mh); +} + + + +#define SUPPORT_HOST_10_11 1 + +#if SUPPORT_HOST_10_11 +typedef int (*FuncPtr_proc_regionfilename)(int pid, uint64_t address, void* buffer, uint32_t bufferSize); +typedef pid_t (*FuncPtr_getpid)(); +typedef bool (*FuncPtr_mach_port_insert_right)(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly); +typedef kern_return_t (*FuncPtr_mach_port_allocate)(ipc_space_t, mach_port_right_t, mach_port_name_t*); +typedef mach_msg_return_t (*FuncPtr_mach_msg)(mach_msg_header_t *, mach_msg_option_t , mach_msg_size_t , mach_msg_size_t , mach_port_name_t , mach_msg_timeout_t , mach_port_name_t); + +static FuncPtr_proc_regionfilename proc_proc_regionfilename = NULL; +static FuncPtr_getpid proc_getpid = NULL; +static FuncPtr_mach_port_insert_right proc_mach_port_insert_right = NULL; +static FuncPtr_mach_port_allocate proc_mach_port_allocate = NULL; +static FuncPtr_mach_msg proc_mach_msg = NULL; + + + +// Look up sycalls in host dyld needed by coresymbolication_ routines in dyld_sim +static void findHostFunctions() { + // Only look up symbols once + if ( proc_mach_msg != NULL ) + return; + + struct dyld_all_image_infos* imageInfo = (struct dyld_all_image_infos*)(gSyscallHelpers->getProcessInfo()); + const struct mach_header* hostDyldMH = imageInfo->dyldImageLoadAddress; + + // find symbol table and slide of host dyld + uintptr_t slide = 0; + const macho_nlist* symbolTable = NULL; + const char* symbolTableStrings = NULL; + const struct dysymtab_command* dynSymbolTable = NULL; + const uint32_t cmd_count = hostDyldMH->ncmds; + const struct load_command* const cmds = (struct load_command*)(((char*)hostDyldMH)+sizeof(macho_header)); + const struct load_command* cmd = cmds; + const uint8_t* linkEditBase = NULL; + for (uint32_t i = 0; i < cmd_count; ++i) { + switch (cmd->cmd) { + case LC_SEGMENT_COMMAND: + { + const macho_segment_command* seg = (macho_segment_command*)cmd; + if ( (seg->fileoff == 0) && (seg->filesize != 0) ) + slide = (uintptr_t)hostDyldMH - seg->vmaddr; + if ( strcmp(seg->segname, "__LINKEDIT") == 0 ) + linkEditBase = (uint8_t*)(seg->vmaddr - seg->fileoff + slide); + } + break; + case LC_SYMTAB: + { + const struct symtab_command* symtab = (struct symtab_command*)cmd; + if ( linkEditBase == NULL ) + return; + symbolTableStrings = (const char*)&linkEditBase[symtab->stroff]; + symbolTable = (macho_nlist*)(&linkEditBase[symtab->symoff]); + } + break; + case LC_DYSYMTAB: + dynSymbolTable = (struct dysymtab_command*)cmd; + break; + } + cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); + } + if ( symbolTableStrings == NULL ) + return; + if ( dynSymbolTable == NULL ) + return; + + // scan local symbols in host dyld looking for load/unload functions + const macho_nlist* const localsStart = &symbolTable[dynSymbolTable->ilocalsym]; + const macho_nlist* const localsEnd= &localsStart[dynSymbolTable->nlocalsym]; + for (const macho_nlist* s = localsStart; s < localsEnd; ++s) { + if ( ((s->n_type & N_TYPE) == N_SECT) && ((s->n_type & N_STAB) == 0) ) { + const char* name = &symbolTableStrings[s->n_un.n_strx]; + if ( strcmp(name, "_proc_regionfilename") == 0 ) + proc_proc_regionfilename = (FuncPtr_proc_regionfilename)(s->n_value + slide); + else if ( strcmp(name, "_getpid") == 0 ) + proc_getpid = (FuncPtr_getpid)(s->n_value + slide); + else if ( strcmp(name, "mach_port_insert_right") == 0 ) + proc_mach_port_insert_right = (FuncPtr_mach_port_insert_right)(s->n_value + slide); + else if ( strcmp(name, "_mach_port_allocate") == 0 ) + proc_mach_port_allocate = (FuncPtr_mach_port_allocate)(s->n_value + slide); + else if ( strcmp(name, "_mach_msg") == 0 ) + proc_mach_msg = (FuncPtr_mach_msg)(s->n_value + slide); + } + } +} +#endif + + +int proc_regionfilename(int pid, uint64_t address, void* buffer, uint32_t bufferSize) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->proc_regionfilename(pid, address, buffer, bufferSize); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + if ( proc_proc_regionfilename ) + return (*proc_proc_regionfilename)(pid, address, buffer, bufferSize); + else + return 0; +#else + return 0; +#endif +} + +pid_t getpid() +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->getpid(); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + return (*proc_getpid)(); +#else + return 0; +#endif +} + +kern_return_t mach_port_insert_right(ipc_space_t task, mach_port_name_t name, mach_port_t poly, mach_msg_type_name_t polyPoly) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->mach_port_insert_right(task, name, poly, polyPoly); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + if ( proc_mach_port_insert_right ) + return (*proc_mach_port_insert_right)(task, name, poly, polyPoly); + else + return KERN_NOT_SUPPORTED; +#else + return KERN_NOT_SUPPORTED; +#endif +} + +kern_return_t mach_port_allocate(ipc_space_t task, mach_port_right_t right, mach_port_name_t* name) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->mach_port_allocate(task, right, name); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + return (*proc_mach_port_allocate)(task, right, name); +#else + return KERN_NOT_SUPPORTED; +#endif +} + +kern_return_t mach_msg(mach_msg_header_t* msg, mach_msg_option_t option, mach_msg_size_t send_size, mach_msg_size_t rcv_size, mach_port_name_t rcv_name, mach_msg_timeout_t timeout, mach_port_name_t notify) +{ + if ( gSyscallHelpers->version >= 5 ) + return gSyscallHelpers->mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify); +#if SUPPORT_HOST_10_11 + findHostFunctions(); + return (*proc_mach_msg)(msg, option, send_size, rcv_size, rcv_name, timeout, notify); +#else + return KERN_NOT_SUPPORTED; +#endif +} + + +void abort_with_payload(uint32_t reason_namespace, uint64_t reason_code, void* payload, uint32_t payload_size, const char* reason_string, uint64_t reason_flags) +{ + if ( gSyscallHelpers->version >= 6 ) + gSyscallHelpers->abort_with_payload(reason_namespace, reason_code, payload, payload_size, reason_string, reason_flags); + dyld_fatal_error(reason_string); +} + +kern_return_t task_register_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_image_infos(task, dyld_images, dyld_imagesCnt); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_unregister_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t dyld_images, mach_msg_type_number_t dyld_imagesCnt) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_unregister_dyld_image_infos(task, dyld_images, dyld_imagesCnt); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_get_dyld_image_infos(task_t task, dyld_kernel_image_info_array_t *dyld_images, mach_msg_type_number_t *dyld_imagesCnt) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_get_dyld_image_infos(task, dyld_images, dyld_imagesCnt); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_register_dyld_shared_cache_image_info(task_t task, dyld_kernel_image_info_t dyld_cache_image, boolean_t no_cache, boolean_t private_cache) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_shared_cache_image_info(task, dyld_cache_image, no_cache, private_cache); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_register_dyld_set_dyld_state(task_t task, uint8_t dyld_state) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_set_dyld_state(task, dyld_state); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_register_dyld_get_process_state(task_t task, dyld_kernel_process_info_t *dyld_process_state) { + if ( gSyscallHelpers->version >= 7 ) + return gSyscallHelpers->task_register_dyld_get_process_state(task, dyld_process_state); + return KERN_NOT_SUPPORTED; +} + +kern_return_t task_info(task_name_t target_task, task_flavor_t flavor, task_info_t task_info_out, mach_msg_type_number_t *task_info_outCnt) { + if ( gSyscallHelpers->version >= 8 ) + return gSyscallHelpers->task_info(target_task, flavor, task_info_out, task_info_outCnt); + return KERN_NOT_SUPPORTED; +} + +kern_return_t thread_info(thread_inspect_t target_act, thread_flavor_t flavor, thread_info_t thread_info_out, mach_msg_type_number_t *thread_info_outCnt) { + if ( gSyscallHelpers->version >= 8 ) + return gSyscallHelpers->task_info(target_act, flavor, thread_info_out, thread_info_outCnt); + return KERN_NOT_SUPPORTED; +} + +bool kdebug_is_enabled(uint32_t code) { + if ( gSyscallHelpers->version >= 8 ) + return gSyscallHelpers->kdebug_is_enabled(code); + return false; +} + +int kdebug_trace(uint32_t code, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4) { + if ( gSyscallHelpers->version >= 8 ) + return gSyscallHelpers->kdebug_trace(code, arg1, arg2, arg3, arg4); + return 0; +} + +int* __error(void) { + return gSyscallHelpers->errnoAddress(); +} + +void mach_init() { + mach_task_self_ = task_self_trap(); + //_task_reply_port = _mach_reply_port(); +} + +mach_port_t mach_task_self_ = MACH_PORT_NULL; + +extern int myerrno_fallback __asm("_errno"); +int myerrno_fallback = 0; + +#endif // TARGET_IPHONE_SIMULATOR + + +#if ! TARGET_IPHONE_SIMULATOR + #include "mach-o/dyld_process_info.h" + + void _dyld_debugger_notification(enum dyld_notify_mode mode, unsigned long count, uint64_t machHeaders[]) + { + // Do nothing. This exists for the debugger to set a break point on to see what images have been loaded or unloaded. + } +#endif + + +void* _NSConcreteStackBlock[32]; +void* _NSConcreteGlobalBlock[32]; + +void _Block_object_assign() +{ + _ZN4dyld4haltEPKc("_Block_object_assign()"); +} + +void _Block_object_dispose(const void* object, int flags) +{ + // only support stack blocks in dyld: BLOCK_FIELD_IS_BYREF=8 + if ( flags != 8 ) + _ZN4dyld4haltEPKc("_Block_object_dispose()"); +} + + + diff --git a/dyld/src/libdyld_data_symbols.dirty b/dyld/src/libdyld_data_symbols.dirty new file mode 100644 index 0000000..85d1a92 --- /dev/null +++ b/dyld/src/libdyld_data_symbols.dirty @@ -0,0 +1,12 @@ +__ZL12sGlobalMutex +__ZZ26NSVersionOfLinkTimeLibraryE2mh +__ZZ33_dyld_register_func_for_add_imageE1p +__ZZ36_dyld_register_func_for_remove_imageE1p +__ZZ21_dyld_get_image_slideE1p +__ZZ6dlopenE1p +__ZZ40dyld_register_image_state_change_handlerE1p +__ZZ21_dyld_fast_stub_entryPvlE1p +__ZZ34dyld_image_path_containing_addressE1p +__ZZ20_NSGetExecutablePathE1p +_myDyldSection +_tlv_terminators_key diff --git a/dyld/src/libdyld_sim.exp b/dyld/src/libdyld_sim.exp new file mode 100644 index 0000000..be8a4e4 --- /dev/null +++ b/dyld/src/libdyld_sim.exp @@ -0,0 +1,5 @@ +_dyld_get_sdk_version +_dyld_get_program_sdk_version +_dyld_get_min_os_version +_dyld_get_program_min_os_version + diff --git a/dyld/src/start_glue.h b/dyld/src/start_glue.h new file mode 100644 index 0000000..57b03c1 --- /dev/null +++ b/dyld/src/start_glue.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef __START_GLUE_H__ +#define __START_GLUE_H__ + +// Implemented in start_glue.s +extern "C" void start(); + + +// need 'start' to be one atom, but entry is in interior + +#if __x86_64__ || __i386__ + #define address_of_start (void*)((uintptr_t)&start + 1) +#elif __arm64__ + #define address_of_start (void*)((uintptr_t)&start + 4) +#elif __arm__ + #define address_of_start (void*)((uintptr_t)&start + 2) +#endif + + + +#endif // __START_GLUE_H__ diff --git a/dyld/src/start_glue.s b/dyld/src/start_glue.s new file mode 100644 index 0000000..3336f66 --- /dev/null +++ b/dyld/src/start_glue.s @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +#ifdef __i386__ + + .align 2 + .globl _start + .private_extern _start +_start: + nop # backtraces of LC_MAIN binaries don't end in "start" +Lstart: + movl %eax,(%esp) # pass result from main() to exit() + call _exit + hlt + +#endif /* __i386__ */ + + +#if __x86_64__ + + .align 2 + .globl _start + .private_extern _start +_start: + nop # backtraces of LC_MAIN binaries don't end in "start" +Lstart: + movl %eax,%edi # pass result from main() to exit() + call _exit + hlt + +#endif + + +#if __arm__ + + .align 2 + .code 16 + .globl _start + .private_extern _start + .thumb_func _start +_start: + nop // backtraces of LC_MAIN binaries don't end in "start" +Lstart: + bl _exit // result in r0 already in param reg r0 + trap + +#endif /* __arm__ */ + + +#if __arm64__ + + .align 2 + .globl _start + .private_extern _start +_start: + nop +Lstart: + bl _exit // result in x0 already in param reg x0 + brk #3 + +#endif /* __arm64__ */ + + .subsections_via_symbols + diff --git a/dyld/src/stub_binding_helper.s b/dyld/src/stub_binding_helper.s new file mode 100644 index 0000000..de2d90b --- /dev/null +++ b/dyld/src/stub_binding_helper.s @@ -0,0 +1,226 @@ +/* + * Copyright (c) 1999-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@ + */ + + + +#ifdef __i386__ +/* + * This is the interface for the stub_binding_helper for i386: + * The caller has pushed the address of the a lazy pointer to be filled in + * and pushed the address of the the mach header this pointer comes from. + * + * sp+4 address of lazy pointer + * sp+0 address of mach header + * + * Some inter-image function calls pass parameters in registers EAX, ECX, EDX, or XXM0-3, + * Therefore those registers need to be preserved during the lazy binding. + * + * After the symbol has been resolved and the lazy pointer filled in, this jumps + * to the target address. + */ +#define MH_PARAM_OUT 0 +#define LP_PARAM_OUT 4 +#define XMMM0_SAVE 16 /* 16-byte align */ +#define XMMM1_SAVE 32 +#define XMMM2_SAVE 48 +#define XMMM3_SAVE 64 +#define EAX_SAVE 84 +#define ECX_SAVE 88 +#define EDX_SAVE 92 +#define LP_LOCAL 96 +#define MH_LOCAL 100 +#define STACK_SIZE 100 /* must be 4 mod 16 so that stack winds up 16-byte aliged */ +#define LP_OLD_BP_SAVE 104 + + .text + .align 4,0x90 + .globl _stub_binding_helper_i386_old +_stub_binding_helper_i386_old: + pushl $0 + .globl _stub_binding_helper + .globl _misaligned_stack_error +_stub_binding_helper: + subl $STACK_SIZE,%esp # makes stack 16-byte aligned + movl %eax,EAX_SAVE(%esp) + movl LP_OLD_BP_SAVE(%esp),%eax # get lazy-pointer meta-parameter + movl %eax,LP_LOCAL(%esp) + movl %ebp,LP_OLD_BP_SAVE(%esp) # store epb back chain + movl %esp,%ebp # set epb to be this frame + add $LP_OLD_BP_SAVE,%ebp + movl %ecx,ECX_SAVE(%esp) + movl %edx,EDX_SAVE(%esp) + .align 0,0x90 +_misaligned_stack_error: + movdqa %xmm0,XMMM0_SAVE(%esp) + movdqa %xmm1,XMMM1_SAVE(%esp) + movdqa %xmm2,XMMM2_SAVE(%esp) + movdqa %xmm3,XMMM3_SAVE(%esp) +_stub_binding_helper_interface2: + movl MH_LOCAL(%esp),%eax # call dyld::bindLazySymbol(mh, lazy_ptr) + movl %eax,MH_PARAM_OUT(%esp) + movl LP_LOCAL(%esp),%eax + movl %eax,LP_PARAM_OUT(%esp) + call __ZN4dyld14bindLazySymbolEPK11mach_headerPm + movdqa XMMM0_SAVE(%esp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%esp),%xmm1 + movdqa XMMM2_SAVE(%esp),%xmm2 + movdqa XMMM3_SAVE(%esp),%xmm3 + movl ECX_SAVE(%esp),%ecx + movl EDX_SAVE(%esp),%edx + movl %eax,%ebp # move target address to epb + movl EAX_SAVE(%esp),%eax # restore eaz + addl $STACK_SIZE+4,%esp # cut back stack + xchg %ebp, (%esp) # restore ebp and set target to top of stack + ret # jump to target + +#endif /* __i386__ */ + + +#if __x86_64__ +/* + * This is the interface for the stub_binding_helper for x86_64: + * The caller has pushed the address of the a lazy pointer to be filled in with + * the value for the defined symbol and pushed the address of the the mach + * header this pointer comes from. + * + * sp+8 address of lazy pointer + * sp+0 address of mach header + * + * All parameters registers must be preserved. + * + * After the symbol has been resolved and the pointer filled in this is to pop + * these arguments off the stack and jump to the address of the defined symbol. + */ +#define MH_PARAM_BP 8 +#define LP_PARAM_BP 16 + +#define RDI_SAVE 0 +#define RSI_SAVE 8 +#define RDX_SAVE 16 +#define RCX_SAVE 24 +#define R8_SAVE 32 +#define R9_SAVE 40 +#define RAX_SAVE 48 +#define XMMM0_SAVE 64 /* 16-byte align */ +#define XMMM1_SAVE 80 +#define XMMM2_SAVE 96 +#define XMMM3_SAVE 112 +#define XMMM4_SAVE 128 +#define XMMM5_SAVE 144 +#define XMMM6_SAVE 160 +#define XMMM7_SAVE 176 +#define STACK_SIZE 192 /* (XMMM7_SAVE+16) must be 16 byte aligned too */ + + .text + .align 2,0x90 + .globl _stub_binding_helper +_stub_binding_helper: + pushq %rbp + movq %rsp,%rbp + subq $STACK_SIZE,%rsp # at this point stack is 16-byte aligned because two meta-parameters where pushed + movq %rdi,RDI_SAVE(%rsp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE(%rsp) + movq %rdx,RDX_SAVE(%rsp) + movq %rcx,RCX_SAVE(%rsp) + movq %r8,R8_SAVE(%rsp) + movq %r9,R9_SAVE(%rsp) + movq %rax,RAX_SAVE(%rsp) + movdqa %xmm0,XMMM0_SAVE(%rsp) + movdqa %xmm1,XMMM1_SAVE(%rsp) + movdqa %xmm2,XMMM2_SAVE(%rsp) + movdqa %xmm3,XMMM3_SAVE(%rsp) + movdqa %xmm4,XMMM4_SAVE(%rsp) + movdqa %xmm5,XMMM5_SAVE(%rsp) + movdqa %xmm6,XMMM6_SAVE(%rsp) + movdqa %xmm7,XMMM7_SAVE(%rsp) + movq MH_PARAM_BP(%rbp),%rdi # call dyld::bindLazySymbol(mh, lazy_ptr) + movq LP_PARAM_BP(%rbp),%rsi + call __ZN4dyld14bindLazySymbolEPK11mach_headerPm + movq %rax,%r11 # save target + movdqa XMMM0_SAVE(%rsp),%xmm0 # restore registers + movdqa XMMM1_SAVE(%rsp),%xmm1 + movdqa XMMM2_SAVE(%rsp),%xmm2 + movdqa XMMM3_SAVE(%rsp),%xmm3 + movdqa XMMM4_SAVE(%rsp),%xmm4 + movdqa XMMM5_SAVE(%rsp),%xmm5 + movdqa XMMM6_SAVE(%rsp),%xmm6 + movdqa XMMM7_SAVE(%rsp),%xmm7 + movq RDI_SAVE(%rsp),%rdi + movq RSI_SAVE(%rsp),%rsi + movq RDX_SAVE(%rsp),%rdx + movq RCX_SAVE(%rsp),%rcx + movq R8_SAVE(%rsp),%r8 + movq R9_SAVE(%rsp),%r9 + movq RAX_SAVE(%rsp),%rax + addq $STACK_SIZE,%rsp + popq %rbp + addq $16,%rsp # remove meta-parameters + jmp *%r11 # jmp to target + +#endif + + +#if __arm__ && !__ARM_ARCH_7K__ +/* + * This is the interface for the old stub_binding_helper for ARM: + * The caller has pushed the address of the a lazy pointer to be filled in with + * the value for the defined symbol and pushed the address of the the mach + * header this pointer comes from. + * + * sp+4 address of lazy pointer + * sp+0 address of mach header + * + * After the symbol has been resolved and the pointer filled in this is to pop + * these arguments off the stack and jump to the address of the defined symbol. + */ + + .text + .align 2 + .globl _stub_binding_helper +_stub_binding_helper: + stmfd sp!, {r0,r1,r2,r3,r7,lr} // save registers + add r7, sp, #16 // point FP to previous FP + + ldr r0, [sp, #24] // move address of mach header to 1st parameter + ldr r1, [sp, #28] // move address of lazy pointer to 2nd parameter + + // call dyld::bindLazySymbol(mh, lazy_symbol_pointer_address) + bl __ZN4dyld14bindLazySymbolEPK11mach_headerPm + mov ip, r0 // move the symbol`s address into ip + + ldmfd sp!, {r0,r1,r2,r3,r7,lr} // restore registers + add sp, sp, #8 // remove meta-parameters + + bx ip // jump to the symbol`s address that was bound + +#endif /* __arm__ */ + + + + + + + + + + diff --git a/dyld/src/threadLocalHelpers.s b/dyld/src/threadLocalHelpers.s new file mode 100644 index 0000000..fb63817 --- /dev/null +++ b/dyld/src/threadLocalHelpers.s @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2010-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +// bool save_xxm = (*((uint32_t*)_COMM_PAGE_CPU_CAPABILITIES) & kHasAVX1_0) != 0; + +#if __x86_64__ + +#define RDI_SAVE_RBP -8 +#define RSI_SAVE_RBP -16 +#define RDX_SAVE_RBP -24 +#define RCX_SAVE_RBP -32 +#define RBX_SAVE_RBP -40 +#define R8_SAVE_RBP -48 +#define R9_SAVE_RBP -56 +#define R10_SAVE_RBP -64 +#define R11_SAVE_RBP -72 +#define STATIC_STACK_SIZE 256 // extra padding to allow it to be 64-byte aligned + +#define XMM0_SAVE_RSP 0x00 +#define XMM1_SAVE_RSP 0x10 +#define XMM2_SAVE_RSP 0x20 +#define XMM3_SAVE_RSP 0x30 +#define XMM4_SAVE_RSP 0x40 +#define XMM5_SAVE_RSP 0x50 +#define XMM6_SAVE_RSP 0x60 +#define XMM7_SAVE_RSP 0x70 + + + // returns address of TLV in %rax, all other registers preserved + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + 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 $STATIC_STACK_SIZE,%rsp + movq %rdi,RDI_SAVE_RBP(%rbp) # save registers that might be used as parameters + movq %rsi,RSI_SAVE_RBP(%rbp) + movq %rdx,RDX_SAVE_RBP(%rbp) + movq %rcx,RCX_SAVE_RBP(%rbp) + movq %rbx,RBX_SAVE_RBP(%rbp) + movq %r8, R8_SAVE_RBP(%rbp) + movq %r9, R9_SAVE_RBP(%rbp) + movq %r10,R10_SAVE_RBP(%rbp) + movq %r11,R11_SAVE_RBP(%rbp) + + cmpl $0, _inited(%rip) + jne Linited + movl $0x01,%eax + cpuid # get cpu features to check on xsave instruction support + andl $0x08000000,%ecx # check OSXSAVE bit + movl %ecx,_hasXSave(%rip) + cmpl $0, %ecx + jne LxsaveInfo + movl $1, _inited(%rip) + jmp Lsse + +LxsaveInfo: + movl $0x0D,%eax + movl $0x00,%ecx + cpuid # get xsave parameter info + movl %eax,_features_lo32(%rip) + movl %edx,_features_hi32(%rip) + movl %ecx,_bufferSize32(%rip) + movl $1, _inited(%rip) + +Linited: + cmpl $0, _hasXSave(%rip) + jne Lxsave + +Lsse: + subq $128, %rsp + movdqa %xmm0, XMM0_SAVE_RSP(%rsp) + movdqa %xmm1, XMM1_SAVE_RSP(%rsp) + movdqa %xmm2, XMM2_SAVE_RSP(%rsp) + movdqa %xmm3, XMM3_SAVE_RSP(%rsp) + movdqa %xmm4, XMM4_SAVE_RSP(%rsp) + movdqa %xmm5, XMM5_SAVE_RSP(%rsp) + movdqa %xmm6, XMM6_SAVE_RSP(%rsp) + movdqa %xmm7, XMM7_SAVE_RSP(%rsp) + jmp Lalloc + +Lxsave: + movl _bufferSize32(%rip),%eax + movq %rsp, %rdi + subq %rax, %rdi # stack alloc buffer + andq $-64, %rdi # 64-byte align stack + movq %rdi, %rsp + # xsave requires buffer to be zero'ed out + movq $0, %rcx + movq %rdi, %r8 + movq %rdi, %r9 + addq %rax, %r9 +Lz: movq %rcx, (%r8) + addq $8, %r8 + cmpq %r8,%r9 + ja Lz + + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + # note: do not use xsaveopt, it assumes you are using the same + # buffer as previous xsaves, and this thread is on the same cpu. + xsave (%rsp) + +Lalloc: + movq RDI_SAVE_RBP(%rbp),%rdi + movq 8(%rdi),%rdi // get key from descriptor + call _tlv_allocate_and_initialize_for_key + + cmpl $0, _hasXSave(%rip) + jne Lxrstror + + movdqa XMM0_SAVE_RSP(%rsp),%xmm0 + movdqa XMM1_SAVE_RSP(%rsp),%xmm1 + movdqa XMM2_SAVE_RSP(%rsp),%xmm2 + movdqa XMM3_SAVE_RSP(%rsp),%xmm3 + movdqa XMM4_SAVE_RSP(%rsp),%xmm4 + movdqa XMM5_SAVE_RSP(%rsp),%xmm5 + movdqa XMM6_SAVE_RSP(%rsp),%xmm6 + movdqa XMM7_SAVE_RSP(%rsp),%xmm7 + jmp Ldone + +Lxrstror: + movq %rax,%r11 + movl _features_lo32(%rip),%eax + movl _features_hi32(%rip),%edx + # call xsave with buffer on stack and eax:edx flag bits + xrstor (%rsp) + movq %r11,%rax + +Ldone: + movq RDI_SAVE_RBP(%rbp),%rdi + movq RSI_SAVE_RBP(%rbp),%rsi + movq RDX_SAVE_RBP(%rbp),%rdx + movq RCX_SAVE_RBP(%rbp),%rcx + movq RBX_SAVE_RBP(%rbp),%rbx + movq R8_SAVE_RBP(%rbp),%r8 + movq R9_SAVE_RBP(%rbp),%r9 + movq R10_SAVE_RBP(%rbp),%r10 + movq R11_SAVE_RBP(%rbp),%r11 + movq %rbp,%rsp + popq %rbp + addq 16(%rdi),%rax // result = buffer + offset + ret + + .data +# Cached info from cpuid. +_inited: .long 0 +_features_lo32: .long 0 +_features_hi32: .long 0 +_bufferSize32: .long 0 +_hasXSave: .long 0 + +#endif + + + +#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 __arm64__ + // Parameters: X0 = descriptor + // Result: X0 = address of TLV + // Note: all registers except X0, x16, and x17 are preserved + .align 2 + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + ldr x16, [x0, #8] // get key from descriptor + mrs x17, TPIDRRO_EL0 + and x17, x17, #-8 // clear low 3 bits??? + ldr x17, [x17, x16, lsl #3] // get thread allocation address for this key + cbz x17, LlazyAllocate // if NULL, lazily allocate + ldr x16, [x0, #16] // get offset from descriptor + add x0, x17, x16 // return allocation+offset + ret lr + +LlazyAllocate: + stp fp, lr, [sp, #-16]! + mov fp, sp + sub sp, sp, #288 + stp x1, x2, [sp, #-16]! // save all registers that C function might trash + stp x3, x4, [sp, #-16]! + stp x5, x6, [sp, #-16]! + stp x7, x8, [sp, #-16]! + stp x9, x10, [sp, #-16]! + stp x11, x12, [sp, #-16]! + stp x13, x14, [sp, #-16]! + stp x15, x16, [sp, #-16]! + stp q0, q1, [sp, #-32]! + stp q2, q3, [sp, #-32]! + stp q4, q5, [sp, #-32]! + stp q6, q7, [sp, #-32]! + stp x0, x17, [sp, #-16]! // save descriptor + + mov x0, x16 // use key from descriptor as parameter + bl _tlv_allocate_and_initialize_for_key + ldp x16, x17, [sp], #16 // pop descriptor + ldr x16, [x16, #16] // get offset from descriptor + add x0, x0, x16 // return allocation+offset + + ldp q6, q7, [sp], #32 + ldp q4, q5, [sp], #32 + ldp q2, q3, [sp], #32 + ldp q0, q1, [sp], #32 + ldp x15, x16, [sp], #16 + ldp x13, x14, [sp], #16 + ldp x11, x12, [sp], #16 + ldp x9, x10, [sp], #16 + ldp x7, x8, [sp], #16 + ldp x5, x6, [sp], #16 + ldp x3, x4, [sp], #16 + ldp x1, x2, [sp], #16 + + mov sp, fp + ldp fp, lr, [sp], #16 + ret lr + +#endif + +#if __arm__ + // returns address of TLV in r0, all other registers preserved + .align 2 + .globl _tlv_get_addr + .private_extern _tlv_get_addr +_tlv_get_addr: + push {r1,r2,r3,r7,lr} +#if __ARM_ARCH_7K__ + sub sp, sp, #12 // align stack to 16 bytes +#endif + 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 +#if __ARM_ARCH_7K__ + vpush {d0, d1, d2, d3, d4, d5, d6, d7} +#endif + ldr r0, [r7, #4] // get key from descriptor + bl _tlv_allocate_and_initialize_for_key +#if __ARM_ARCH_7K__ + vpop {d0, d1, d2, d3, d4, d5, d6, d7} +#endif +L2: ldr r1, [r7, #8] // get offset from descriptor + add r0, r1, r0 // add offset into allocation block +#if __ARM_ARCH_7K__ + add sp, sp, #12 +#endif + pop {r1,r2,r3,r7,pc} +#endif + + .subsections_via_symbols + + diff --git a/dyld/src/threadLocalVariables.c b/dyld/src/threadLocalVariables.c new file mode 100644 index 0000000..a0b3661 --- /dev/null +++ b/dyld/src/threadLocalVariables.c @@ -0,0 +1,497 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + + +typedef void (*TermFunc)(void*); + + + +#if __has_feature(tls) || __arm64__ || __arm__ + +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); + if ( mh == NULL ) + return NULL; // if data structures are screwed up, don't crash + + // first pass, find size and template + uint8_t* start = NULL; + unsigned long size = 0; + 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); + } + // no thread local storage in image: should never happen + if ( size == 0 ) + return NULL; + + // 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 size_t count = sect->size / sizeof(uintptr_t); + for (size_t j=count; j > 0; --j) { + InitFunc func = funcs[j-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); + } +} + + +static void tlv_load_notification(const struct mach_header* mh, intptr_t slide) +{ + // 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. + if ( mh->flags & MH_HAS_TLV_DESCRIPTORS ) + tlv_initialize_descriptors(mh); +} + + +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. +// + +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->useCount == list->allocCount ) { + // handle resizing allocation + uint32_t newAllocCount = list->allocCount * 2; + size_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 away and +// _tlv_atexit() has been called on the thread. +static void tlv_finalize(void* storage) +{ + struct TLVTerminatorList* list = (struct TLVTerminatorList*)storage; + // destroy in reverse order of construction + for(uint32_t i=list->useCount; i > 0 ; --i) { + struct TLVTerminatorListEntry* entry = &list->entries[i-1]; + if ( entry->termFunc != NULL ) { + (*entry->termFunc)(entry->objAddr); + } + } + free(storage); +} + +// +// called by exit() before it calls cxa_finalize() so that thread_local +// objects are destroyed before global objects. +void _tlv_exit() +{ + void* termFuncs = pthread_getspecific(tlv_terminators_key); + if ( termFuncs != NULL ) + tlv_finalize(termFuncs); +} + + +__attribute__((visibility("hidden"))) +void tlv_initializer() +{ + // 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_func_for_add_image(tlv_load_notification); + +} + + +// linked images with TLV have references to this symbol, but it is never used at runtime +void _tlv_bootstrap() +{ + abort(); +} + + + +#else + + + +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) +{ +} + +void _tlv_exit() +{ +} + +void _tlv_atexit(TermFunc func, void* objAddr) +{ +} + +__attribute__((visibility("hidden"))) +void tlv_initializer() +{ +} + + + +#endif // __has_feature(tls) + + diff --git a/dyld/testing/README.txt b/dyld/testing/README.txt new file mode 100755 index 0000000..f94a6f6 --- /dev/null +++ b/dyld/testing/README.txt @@ -0,0 +1,47 @@ + +When the dyld_tests target is built, all test cases are built into /AppleInternal/. +A test case is a directory in $SRCROOT/testing/test-cases/ whose name ends in ".dtest". +The build system scraps any .c or .cxx files in the .dtest directory looking for BUILD: or RUN: lines. +The BUILD: lines are use to build the test case binaries. +The RUN: lines are used to build the information needed for BATS to run the test cases. +Example, main.c may contain: + + // BUILD: $CC main.c -o $BUILD_DIR/example.exe + // RUN: ./example.exe + int main() { return 0; } + +When build lines are executed, the current directory is set to the test case's .dtest dir. +Build lines may contain the follow variables: + $BUILD_DIR - expands to the directory in $DSTROOT where this test case binaries are installed + $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries will be run + $TEMP_DIR - expands to a temporary directory that will be delete after this test case is built + $CC - expands to the C compiler command line for the current platform. It includes + the min-os-version option, then SDK option, and the architectures. + $CXX - expands to the C++ compiler plus standard options + +When run lines are executed, the current directory is set to what $RUN_DIR was during build. +Run lines may contain the follow variables: + $RUN_DIR - expands to the directory in /AppleInternal/ where this test case binaries are installed + $REQUIRE_CRASH - expands to the 'nocr' tool which is used when a program is expected to "crash" in order to pass + + +When a test program runs, it should initially print out the name of the test case. Ex: + [BEGIN] dlfoo-thread-safe +Then it should print a pass or fail message with the same test name. Ex: + [PASS] dlfoo-thread-safe + + +To support tests that dyld is supposed to terminate, use $REQUIRE_CRASH on the RUN: line +along with setting env var NOCR_TEST_NAME to the name of the test case. When that env +var is set, the nocr tool wil print out the [BEGIN], then [PASS] if the test crashes +otherwise [FAIL]. Ex: + // RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe + + +To support tests that are platform specific, add the BUILD_ONLY: line which specifies the platform. +Valid platforms are: MacOSX, iOS, watchOS, and tvOS. When a specific platform is specified, a +new min OS version can also be specified via the BUILD_MIN_OS option. For instance: + // BUILD_ONLY: MacOSX + // BUILD_MIN_OS: 10.5 + + diff --git a/dyld/testing/build_tests.py b/dyld/testing/build_tests.py new file mode 100755 index 0000000..ab4d4fd --- /dev/null +++ b/dyld/testing/build_tests.py @@ -0,0 +1,216 @@ +#!/usr/bin/python2.7 + +import plistlib +import string +import argparse +import sys +import os +import tempfile +import shutil +import subprocess + + +# +# Scan files in .dtest directory looking for BUILD: or RUN: directives. +# Return a dictionary of all directives. +# +def parseDirectives(testCaseSourceDir): + onlyLines = [] + buildLines = [] + runLines = [] + minOS = "" + timeout = "" + for file in os.listdir(testCaseSourceDir): + if file.endswith((".c", ".cpp", ".cxx")): + with open(testCaseSourceDir + "/" + file) as f: + for line in f.read().splitlines(): + buildIndex = string.find(line, "BUILD:") + if buildIndex != -1: + buildLines.append(line[buildIndex + 6:].lstrip()) + runIndex = string.find(line, "RUN:") + if runIndex != -1: + runLines.append(line[runIndex+4:].lstrip()) + onlyIndex = string.find(line, "BUILD_ONLY:") + if onlyIndex != -1: + onlyLines.append(line[onlyIndex+11:].lstrip()) + minOsIndex = string.find(line, "BUILD_MIN_OS:") + if minOsIndex != -1: + minOS = line[minOsIndex+13:].lstrip() + timeoutIndex = string.find(line, "RUN_TIMEOUT:") + if timeoutIndex != -1: + timeout = line[timeoutIndex+12:].lstrip() + + return { + "BUILD": buildLines, + "BUILD_ONLY": onlyLines, + "BUILD_MIN_OS": minOS, + "RUN": runLines, + "RUN_TIMEOUT": timeout + } + + +# +# Look at directives dictionary to see if this test should be skipped for this platform +# +def useTestCase(testCaseDirectives, platformName): + onlyLines = testCaseDirectives["BUILD_ONLY"] + for only in onlyLines: + if only == "MacOSX" and platformName != "macosx": + return False + if only == "iOS" and platformName != "iphoneos": + return False + return True + + +# +# Use BUILD directives to construct the test case +# Use RUN directives to generate a shell script to run test(s) +# +def buildTestCase(testCaseDirectives, testCaseSourceDir, toolsDir, sdkDir, minOsOptionsName, defaultMinOS, archOptions, testCaseDestDirBuild, testCaseDestDirRun): + scratchDir = tempfile.mkdtemp() + if testCaseDirectives["BUILD_MIN_OS"]: + minOS = testCaseDirectives["BUILD_MIN_OS"] + else: + minOS = defaultMinOS + compilerSearchOptions = " -isysroot " + sdkDir + " -I" + sdkDir + "/System/Library/Frameworks/System.framework/PrivateHeaders" + if minOsOptionsName == "mmacosx-version-min": + taskForPidCommand = "touch " + envEnableCommand = "touch " + else: + taskForPidCommand = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../task_for_pid_entitlement.plist " + envEnableCommand = "codesign --force --sign - --entitlements " + testCaseSourceDir + "/../../get_task_allow_entitlement.plist " + buildSubs = { + "CC": toolsDir + "/usr/bin/clang " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions, + "CXX": toolsDir + "/usr/bin/clang++ " + archOptions + " -" + minOsOptionsName + "=" + str(minOS) + compilerSearchOptions, + "BUILD_DIR": testCaseDestDirBuild, + "RUN_DIR": testCaseDestDirRun, + "TEMP_DIR": scratchDir, + "TASK_FOR_PID_ENABLE": taskForPidCommand, + "DYLD_ENV_VARS_ENABLE": envEnableCommand + } + os.makedirs(testCaseDestDirBuild) + os.chdir(testCaseSourceDir) + print >> sys.stderr, "cd " + testCaseSourceDir + for line in testCaseDirectives["BUILD"]: + cmd = string.Template(line).safe_substitute(buildSubs) + print >> sys.stderr, cmd + if "&&" in cmd: + result = subprocess.call(cmd, shell=True) + else: + cmdList = [] + cmdList = string.split(cmd) + result = subprocess.call(cmdList) + if result: + return result + shutil.rmtree(scratchDir, ignore_errors=True) + sudoSub = "" + if minOsOptionsName == "mmacosx-version-min": + sudoSub = "sudo" + runSubs = { + "RUN_DIR": testCaseDestDirRun, + "REQUIRE_CRASH": "nocr -require_crash", + "SUDO": sudoSub, + } + runFilePath = testCaseDestDirBuild + "/run.sh" + with open(runFilePath, "a") as runFile: + runFile.write("#!/bin/sh\n") + runFile.write("cd " + testCaseDestDirRun + "\n") + os.chmod(runFilePath, 0755) + for runline in testCaseDirectives["RUN"]: + runFile.write(string.Template(runline).safe_substitute(runSubs) + "\n") + runFile.write("\n") + runFile.close() + return 0 + + + +# +# Use XCode build settings to build all unit tests for specified platform +# Generate a .plist for BATS to use to run all tests +# +if __name__ == "__main__": + dstDir = os.getenv("DSTROOT", "/tmp/dyld_tests/") + testsRunDstTopDir = "/AppleInternal/CoreOS/tests/dyld/" + testsBuildDstTopDir = dstDir + testsRunDstTopDir + shutil.rmtree(testsBuildDstTopDir, ignore_errors=True) + dyldSrcDir = os.getenv("SRCROOT", "") + if not dyldSrcDir: + dyldSrcDir = os.getcwd() + testsSrcTopDir = dyldSrcDir + "/testing/test-cases/" + sdkDir = os.getenv("SDKROOT", "") + if not sdkDir: + #sdkDir = subprocess.check_output(["xcrun", "--show-sdk-path"]).rstrip() + sdkDir = "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.Internal.sdk" + toolsDir = os.getenv("TOOLCHAIN_DIR", "/") + defaultMinOS = "" + minVersNum = "10.12" + minOSOption = os.getenv("DEPLOYMENT_TARGET_CLANG_FLAG_NAME", "") + if minOSOption: + minOSVersName = os.getenv("DEPLOYMENT_TARGET_CLANG_ENV_NAME", "") + if minOSVersName: + minVersNum = os.getenv(minOSVersName, "") + else: + minOSOption = "mmacosx-version-min" + platformName = os.getenv("PLATFORM_NAME", "osx") + archOptions = "" + archList = os.getenv("RC_ARCHS", "") + if archList: + for arch in string.split(archList, " "): + archOptions = archOptions + " -arch " + arch + else: + archList = os.getenv("ARCHS_STANDARD_32_64_BIT", "") + if platformName == "watchos": + archOptions = "-arch armv7k" + elif platformName == "appletvos": + archOptions = "-arch arm64" + else: + if archList: + for arch in string.split(archList, " "): + archOptions = archOptions + " -arch " + arch + else: + archOptions = "-arch x86_64" + allTests = [] + for f in os.listdir(testsSrcTopDir): + if f.endswith(".dtest"): + testName = f[0:-6] + outDirBuild = testsBuildDstTopDir + testName + outDirRun = testsRunDstTopDir + testName + testCaseDir = testsSrcTopDir + f + testCaseDirectives = parseDirectives(testCaseDir) + if useTestCase(testCaseDirectives, platformName): + result = buildTestCase(testCaseDirectives, testCaseDir, toolsDir, sdkDir, minOSOption, minVersNum, archOptions, outDirBuild, outDirRun) + if result: + sys.exit(result) + mytest = {} + mytest["TestName"] = testName + mytest["Arch"] = "platform-native" + mytest["WorkingDirectory"] = testsRunDstTopDir + testName + mytest["Command"] = [] + mytest["Command"].append("./run.sh") + for runline in testCaseDirectives["RUN"]: + if "$SUDO" in runline: + mytest["AsRoot"] = True + if testCaseDirectives["RUN_TIMEOUT"]: + mytest["Timeout"] = testCaseDirectives["RUN_TIMEOUT"] + allTests.append(mytest) + batsInfo = { "BATSConfigVersion": "0.1.0", + "Project": "dyld_tests", + "Tests": allTests } + batsFileDir = dstDir + "/AppleInternal/CoreOS/BATS/unit_tests/" + shutil.rmtree(batsFileDir, ignore_errors=True) + os.makedirs(batsFileDir) + batsFilePath = batsFileDir + "dyld.plist" + with open(batsFilePath, "w") as batsFile: + batsFile.write(plistlib.writePlistToString(batsInfo)) + batsFile.close() + os.system('plutil -convert binary1 ' + batsFilePath) # convert the plist in place to binary + runHelper = dstDir + "/AppleInternal/CoreOS/tests/dyld/run_all_dyld_tests.sh" + print runHelper + with open(runHelper, "w") as shFile: + shFile.write("#!/bin/sh\n") + for test in allTests: + shFile.write(test["WorkingDirectory"] + "/run.sh\n") + shFile.close() + os.chmod(runHelper, 0755) + + diff --git a/dyld/testing/get_task_allow_entitlement.plist b/dyld/testing/get_task_allow_entitlement.plist new file mode 100644 index 0000000..bd10085 --- /dev/null +++ b/dyld/testing/get_task_allow_entitlement.plist @@ -0,0 +1,8 @@ + + + + + get-task-allow + + + diff --git a/dyld/testing/nocr/execserver.defs b/dyld/testing/nocr/execserver.defs new file mode 100644 index 0000000..e528df4 --- /dev/null +++ b/dyld/testing/nocr/execserver.defs @@ -0,0 +1 @@ +#include diff --git a/dyld/testing/nocr/nocr.1 b/dyld/testing/nocr/nocr.1 new file mode 100644 index 0000000..2c2bd3e --- /dev/null +++ b/dyld/testing/nocr/nocr.1 @@ -0,0 +1,26 @@ +.Dd Dec 15, 2015 +.Dt nocr 1 +.Os Darwin +.Sh NAME +.Nm nocr +.Nd "runs a command with crash reporter disabled" +.Sh SYNOPSIS +.HP 5n +\fBnocr\fR +\fIcommand\fR +.br +.Sh DESCRIPTION +\fBnocr\fR +executes +\fIcommand\fR +in a way that blocks crash reporter should the command process crash. +This is useful when running test suites +.Pp +.Sh "EXIT VALUE" +Upon successful execution of a program, the exit status from +\fInocr\fR +will simply be the exit status of the program that was executed. +.Pp +If the program +\fIcommand\fR +crashed, the exit value will be 1 diff --git a/dyld/testing/nocr/nocr.c b/dyld/testing/nocr/nocr.c new file mode 100644 index 0000000..74c1e88 --- /dev/null +++ b/dyld/testing/nocr/nocr.c @@ -0,0 +1,189 @@ +#include "execserverServer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +static pid_t sChildPid; +static dispatch_semaphore_t sServerRunning; +static bool sChildCrashed = false; +static bool sChildTerminatedByDyld = false; + +/* + * setup exception handling port for EXC_CRASH and EXC_CORPSE_NOTIFY. + * runs mach_msg_server once for receiving exception messages from kernel. + */ +static void* serverCode(void* arg) +{ + mach_port_t exception_port; + + kern_return_t kret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port); + if (kret != KERN_SUCCESS) + errx(1, "mach_port_allocate: %s (%d)", mach_error_string(kret), kret); + + kret = mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND); + if (kret != KERN_SUCCESS) + errx(1, "mach_port_insert_right: %s (%d)", mach_error_string(kret), kret); + + kret = task_set_exception_ports(mach_task_self(), EXC_MASK_CRASH | EXC_MASK_CORPSE_NOTIFY, exception_port, + EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, 0); + if (kret != KERN_SUCCESS) + errx(1, "task_set_exception_ports: %s (%d)", mach_error_string(kret), kret); + + dispatch_semaphore_signal(sServerRunning); + + kret = mach_msg_server(mach_exc_server, MACH_MSG_SIZE_RELIABLE, exception_port, 0); + if (kret != KERN_SUCCESS) + errx(1, "mach_msg_server: %s (%d)", mach_error_string(kret), kret); + + return NULL; +} + + +static void childDied(int sig) +{ + struct proc_exitreasoninfo info; + bzero(&info, sizeof(info)); + uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; + bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); + info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; + info.eri_kcd_buf = (user_addr_t)packReasonData; + //fprintf(stderr, "info=%p\n", &info); + if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) { + printf("bad return size from proc_pidinfo()\n"); + return; + } + sChildTerminatedByDyld = (info.eri_namespace == OS_REASON_DYLD); + } + + +int main(int argc, const char* argv[]) +{ + if ( argc < 2 ) { + fprintf(stderr, "usage: nocr [-require_crash] prog args...\n"); + return EXIT_FAILURE; + } + unsigned progArgIndex = 1; + bool requireCrash = false; + const char* testName = NULL; + if ( strcmp(argv[1], "-require_crash") == 0 ) { + progArgIndex = 2; + requireCrash = true; + testName = getenv("NOCR_TEST_NAME"); + if ( testName ) + printf("[BEGIN] %s\n", testName); + } + + signal(SIGCHLD, childDied); + + sServerRunning = dispatch_semaphore_create(0); + + // start up thread for mach server which handles mach exception ports + pthread_t serverThread; + int result = pthread_create(&serverThread, NULL, serverCode, NULL); + if ( result ) + err(EXIT_FAILURE, "pthread_create"); + + // wait until server is up before starting child + dispatch_semaphore_wait(sServerRunning, DISPATCH_TIME_FOREVER); + + // fork and exec child + sChildPid = fork(); + if ( sChildPid < 0 ) + err(EXIT_FAILURE, "fork"); + if ( sChildPid == 0 ) { + // child side + result = execvp(argv[progArgIndex], (char**)&argv[progArgIndex]); + err(EXIT_FAILURE, "exec(\"%s\",...)", argv[progArgIndex]); + } + + // wait for child to finish (including crash) + int status; + int waitResult; + int childResult = EXIT_FAILURE; + do { + waitResult = waitpid(sChildPid, &status, 0); + } while ( (waitResult == -1) && (errno == EINTR) ); + if ( waitResult != -1 ) { + if ( WIFEXITED(status) ) { + childResult = WEXITSTATUS(status); + } + } + + if ( requireCrash ) { + if ( testName ) { + if ( sChildCrashed || sChildTerminatedByDyld ) + printf("[PASS] %s\n", testName); + else + printf("[FAIL] %s\n", testName); + } + return sChildCrashed ? EXIT_SUCCESS : EXIT_FAILURE; + } + else + return childResult; +} + + + + +// Mach exception handler routines needed by execserverServer.c + +kern_return_t +catch_mach_exception_raise(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt) +{ + //fprintf(stderr, "child crashed\n"); + sChildCrashed = true; + return KERN_SUCCESS; +} + +kern_return_t +catch_mach_exception_raise_state(mach_port_t exception_port, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int * flavor, + const thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt) +{ + errx(1, "Unsupported catch_mach_exception_raise_state"); + return KERN_NOT_SUPPORTED; +} + +kern_return_t +catch_mach_exception_raise_state_identity(mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + mach_exception_data_t code, + mach_msg_type_number_t codeCnt, + int * flavor, + thread_state_t old_state, + mach_msg_type_number_t old_stateCnt, + thread_state_t new_state, + mach_msg_type_number_t * new_stateCnt) +{ + errx(1, "Unsupported catch_mach_exception_raise_state_identity"); + return KERN_NOT_SUPPORTED; +} + + + diff --git a/dyld/testing/run_all_dyld_tests.py b/dyld/testing/run_all_dyld_tests.py new file mode 100755 index 0000000..14ebc60 --- /dev/null +++ b/dyld/testing/run_all_dyld_tests.py @@ -0,0 +1,114 @@ +#!/usr/bin/python2.7 + +import plistlib +import string +import sys +import os +import shutil +import subprocess + + + + +# +# Parse dyld's BATS input file and run each test +# +if __name__ == "__main__": + batsPlist = plistlib.readPlist("/AppleInternal/CoreOS/BATS/unit_tests/dyld.plist") + tests = batsPlist["Tests"] + passedCount = 0 + failedCount = 0 + for test in tests: + cwd = test["WorkingDirectory"] + if cwd: + os.chdir(cwd) + cmd = test["Command"] + testName = test["TestName"] + if cwd: + try: + runOutput = subprocess.check_output(cmd,stderr= subprocess.STDOUT) + lines = runOutput.splitlines() + foundBegin = False + passed = False + failed = False + currentTestName = "" + for line in lines: + line = line.lstrip().rstrip() + #print testName + ": " + line + beginIndex = string.find(line, "[BEGIN]") + if beginIndex != -1: + beginName = line[beginIndex + 7:].lstrip() + foundBegin = True + currentTestName = beginName + passIndex = string.find(line, "[PASS]") + if passIndex != -1: + passName = line[passIndex + 6:].lstrip() + passed = True + if passName != currentTestName: + print >> sys.stderr, "[PASS] name does not match [BEGIN] name for test " + testName + failIndex = string.find(line, "[FAIL]") + if failIndex != -1: + failName = line[failIndex + 6:].lstrip() + failed = True + if failName != currentTestName: + print >> sys.stderr, "[FAIL] name does not match [BEGIN] name for test " + testName + if foundBegin: + if not passed and not failed: + print >> sys.stderr, "[BEGIN] found [PASS] or [FAIL] for test " + testName + else: + print >> sys.stderr, "Missing [BEGIN] for test " + testName + if passed: + print "PASSED: " + testName + passedCount = passedCount + 1 + elif failed: + print "FAILED: " + testName + failedCount = failedCount + 1 + except subprocess.CalledProcessError as e: + print >> sys.stderr, "FAILED: " + testName + " (execution failure)" + print "Total PASS count: " + str(passedCount) + print "Total FAIL count: " + str(failedCount) + + +def alt(): + testsTopDir = "/AppleInternal/CoreOS/tests/dyld/" + for f in os.listdir(testsTopDir): + testRunner = testsTopDir + f + "/run.sh" + if os.path.isfile(testRunner): + try: + runOutput = subprocess.check_output([testRunner],stderr= subprocess.STDOUT) + lines = runOutput.splitlines() + foundBegin = False + passed = False + failed = False + currentTestName = "" + for line in lines: + line = line.lstrip().rstrip() + #print f + ": " + line + beginIndex = string.find(line, "[BEGIN]") + if beginIndex != -1: + beginName = line[beginIndex + 7:].lstrip() + foundBegin = True + currentTestName = beginName + passIndex = string.find(line, "[PASS]") + if passIndex != -1: + passName = line[passIndex + 6:].lstrip() + passed = True + if passName != currentTestName: + print >> sys.stderr, "[PASS] name does not match [BEGIN] name for test " + f + failIndex = string.find(line, "[FAIL]") + if failIndex != -1: + failName = line[failIndex + 6:].lstrip() + failed = True + if failName != currentTestName: + print >> sys.stderr, "[FAIL] name does not match [BEGIN] name for test " + f + if foundBegin: + if not passed and not failed: + print >> sys.stderr, "[BEGIN] found [PASS] or [FAIL] for test " + f + else: + print >> sys.stderr, "Missing [BEGIN] for test " + f + if passed: + print "PASSED: " + f + elif failed: + print "FAILED: " + f + except subprocess.CalledProcessError as e: + print >> sys.stderr, "FAILED: " + f + " (execution failure)" diff --git a/dyld/testing/task_for_pid_entitlement.plist b/dyld/testing/task_for_pid_entitlement.plist new file mode 100644 index 0000000..2398d67 --- /dev/null +++ b/dyld/testing/task_for_pid_entitlement.plist @@ -0,0 +1,10 @@ + + + + + com.apple.system-task-ports + + task_for_pid-allow + + + diff --git a/dyld/testing/test-cases/NSAddImage-basic.dtest/main.c b/dyld/testing/test-cases/NSAddImage-basic.dtest/main.c new file mode 100644 index 0000000..5c870c4 --- /dev/null +++ b/dyld/testing/test-cases/NSAddImage-basic.dtest/main.c @@ -0,0 +1,28 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC zzz.c -dynamiclib -o $BUILD_DIR/libzzz.dylib -install_name $RUN_DIR/libzzz.dylib +// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-basic.exe -Wno-deprecated-declarations + +// RUN: ./NSAddImage-basic.exe $RUN_DIR/libzzz.dylib +// RUN: ./NSAddImage-basic.exe libzzz.dylib + + +#include +#include +#include + + +int main(int arg, const char* argv[]) +{ + const char* path = argv[1]; + printf("[BEGIN] NSAddImage-basic %s\n", path); + + const struct mach_header* mh = NSAddImage(path, NSADDIMAGE_OPTION_WITH_SEARCHING); + if ( mh == NULL ) + printf("[FAIL] NSAddImage-basic %s\n", path); + else + printf("[PASS] NSAddImage-basic %s\n", path); + + return 0; +} + diff --git a/dyld/testing/test-cases/NSAddImage-basic.dtest/zzz.c b/dyld/testing/test-cases/NSAddImage-basic.dtest/zzz.c new file mode 100644 index 0000000..c02be2d --- /dev/null +++ b/dyld/testing/test-cases/NSAddImage-basic.dtest/zzz.c @@ -0,0 +1 @@ +void zzz() {} diff --git a/dyld/testing/test-cases/NSAddImage-fail.dtest/main.c b/dyld/testing/test-cases/NSAddImage-fail.dtest/main.c new file mode 100644 index 0000000..74bf245 --- /dev/null +++ b/dyld/testing/test-cases/NSAddImage-fail.dtest/main.c @@ -0,0 +1,35 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-fail.exe -Wno-deprecated-declarations + +// RUN: ./NSAddImage-fail.exe return +// RUN: NOCR_TEST_NAME="NSAddImage-fail expected abort" $REQUIRE_CRASH ./NSAddImage-fail.exe abort + + + +#include +#include +#include +#include + + +int main(int argc, const char* argv[]) +{ + const char* arg = argv[1]; + + if ( strcmp(arg, "return") == 0 ) { + printf("[BEGIN] NSAddImage-fail %s\n", arg); + const struct mach_header* mh = NSAddImage("/xqz/42/libnotfound.xxx", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); + if ( mh == NULL ) + printf("[PASS] NSAddImage-fail %s\n", arg); + else + printf("[FAIL] NSAddImage-fail %s\n", arg); + } + else { + // run with nocr which print BEGIN/PASS/FAIL + NSAddImage("/xqz/42/libnotfound.xxx", 0); + } + + return 0; +} + diff --git a/dyld/testing/test-cases/NSAddImage-loaded.dtest/main.c b/dyld/testing/test-cases/NSAddImage-loaded.dtest/main.c new file mode 100644 index 0000000..db7ca4b --- /dev/null +++ b/dyld/testing/test-cases/NSAddImage-loaded.dtest/main.c @@ -0,0 +1,33 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/NSAddImage-loaded.exe -Wno-deprecated-declarations + +// RUN: ./NSAddImage-loaded.exe return + + + +#include +#include +#include +#include + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] NSAddImage-loaded\n"); + + // verify value is returned for image already loaded + const struct mach_header* mh = NSAddImage("/usr/lib/libSystem.B.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); + if ( mh == NULL ) + printf("[FAIL] NSAddImage-loaded\n"); + + // verify existing dylib is not loaded if it is not already loaded + mh = NSAddImage("/usr/lib/libz.dylib", NSADDIMAGE_OPTION_RETURN_ONLY_IF_LOADED); + if ( mh != NULL ) + printf("[FAIL] NSAddImage-loaded\n"); + + printf("[PASS] NSAddImage-loaded\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c b/dyld/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c new file mode 100644 index 0000000..ddd9c74 --- /dev/null +++ b/dyld/testing/test-cases/NSAddressOfSymbol-basic.dtest/main.c @@ -0,0 +1,40 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/NSAddressOfSymbol-basic.exe -Wno-deprecated-declarations + +// RUN: ./NSAddressOfSymbol-basic.exe + + + +#include +#include +#include +#include + +extern struct mach_header __dso_handle; + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] NSAddressOfSymbol-basic\n"); + + NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + if ( sym == NULL ) { + printf("[FAIL] NSAddressOfSymbol-basic can't find main\n"); + return 0; + } + void* mainAddr = NSAddressOfSymbol(sym); + if ( mainAddr != &main ) { + printf("[FAIL] NSAddressOfSymbol-basic address returned %p is not &main=%p\n", mainAddr, &main); + return 0; + } + + // verify NULL works + if ( NSAddressOfSymbol(NULL) != NULL ) { + printf("[FAIL] NSAddressOfSymbol-basic NULL not handle\n"); + return 0; + } + + printf("[PASS] NSAddressOfSymbol-basic\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/foo.c b/dyld/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/foo.c new file mode 100644 index 0000000..837ec00 --- /dev/null +++ b/dyld/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/foo.c @@ -0,0 +1,7 @@ + +#include +#include + +void fooInBundle() +{ +} diff --git a/dyld/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c b/dyld/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c new file mode 100644 index 0000000..49341b3 --- /dev/null +++ b/dyld/testing/test-cases/NSCreateObjectFileImageFromFile-basic.dtest/main.c @@ -0,0 +1,70 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromFile-basic.exe -Wno-deprecated-declarations +// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle + +// RUN: ./NSCreateObjectFileImageFromFile-basic.exe $RUN_DIR/foo.bundle + + +#include +#include +#include +#include + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] NSCreateObjectFileImageFromFile-basic\n"); + + const char* path = argv[1]; + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { + printf("[FAIL] NSCreateObjectFileImageFromFile failed\n"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + printf("[FAIL] NSLinkModule failed\n"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); + if ( sym == NULL ) { + printf("[FAIL] NSLookupSymbolInModule failed\n"); + return 0; + } + + void* func = NSAddressOfSymbol(sym); + if ( func == NULL ) { + printf("[FAIL] NSAddressOfSymbol failed\n"); + return 0; + } + + Dl_info info; + if ( dladdr(func, &info) == 0 ) { + printf("[FAIL] dladdr(&p, xx) failed"); + return 0; + } + //printf("_fooInBundle found in %s\n", info.dli_fname); + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + printf("[FAIL] NSUnLinkModule failed\n"); + return 0; + } + + if ( dladdr(func, &info) != 0 ) { + printf("[FAIL] dladdr(&p, xx) found but should not have\n"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + printf("[FAIL] NSDestroyObjectFileImage failed\n"); + return 0; + } + + printf("[PASS] NSCreateObjectFileImageFromFile-basic\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/foo.c b/dyld/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/foo.c new file mode 100644 index 0000000..837ec00 --- /dev/null +++ b/dyld/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/foo.c @@ -0,0 +1,7 @@ + +#include +#include + +void fooInBundle() +{ +} diff --git a/dyld/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c b/dyld/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c new file mode 100644 index 0000000..888c043 --- /dev/null +++ b/dyld/testing/test-cases/NSCreateObjectFileImageFromMemory-basic.dtest/main.c @@ -0,0 +1,115 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/NSCreateObjectFileImageFromMemory-basic.exe -Wno-deprecated-declarations +// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle + +// RUN: ./NSCreateObjectFileImageFromMemory-basic.exe $RUN_DIR/foo.bundle + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static void checkBundle(const char* path, bool unlinkBeforeDestroy) +{ + int fd = open(path, O_RDONLY, 0); + if ( fd == -1 ) { + printf("[FAIL] open(%s) failed", path); + exit(0); + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + printf("[FAIL] fstat() failed\n"); + exit(0); + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + printf("[FAIL] mmap() failed\n"); + exit(0); + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n"); + exit(0); + } + + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + printf("[FAIL] NSLinkModule failed\n"); + exit(0); + } + + if ( !unlinkBeforeDestroy ) { + // API lets you destroy ofi and NSModule lives on + if ( !NSDestroyObjectFileImage(ofi) ) { + printf("[FAIL] NSDestroyObjectFileImage failed\n"); + exit(0); + } + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); + if ( sym == NULL ) { + printf("[FAIL] NSLookupSymbolInModule failed\n"); + exit(0); + } + + void* func = NSAddressOfSymbol(sym); + if ( func == NULL ) { + printf("[FAIL] NSAddressOfSymbol failed\n"); + exit(0); + } + + Dl_info info; + if ( dladdr(func, &info) == 0 ) { + printf("[FAIL] dladdr(&p, xx) failed\n"); + exit(0); + } + //printf("_fooInBundle found in %s\n", info.dli_fname); + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + printf("[FAIL] NSUnLinkModule failed\n"); + exit(0); + } + + if ( dladdr(func, &info) != 0 ) { + printf("[FAIL] dladdr(&p, xx) found but should not have\n"); + exit(0); + } + + if ( unlinkBeforeDestroy ) { + if ( !NSDestroyObjectFileImage(ofi) ) { + printf("[FAIL] NSDestroyObjectFileImage failed\n"); + exit(0); + } + } +} + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] NSCreateObjectFileImageFromMemory-basic\n"); + + checkBundle(argv[1], true); + checkBundle(argv[1], false); + + // Now go again enough times to flush out any limits in our dlopen encodings. + for (unsigned i = 0; i != 255; ++i) + checkBundle(argv[1], false); + + printf("[PASS] NSCreateObjectFileImageFromMemory-basic\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c b/dyld/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c new file mode 100644 index 0000000..1adab58 --- /dev/null +++ b/dyld/testing/test-cases/NSLookupSymbolInImage-basic.dtest/main.c @@ -0,0 +1,39 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/NSLookupSymbolInImage-basic.exe -Wno-deprecated-declarations + +// RUN: ./NSLookupSymbolInImage-basic.exe + + +#include +#include +#include +#include + +extern struct mach_header __dso_handle; + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] NSLookupSymbolInImage-basic\n"); + + // verify known symbol works + NSSymbol sym = NSLookupSymbolInImage(&__dso_handle, "_main", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + if ( sym == NULL ) { + printf("[FAIL] NSLookupSymbolInImage-basic _main\n"); + return 0; + } + + // verify mode where NSLookupSymbolInImage() returns NULL if symbol not found + sym = NSLookupSymbolInImage(&__dso_handle, "_42hhg", NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + if ( sym != NULL ) { + printf("[FAIL] NSLookupSymbolInImage-basic _42hhg\n"); + return 0; + } + + // Note: NSLookupSymbolInImage is documented to abort if symbol not found and NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR not used, + // but dyld 2 just returned NULL, so no need to test that. + + printf("[PASS] NSLookupSymbolInImage-basic\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/bar.c b/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/bar.c new file mode 100644 index 0000000..70b4d1b --- /dev/null +++ b/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/bar.c @@ -0,0 +1,4 @@ +const char* bar() +{ + return "bar"; +} diff --git a/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/foo.c b/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/foo.c new file mode 100644 index 0000000..a5db092 --- /dev/null +++ b/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/foo.c @@ -0,0 +1,4 @@ +const char* foo() +{ + return "foo"; +} diff --git a/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c b/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c new file mode 100644 index 0000000..a58b85b --- /dev/null +++ b/dyld/testing/test-cases/_dyld_is_memory_immutable.dtest/main.c @@ -0,0 +1,79 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dyld_immutable_test.exe + +// RUN: ./dyld_immutable_test.exe + +#include +#include +#include +#include +#include + +typedef const char* (*BarProc)(void); + +extern uint32_t _cpu_capabilities; +extern const char* foo(); + +const char* myStr = "myStr"; + +int myInt; + + +int main() +{ + printf("[BEGIN] _dyld_is_memory_immutable\n"); + + if ( !_dyld_is_memory_immutable(myStr, 6) ) { + printf("[FAIL] _dyld_is_memory_immutable() returned false for string in main executable\n"); + return 0; + } + + if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) { + printf("[FAIL] _dyld_is_memory_immutable() returned true for result from strdup()\n"); + return 0; + } + + if ( _dyld_is_memory_immutable(&myInt, 4) ) { + printf("[FAIL] _dyld_is_memory_immutable() returned true for global variabe in main executable\n"); + return 0; + } + + if ( !_dyld_is_memory_immutable(foo(), 4) ) { + printf("[FAIL] _dyld_is_memory_immutable() returned false for string in statically linked dylib\n"); + return 0; + } + + if ( !_dyld_is_memory_immutable(&strcpy, 4) ) { + printf("[FAIL] _dyld_is_memory_immutable() returned false for function in dyld shared cache\n"); + return 0; + } + + if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) { + printf("[FAIL] _dyld_is_memory_immutable() returned true for global variable in shared cache\n"); + return 0; + } + + void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_FIRST); + if ( handle == NULL ) { + printf("[FAIL] dlopen(libbar.dylib) failed"); + return 0; + } + + BarProc proc = dlsym(handle, "bar"); + if ( proc == NULL ) { + printf("[FAIL] dlsym(bar) failed\n"); + return 0; + } + const char* barStr = (*proc)(); + if ( _dyld_is_memory_immutable(barStr, 4) ) { + printf("[FAIL] _dyld_is_memory_immutable() returned true for string in unloadable dylib\n"); + return 0; + } + + + printf("[PASS] _dyld_is_memory_immutable\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/_dyld_register_func_for_add_image.dtest/foo.c b/dyld/testing/test-cases/_dyld_register_func_for_add_image.dtest/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/dyld/testing/test-cases/_dyld_register_func_for_add_image.dtest/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/dyld/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx b/dyld/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx new file mode 100644 index 0000000..905a153 --- /dev/null +++ b/dyld/testing/test-cases/_dyld_register_func_for_add_image.dtest/main.cxx @@ -0,0 +1,77 @@ + +// BUILD: $CXX main.cxx -o $BUILD_DIR/dyld_register_test.exe -DRUN_DIR="$RUN_DIR" +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib + +// RUN: ./dyld_register_test.exe + +#include +#include +#include +#include +#include + +#include + +extern mach_header __dso_handle; + +static std::unordered_set sCurrentImages; + +static void notify(const mach_header* mh, intptr_t vmaddr_slide) +{ + //fprintf(stderr, "mh=%p\n", mh); + if ( sCurrentImages.count(mh) != 0 ) { + printf("[FAIL] _dyld_register_func_for_add_image: notified twice about %p\n", mh); + exit(0); + } + sCurrentImages.insert(mh); +} + + +int main() +{ + printf("[BEGIN] _dyld_register_func_for_add_image\n"); + + _dyld_register_func_for_add_image(¬ify); + + // verify we were notified about already loaded images + if ( sCurrentImages.count(&__dso_handle) == 0 ) { + printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about main executable"); + exit(0); + } + const mach_header* libSysMH = dyld_image_header_containing_address((void*)&printf); + if ( sCurrentImages.count(libSysMH) == 0 ) { + printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libsystem_c.dylib"); + exit(0); + } + + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST); + if ( handle == NULL ) { + printf("[FAIL] dlopen(\"%s\") failed with: %s", RUN_DIR "/libfoo.dylib", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + printf("[FAIL] dlsym(handle, \"foo\") failed"); + exit(0); + } + + // verify we were notified about load of libfoo.dylib + const mach_header* libfooMH = dyld_image_header_containing_address(sym); + if ( sCurrentImages.count(libfooMH) == 0 ) { + printf("[FAIL] _dyld_register_func_for_add_image() did not notify us about libfoo.dylib"); + exit(0); + } + + + int result = dlclose(handle); + if ( result != 0 ) { + printf("[FAIL] dlclose(handle) returned %d", result); + exit(0); + } + + + printf("[PASS] _dyld_register_func_for_add_image\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/crt-vars-libSystem.dtest/main.c b/dyld/testing/test-cases/crt-vars-libSystem.dtest/main.c new file mode 100644 index 0000000..6eb9381 --- /dev/null +++ b/dyld/testing/test-cases/crt-vars-libSystem.dtest/main.c @@ -0,0 +1,100 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/crt-vars-libSystem.exe + +// RUN: ./crt-vars-libSystem.exe + +#include +#include +#include +#include +#include + +// This struct is passed as fifth parameter to libSystem.dylib's initializer so it record +// the address of crt global variables. +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + char*** NXArgvPtr; + char*** environPtr; + char** __prognamePtr; +}; + + +// global variables defeined in crt1.o +extern char** NXArgv; +extern int NXArgc; +extern char** environ; +extern char* __progname; + + +static const struct ProgramVars* sVars; + +void __attribute__((constructor)) +myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) +{ + sVars = vars; +} + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] crt-vars-libSystem\n"); + bool success = true; + + if ( _NSGetArgv() != &NXArgv ) { + printf("[FAIL] crt-libSystem: _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); + success = false; + } + + if ( _NSGetArgc() != &NXArgc ) { + printf("[FAIL] crt-libSystem: _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); + success = false; + } + + if ( _NSGetEnviron() != &environ ) { + printf("[FAIL] crt-libSystem: _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); + success = false; + } + + if ( _NSGetProgname() != &__progname ) { + printf("[FAIL] crt-libSystem: _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); + success = false; + } + + if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) { + printf("[FAIL] crt-libSystem: _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); + success = false; + } + + if ( sVars->NXArgvPtr != &NXArgv ) { + printf("[FAIL] crt-libSystem: sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); + success = false; + } + + if ( sVars->NXArgcPtr != &NXArgc ) { + printf("[FAIL] crt-libSystem: sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); + success = false; + } + + if ( sVars->environPtr != &environ ) { + printf("[FAIL] crt-libSystem: sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); + success = false; + } + + if ( sVars->__prognamePtr != &__progname ) { + printf("[FAIL] crt-libSystem: sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); + success = false; + } + + if ( sVars->mh != &_mh_execute_header ) { + printf("[FAIL] crt-libSystem: sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); + success = false; + } + + if ( success ) + printf("[PASS] crt-vars-libSystem\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dladdr-basic.dtest/main-no-syms.c b/dyld/testing/test-cases/dladdr-basic.dtest/main-no-syms.c new file mode 100644 index 0000000..672595d --- /dev/null +++ b/dyld/testing/test-cases/dladdr-basic.dtest/main-no-syms.c @@ -0,0 +1,38 @@ + +// BUILD: $CC main-no-syms.c -o $BUILD_DIR/dladdr-stripped.exe +// BUILD: strip $BUILD_DIR/dladdr-stripped.exe + +// RUN: ./dladdr-stripped.exe + + +#include +#include +#include +#include +#include + + + +/// +/// verify dladdr() returns NULL for a symbol name in a fully stripped +/// main executable (and not _mh_execute_header+nnn). +/// + +int main() +{ + printf("[BEGIN] dladdr-stripped\n"); + + Dl_info info; + if ( dladdr(&main, &info) == 0 ) { + printf("[FAIL] dladdr(&main, xx) failed\n"); + return 0; + } + + if ( info.dli_sname != NULL ){ + printf("[FAIL] dladdr() returned: \"%s\" instead of NULL\n", info.dli_sname); + return 0; + } + + printf("[PASS] dladdr-stripped\n"); + return 0; +} diff --git a/dyld/testing/test-cases/dladdr-basic.dtest/main.c b/dyld/testing/test-cases/dladdr-basic.dtest/main.c new file mode 100644 index 0000000..eeeabca --- /dev/null +++ b/dyld/testing/test-cases/dladdr-basic.dtest/main.c @@ -0,0 +1,129 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dladdr-basic.exe + +// RUN: ./dladdr-basic.exe + +#include +#include +#include +#include +#include + + +int bar() +{ + return 2; +} + +static int foo() +{ + return 3; +} + +__attribute__((visibility("hidden"))) int hide() +{ + return 4; +} + +// checks global symbol +static void verifybar() +{ + Dl_info info; + if ( dladdr(&bar, &info) == 0 ) { + printf("[FAIL] dladdr(&bar, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "bar") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &bar) { + printf("[FAIL] dladdr()->dli_saddr is not &bar\n"); + exit(0); + } + if ( info.dli_fbase != dyld_image_header_containing_address(&bar) ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n"); + exit(0); + } +} + +// checks local symbol +static void verifyfoo() +{ + Dl_info info; + if ( dladdr(&foo, &info) == 0 ) { + printf("[FAIL] dladdr(&foo, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "foo") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &foo) { + printf("[FAIL] dladdr()->dli_saddr is not &foo\n"); + exit(0); + } + if ( info.dli_fbase != dyld_image_header_containing_address(&foo) ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n"); + exit(0); + } +} + +// checks hidden symbol +static void verifyhide() +{ + Dl_info info; + if ( dladdr(&hide, &info) == 0 ) { + printf("[FAIL] dladdr(&hide, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "hide") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &hide) { + printf("[FAIL] dladdr()->dli_saddr is not &hide\n"); + exit(0); + } + if ( info.dli_fbase != dyld_image_header_containing_address(&hide) ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n"); + exit(0); + } +} + +// checks dylib symbol +static void verifymalloc() +{ + Dl_info info; + if ( dladdr(&malloc, &info) == 0 ) { + printf("[FAIL] dladdr(&malloc, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "malloc") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &malloc) { + printf("[FAIL] dladdr()->dli_saddr is not &malloc\n"); + exit(0); + } + if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n"); + exit(0); + } +} + + +int main() +{ + printf("[BEGIN] dladdr-basic\n"); + verifybar(); + verifyhide(); + verifyfoo(); + verifymalloc(); + + + printf("[PASS] dladdr-basic\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dladdr-dylib.dtest/foo.c b/dyld/testing/test-cases/dladdr-dylib.dtest/foo.c new file mode 100644 index 0000000..e45b27f --- /dev/null +++ b/dyld/testing/test-cases/dladdr-dylib.dtest/foo.c @@ -0,0 +1,99 @@ + + +#include +#include +#include +#include +#include + +extern void* __dso_handle; + +int dylib_bar() +{ + return 2; +} + +static int dylib_foo() +{ + return 3; +} + +__attribute__((visibility("hidden"))) int dylib_hide() +{ + return 4; +} + +// checks global symbol +static void verifybar() +{ + Dl_info info; + if ( dladdr(&dylib_bar, &info) == 0 ) { + printf("[FAIL] dladdr(&dylib_bar, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "dylib_bar") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_bar\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &dylib_bar) { + printf("[FAIL] dladdr()->dli_saddr is not &dylib_bar\n"); + exit(0); + } + if ( info.dli_fbase != &__dso_handle ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_bar\n"); + exit(0); + } +} + +// checks local symbol +static void verifyfoo() +{ + Dl_info info; + if ( dladdr(&dylib_foo, &info) == 0 ) { + printf("[FAIL] dladdr(&dylib_foo, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "dylib_foo") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_foo\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &dylib_foo) { + printf("[FAIL] dladdr()->dli_saddr is not &dylib_foo\n"); + exit(0); + } + if ( info.dli_fbase != &__dso_handle ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_foo\n"); + exit(0); + } +} + +// checks hidden symbol +static void verifyhide() +{ + Dl_info info; + if ( dladdr(&dylib_hide, &info) == 0 ) { + printf("[FAIL] dladdr(&dylib_hide, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "dylib_hide") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"dylib_hide\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &dylib_hide) { + printf("[FAIL] dladdr()->dli_saddr is not &dylib_hide\n"); + exit(0); + } + if ( info.dli_fbase != &__dso_handle ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &dylib_hide\n"); + exit(0); + } +} + + +void verifyDylib() +{ + verifybar(); + verifyfoo(); + verifyhide(); +} + diff --git a/dyld/testing/test-cases/dladdr-dylib.dtest/main.c b/dyld/testing/test-cases/dladdr-dylib.dtest/main.c new file mode 100644 index 0000000..5d9d0c9 --- /dev/null +++ b/dyld/testing/test-cases/dladdr-dylib.dtest/main.c @@ -0,0 +1,134 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dladdr-basic.exe + +// RUN: ./dladdr-basic.exe + +#include +#include +#include +#include +#include + +extern void* __dso_handle; + +extern void verifyDylib(); + +int bar() +{ + return 2; +} + +static int foo() +{ + return 3; +} + +__attribute__((visibility("hidden"))) int hide() +{ + return 4; +} + +// checks global symbol +static void verifybar() +{ + Dl_info info; + if ( dladdr(&bar, &info) == 0 ) { + printf("[FAIL] dladdr(&bar, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "bar") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"bar\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &bar) { + printf("[FAIL] dladdr()->dli_saddr is not &bar\n"); + exit(0); + } + if ( info.dli_fbase != &__dso_handle ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &bar\n"); + exit(0); + } +} + +// checks local symbol +static void verifyfoo() +{ + Dl_info info; + if ( dladdr(&foo, &info) == 0 ) { + printf("[FAIL] dladdr(&foo, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "foo") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"foo\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &foo) { + printf("[FAIL] dladdr()->dli_saddr is not &foo\n"); + exit(0); + } + if ( info.dli_fbase != &__dso_handle ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &foo\n"); + exit(0); + } +} + +// checks hidden symbol +static void verifyhide() +{ + Dl_info info; + if ( dladdr(&hide, &info) == 0 ) { + printf("[FAIL] dladdr(&hide, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "hide") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"hide\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &hide) { + printf("[FAIL] dladdr()->dli_saddr is not &hide\n"); + exit(0); + } + if ( info.dli_fbase != &__dso_handle ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &hide\n"); + exit(0); + } +} + +// checks dylib symbol +static void verifymalloc() +{ + Dl_info info; + if ( dladdr(&malloc, &info) == 0 ) { + printf("[FAIL] dladdr(&malloc, xx) failed\n"); + exit(0); + } + if ( strcmp(info.dli_sname, "malloc") != 0 ) { + printf("[FAIL] dladdr()->dli_sname is \"%s\" instead of \"malloc\"\n", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &malloc) { + printf("[FAIL] dladdr()->dli_saddr is not &malloc\n"); + exit(0); + } + if ( info.dli_fbase != dyld_image_header_containing_address(&malloc) ) { + printf("[FAIL] dladdr()->dli_fbase is not image that contains &malloc\n"); + exit(0); + } +} + + +int main() +{ + printf("[BEGIN] dladdr-basic\n"); + verifybar(); + verifyhide(); + verifyfoo(); + verifymalloc(); + + verifyDylib(); + + printf("[PASS] dladdr-basic\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/bar.c b/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/bar.c new file mode 100644 index 0000000..39ac49e --- /dev/null +++ b/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/bar.c @@ -0,0 +1,5 @@ +int bar() +{ + return VALUE; +} + diff --git a/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/foo.c b/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/foo.c new file mode 100644 index 0000000..958e1af --- /dev/null +++ b/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/foo.c @@ -0,0 +1,7 @@ +extern int bar(); + +int foo() +{ + return bar() + VALUE; +} + diff --git a/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c b/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c new file mode 100644 index 0000000..96eb08b --- /dev/null +++ b/dyld/testing/test-cases/dlopen-DYLD_LIBRARY_PATH.dtest/main.c @@ -0,0 +1,84 @@ + +// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2 +// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door1/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=3 +// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/door2/libbar.dylib -install_name $RUN_DIR/libbar.dylib -DVALUE=17 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=10 $BUILD_DIR/door1/libbar.dylib +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=25 $BUILD_DIR/door2/libbar.dylib +// BUILD: $CC main.c -o $BUILD_DIR/main.exe +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe + +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door1/ ./main.exe 13 +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door2 ./main.exe 42 +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door3/:$RUN_DIR/door2/ ./main.exe 42 + +#include +#include +#include + +// Program dlopen()s libfoo.dylib which was linked against libbar.dylib +// Neither have valid paths and must be found via DYLD_LIBRARY_PATH +// This test direct and indirect loading. + +int main(int argc, const char* argv[]) +{ + const char* env = getenv("DYLD_LIBRARY_PATH"); + if ( env == NULL ) { + printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n"); + printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, env not set\n"); + return 0; + } + const char* valueStr = argv[1]; + if ( valueStr == NULL ) { + printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH\n"); + printf("[FAIL] dlopen-DYLD_LIBRARY_PATH, arg1 value not set\n"); + return 0; + } + char* end; + long value = strtol(valueStr, &end, 0); + + printf("[BEGIN] dlopen-DYLD_LIBRARY_PATH %s\n", env); + + void* handle = dlopen("/bogus/libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); + return 0; + } + + typedef int (*FooProc)(); + + FooProc sym = (FooProc)dlsym(handle, "foo"); + if ( sym == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); + return 0; + } + + int result = (*sym)(); + if ( result != value ) { + printf("result=%d, expected %ld (str=%s)\n", result, value, valueStr); + printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); + return 0; + } + + int r = dlclose(handle); + if ( r != 0 ) { + printf("dlclose() returned %d\n", r); + printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); + return 0; + } + + void* handle2 = dlopen("/junk/libfoo.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-DYLD_LIBRARY_PATH %s\n", env); + return 0; + } + + + + printf("[PASS] dlopen-DYLD_LIBRARY_PATH %s\n", env); + + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/foo.c b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/foo.c new file mode 100644 index 0000000..13457f2 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 10; +} + diff --git a/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-a.c b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-a.c new file mode 100644 index 0000000..237f51b --- /dev/null +++ b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-a.c @@ -0,0 +1,22 @@ + + +#include + +extern bool inInitB; +extern bool doneInitB; + +bool initsInWrongOrder = false; +bool doneInitA = false; + +__attribute__((constructor)) +void initA() +{ + if ( inInitB ) + initsInWrongOrder = true; + doneInitA = true; +} + +bool allInitsDone() +{ + return doneInitA && doneInitB; +} diff --git a/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c new file mode 100644 index 0000000..5ade0fb --- /dev/null +++ b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-b.c @@ -0,0 +1,25 @@ + +#include +#include +#include +#include + +bool doneInitB = false; +bool inInitB = false; + + +__attribute__((constructor)) +void initB() +{ + inInitB = true; + + // "upward" link to libInitA.dylib + void* handle = dlopen("libInitA.dylib", RTLD_NOLOAD); + if ( handle == NULL ) { + printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: dlopen(libInitA.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror()); + return; + } + inInitB = false; + + doneInitB = true; +} diff --git a/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c new file mode 100644 index 0000000..6218689 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/init-main.c @@ -0,0 +1,33 @@ + +// BUILD: $CC init-b.c -dynamiclib -install_name $RUN_DIR/libInitB.dylib -o $BUILD_DIR/libInitB.dylib +// BUILD: $CC init-a.c -dynamiclib -install_name $RUN_DIR/libInitA.dylib $BUILD_DIR/libInitB.dylib -o $BUILD_DIR/libInitA.dylib +// BUILD: $CC init-main.c $BUILD_DIR/libInitA.dylib -o $BUILD_DIR/dlopen-RTLD_NOLOAD-in-initializer.exe + +// RUN: ./dlopen-RTLD_NOLOAD-in-initializer.exe + +#include +#include +#include +#include +#include + + +extern bool initsInWrongOrder; +extern bool allInitsDone(); + +int main() +{ + printf("[BEGIN] dlopen-RTLD_NOLOAD-in-initializer\n"); + + /// + /// This tests that using RTLD_NOLOAD in an initializer does not trigger out of order initializers + /// + if ( initsInWrongOrder ) + printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: wrong init order\n"); + else if ( !allInitsDone() ) + printf("[FAIL] dlopen-RTLD_NOLOAD-in-initializer: all initializers not run\n"); + else + printf("[PASS] dlopen-RTLD_NOLOAD-in-initializer\n"); + + return 0; +} diff --git a/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c new file mode 100644 index 0000000..56aa42c --- /dev/null +++ b/dyld/testing/test-cases/dlopen-RTLD_NOLOAD.dtest/main.c @@ -0,0 +1,53 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dlopen-RTLD_NOLOAD-basic.exe +// BUILD: cd $BUILD_DIR && ln -s libfoo.dylib libfoo-sym.dylib + +// RUN: ./dlopen-RTLD_NOLOAD-basic.exe + +#include +#include +#include +#include + + +int main() +{ + printf("[BEGIN] dlopen-RTLD_NOLOAD-basic\n"); + + /// + /// This tests that RTLD_NOLOAD finds existing dylib statically linked + /// + void* handle = dlopen("libfoo.dylib", RTLD_NOLOAD); + if ( handle == NULL ) { + printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror()); + return 0; + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlsym(handle, \"foo\") failed but it should have worked: %s\n", dlerror()); + return 0; + } + + /// + /// This tests that RTLD_NOLOAD verifies that non-existant dylib returns NULL + /// + void* handle2 = dlopen("libfobbulate.dylib", RTLD_NOLOAD); + if ( handle2 != NULL ) { + printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfobbulate.dylib, RTLD_NOLOAD) succeeded but it should have failed\n"); + return 0; + } + + + /// + /// This tests that RTLD_NOLOAD finds symlink to existing dylib + /// + void* handle3 = dlopen("libfoo-sym.dylib", RTLD_NOLOAD); + if ( handle3 == NULL ) { + printf("[FAIL] dlopen-RTLD_NOLOAD-basic: dlopen(libfoo-sym.dylib, RTLD_NOLOAD) failed but it should have worked: %s\n", dlerror()); + return 0; + } + + printf("[PASS] dlopen-RTLD_NOLOAD-basic\n"); + return 0; +} diff --git a/dyld/testing/test-cases/dlopen-bad-file.dtest/bad.txt b/dyld/testing/test-cases/dlopen-bad-file.dtest/bad.txt new file mode 100644 index 0000000..43b3565 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-bad-file.dtest/bad.txt @@ -0,0 +1 @@ +bad file diff --git a/dyld/testing/test-cases/dlopen-bad-file.dtest/main.c b/dyld/testing/test-cases/dlopen-bad-file.dtest/main.c new file mode 100644 index 0000000..63108fd --- /dev/null +++ b/dyld/testing/test-cases/dlopen-bad-file.dtest/main.c @@ -0,0 +1,47 @@ + +// BUILD: cp bad.txt $BUILD_DIR/libnota.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-bad-file.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlopen-bad-file.exe + +#include +#include +#include + + + +int main() +{ + printf("[BEGIN] dlopen-bad-file\n"); + + // try to dlopen() a text file + void* handle = dlopen(RUN_DIR "/libnota.dylib", RTLD_FIRST); + if ( handle != NULL ) { + printf("[FAIL] dlopen-bad-file should have failed on non-mach-o file %s\n", RUN_DIR "/libnota.dylib"); + return 0; + } + const char* message = dlerror(); + if ( (strstr(message, "mach-o") == NULL) && (strstr(message, "too short") == NULL) ) { + printf("dlerror: %s\n", message); + printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'mach-o'\n"); + return 0; + } + + // try to dlopen() a directory + handle = dlopen(RUN_DIR, RTLD_FIRST); + if ( handle != NULL ) { + printf("[FAIL] dlopen-bad-file should have failed on dir %s\n", RUN_DIR); + return 0; + } + message = dlerror(); + if ( strstr(message, "not a file") == NULL ) { + printf("dlerror: %s\n", message); + printf("[FAIL] dlopen-bad-file dlerror() message did not contain 'not a file'\n"); + return 0; + } + + printf("[PASS] dlopen-bad-file\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-basic.dtest/foo.c b/dyld/testing/test-cases/dlopen-basic.dtest/foo.c new file mode 100644 index 0000000..c8e9924 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-basic.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 10; +} + diff --git a/dyld/testing/test-cases/dlopen-basic.dtest/main.c b/dyld/testing/test-cases/dlopen-basic.dtest/main.c new file mode 100644 index 0000000..e506236 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-basic.dtest/main.c @@ -0,0 +1,48 @@ + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/test.dylib +// BUILD: $CC foo.c -bundle -o $BUILD_DIR/test.bundle +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-basic.exe + +// RUN: ./dlopen-basic.exe + +#include +#include + + +static void tryImage(const char* path) +{ + printf("[BEGIN] dlopen-basic %s\n", path); + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-basic %s\n", path); + return; + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-basic %s\n", path); + return; + } + + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-basic %s\n", path); + return; + } + + printf("[PASS] dlopen-basic %s\n", path); +} + + + +int main() +{ + tryImage("test.bundle"); + tryImage("test.dylib"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-empty-data.dtest/foo.c b/dyld/testing/test-cases/dlopen-empty-data.dtest/foo.c new file mode 100644 index 0000000..fd55770 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-empty-data.dtest/foo.c @@ -0,0 +1,8 @@ + +int dummy; + +int foo() +{ + return 10; +} + diff --git a/dyld/testing/test-cases/dlopen-empty-data.dtest/main.c b/dyld/testing/test-cases/dlopen-empty-data.dtest/main.c new file mode 100644 index 0000000..9bf8051 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-empty-data.dtest/main.c @@ -0,0 +1,27 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlopen-empty-data.exe -DRUN_DIR="$RUN_DIR" + + +// RUN: ./dlopen-empty-data.exe + +#include +#include + +// libfoo-static.dylib and libfoo-dynamic.dylib each have an empty (no disk size) DATA segment + +int main() +{ + printf("[BEGIN] dlopen-empty-data\n"); + + void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlopen-empty-data: libfoo-dynamic.dylib could not be loaded: %s\n", dlerror()); + return 0; + } + + printf("[PASS] dlopen-empty-data\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-flat.dtest/bar.c b/dyld/testing/test-cases/dlopen-flat.dtest/bar.c new file mode 100644 index 0000000..0f338d1 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-flat.dtest/bar.c @@ -0,0 +1,15 @@ + +extern int foo(); + +extern int gInitialisersCalled; + +__attribute__((constructor)) +static void onLoad() { + ++gInitialisersCalled; +} + +typedef int(*retTy)(); + +retTy bar() { + return &foo; +} \ No newline at end of file diff --git a/dyld/testing/test-cases/dlopen-flat.dtest/foo.c b/dyld/testing/test-cases/dlopen-flat.dtest/foo.c new file mode 100644 index 0000000..2414437 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-flat.dtest/foo.c @@ -0,0 +1,13 @@ + + +extern int gInitialisersCalled; + +__attribute__((constructor)) +static void onLoad() { + ++gInitialisersCalled; +} + + +int foo() { + return 0; +} \ No newline at end of file diff --git a/dyld/testing/test-cases/dlopen-flat.dtest/main.c b/dyld/testing/test-cases/dlopen-flat.dtest/main.c new file mode 100644 index 0000000..194b6af --- /dev/null +++ b/dyld/testing/test-cases/dlopen-flat.dtest/main.c @@ -0,0 +1,96 @@ + +// BUILD: $CC foo.c -dynamiclib -Wl,-U,_gInitialisersCalled -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC bar.c -dynamiclib -Wl,-U,_gInitialisersCalled $BUILD_DIR/libfoo.dylib -flat_namespace -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/dlopen-flat.exe + +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR ./dlopen-flat.exe + +#include +#include + +int gInitialisersCalled = 0; + +int main() { + printf("[BEGIN] dlopen-flat\n"); + int result; + + // Foo exports foo() + void* fooHandle = 0; + { + const char* path = RUN_DIR "/libfoo.dylib"; + fooHandle = dlopen(path, RTLD_LAZY); + if (!fooHandle) { + printf("dlopen failed with error: %s\n", dlerror()); + return 1; + } + if (gInitialisersCalled != 1) { + printf("gInitialisersCalled != 1\n"); + printf("[FAIL] dlopen-flat\n"); + return 1; + } + } + // Now unload foo which should do something. + result = dlclose(fooHandle); + if (result != 0) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-flat\n"); + return 1; + } + + // Open foo again which should do something. + { + const char* path = RUN_DIR "/libfoo.dylib"; + fooHandle = dlopen(path, RTLD_LAZY); + if (!fooHandle) { + printf("dlopen failed with error: %s\n", dlerror()); + return 1; + } + if (gInitialisersCalled != 2) { + printf("gInitialisersCalled != 2\n"); + printf("[FAIL] dlopen-flat\n"); + return 1; + } + } + + // Bar is going to resolve foo() + void* barHandle = 0; + { + const char* path = RUN_DIR "/libbar.dylib"; + barHandle = dlopen(path, RTLD_LAZY); + if (!barHandle) { + printf("dlopen failed with error: %s\n", dlerror()); + return 1; + } + if (gInitialisersCalled != 3) { + printf("gInitialisersCalled != 3\n"); + printf("[FAIL] dlopen-flat\n"); + return 1; + } + } + // Now unload foo which shouldn't do anything. + result = dlclose(fooHandle); + if (result != 0) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-flat\n"); + return 1; + } + + // Open foo again which shouldn't do anything. + { + const char* path = RUN_DIR "/libfoo.dylib"; + fooHandle = dlopen(path, RTLD_LAZY); + if (!fooHandle) { + printf("dlopen failed with error: %s\n", dlerror()); + return 1; + } + if (gInitialisersCalled != 3) { + printf("gInitialisersCalled != 3\n"); + printf("[FAIL] dlopen-flat\n"); + return 1; + } + } + + printf("[PASS] dlopen-flat\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-framework-fallback.dtest/main.c b/dyld/testing/test-cases/dlopen-framework-fallback.dtest/main.c new file mode 100644 index 0000000..02c467e --- /dev/null +++ b/dyld/testing/test-cases/dlopen-framework-fallback.dtest/main.c @@ -0,0 +1,35 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-framework-fallback.exe + +// RUN: ./dlopen-framework-fallback.exe + +#include +#include + + + +int main() +{ + printf("[BEGIN] dlopen-framework-fallback\n"); + + // Verify dyld will fallback and look for framework in /System/Library/Frameworks/ + void* handle = dlopen("/System/Library/BadPath/CoreFoundation.framework/CoreFoundation", RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-framework-fallback\n"); + return 0; + } + + // validate handle works to find symbols + void* sym = dlsym(handle, "CFRetain"); + if ( sym == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-framework-fallback\n"); + return 0; + } + + printf("[PASS] dlopen-framework-fallback\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/bar.c b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/bar.c new file mode 100644 index 0000000..4381650 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/bar.c @@ -0,0 +1,7 @@ + +#include +#include + +void barInDylib() +{ +} diff --git a/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/baz.c b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/baz.c new file mode 100644 index 0000000..98ea4f1 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/baz.c @@ -0,0 +1,10 @@ + +#include +#include + +extern void barInDylib(); + +void bazInDylib() +{ + return barInDylib(); +} diff --git a/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/foo.c b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/foo.c new file mode 100644 index 0000000..837ec00 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/foo.c @@ -0,0 +1,7 @@ + +#include +#include + +void fooInBundle() +{ +} diff --git a/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c new file mode 100644 index 0000000..4547e90 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-indirect-groupNum.dtest/main.c @@ -0,0 +1,147 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-indirect-groupNum.exe -Wno-deprecated-declarations +// BUILD: $CC foo.c -o $BUILD_DIR/foo.bundle -bundle +// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC baz.c -dynamiclib -install_name $RUN_DIR/libbaz.dylib -o $BUILD_DIR/libbaz.dylib $BUILD_DIR/libbar.dylib + +// RUN: ./dlopen-indirect-groupNum.exe $RUN_DIR/foo.bundle $RUN_DIR/libbar.dylib $RUN_DIR/libbaz.dylib + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static void checkBundle(const char* path, bool unlinkBeforeDestroy) +{ + int fd = open(path, O_RDONLY, 0); + if ( fd == -1 ) { + printf("[FAIL] open(%s) failed", path); + exit(0); + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + printf("[FAIL] fstat() failed\n"); + exit(0); + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + printf("[FAIL] mmap() failed\n"); + exit(0); + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + printf("[FAIL] NSCreateObjectFileImageFromMemory failed\n"); + exit(0); + } + + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + printf("[FAIL] NSLinkModule failed\n"); + exit(0); + } + + if ( !unlinkBeforeDestroy ) { + // API lets you destroy ofi and NSModule lives on + if ( !NSDestroyObjectFileImage(ofi) ) { + printf("[FAIL] NSDestroyObjectFileImage failed\n"); + exit(0); + } + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_fooInBundle"); + if ( sym == NULL ) { + printf("[FAIL] NSLookupSymbolInModule failed\n"); + exit(0); + } + + void* func = NSAddressOfSymbol(sym); + if ( func == NULL ) { + printf("[FAIL] NSAddressOfSymbol failed\n"); + exit(0); + } + + Dl_info info; + if ( dladdr(func, &info) == 0 ) { + printf("[FAIL] dladdr(&p, xx) failed\n"); + exit(0); + } + //printf("_fooInBundle found in %s\n", info.dli_fname); + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + printf("[FAIL] NSUnLinkModule failed\n"); + exit(0); + } + + if ( dladdr(func, &info) != 0 ) { + printf("[FAIL] dladdr(&p, xx) found but should not have\n"); + exit(0); + } + + if ( unlinkBeforeDestroy ) { + if ( !NSDestroyObjectFileImage(ofi) ) { + printf("[FAIL] NSDestroyObjectFileImage failed\n"); + exit(0); + } + } +} + + + +static void tryImage(const char* path, const char* symbol) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-indirect-groupNum %s\n", path); + exit(0); + } + + void* sym = dlsym(handle, symbol); + if ( sym == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-indirect-groupNum %s\n", path); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-indirect-groupNum %s\n", path); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] dlopen-indirect-groupNum\n"); + + checkBundle(argv[1], true); + checkBundle(argv[1], false); + + // Now go again enough times to flush out any limits in our dlopen encodings. + for (unsigned i = 0; i != 255; ++i) + checkBundle(argv[1], false); + + // Open bar.dylib + tryImage(argv[2], "barInDylib"); + + // And now open baz.dylib which depends on bar.dylib + tryImage(argv[3], "bazInDylib"); + + printf("[PASS] dlopen-indirect-groupNum\n"); + return 0; +} \ No newline at end of file diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/A.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/A.c new file mode 100644 index 0000000..2fadfc3 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/A.c @@ -0,0 +1,11 @@ +#include +#include + +extern void setState(const char* from); + + +void a(const char* from) { + char buffer[100]; + sprintf(buffer, "a() from %s", from); + setState(buffer); +} diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/B.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/B.c new file mode 100644 index 0000000..aed2a47 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/B.c @@ -0,0 +1,11 @@ +extern void c(const char*); + +void b() { } + +void __attribute__((constructor)) +initB() +{ + c("initB"); +} + + diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/C.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/C.c new file mode 100644 index 0000000..338ddb5 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/C.c @@ -0,0 +1,16 @@ +#include +#include + +extern void setState(const char* from); + +void c(const char* from) { + char buffer[100]; + sprintf(buffer, "c() from %s", from); + setState(buffer); +} + +void __attribute__((constructor)) +initC() +{ + setState("initC"); +} diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/D.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/D.c new file mode 100644 index 0000000..4d9da2c --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/D.c @@ -0,0 +1,20 @@ +#include +#include + +extern void setState(const char* from); +extern void c(const char* from); + +void d(const char* from) { + char buffer[100]; + sprintf(buffer, "d() from %s", from); + setState(buffer); +} + +void __attribute__((constructor)) +initD() +{ + c("initD"); +} + + + diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/E.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/E.c new file mode 100644 index 0000000..2700f3c --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/E.c @@ -0,0 +1,13 @@ +extern void a(const char*); + +void e() { } + + +void __attribute__((constructor)) +initE() +{ + a("initE"); +} + + + diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/F.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/F.c new file mode 100644 index 0000000..88789cf --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/F.c @@ -0,0 +1,12 @@ +extern void d(const char*); + +void f() { } + +void __attribute__((constructor)) +initF() +{ + d("initF"); +} + + + diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/base.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/base.c new file mode 100644 index 0000000..e3143fb --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/base.c @@ -0,0 +1,26 @@ +#include +#include +#include + +static const char* expectedStrings[] = { + "a() from main", + "initC", + "c() from initB", + "c() from initD", + "a() from initE", + "d() from initF", + "DONE" +}; + +static const char** curState = expectedStrings; + +void setState(const char* from) +{ + printf("%s\n", from); + if ( strcmp(*curState, from) != 0 ) { + printf("[FAIL] dlopen-intertwined: expected %s\n", *curState); + exit(0); + } + ++curState; +} + diff --git a/dyld/testing/test-cases/dlopen-intertwined.dtest/main.c b/dyld/testing/test-cases/dlopen-intertwined.dtest/main.c new file mode 100644 index 0000000..11580d3 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-intertwined.dtest/main.c @@ -0,0 +1,63 @@ + + +// BUILD: $CC base.c -dynamiclib -o $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libbase.dylib +// BUILD: $CC A.c -dynamiclib -o $BUILD_DIR/libA.dylib -install_name $RUN_DIR/libA.dylib $BUILD_DIR/libbase.dylib +// BUILD: $CC C.c -dynamiclib -o $BUILD_DIR/libC.dylib -install_name $RUN_DIR/libC.dylib $BUILD_DIR/libbase.dylib +// BUILD: $CC B.c -dynamiclib -o $BUILD_DIR/libB.dylib -install_name $RUN_DIR/libB.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libC.dylib +// BUILD: $CC D.c -dynamiclib -o $BUILD_DIR/libD.dylib -install_name $RUN_DIR/libD.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libC.dylib +// BUILD: $CC E.c -dynamiclib -o $BUILD_DIR/libE.dylib -install_name $RUN_DIR/libE.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libA.dylib +// BUILD: $CC F.c -dynamiclib -o $BUILD_DIR/libF.dylib -install_name $RUN_DIR/libF.dylib $BUILD_DIR/libbase.dylib $BUILD_DIR/libD.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-intertwined.exe $BUILD_DIR/libbase.dylib $BUILD_DIR/libA.dylib -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlopen-intertwined.exe + +#include +#include +#include +#include + +// main deps on A +// main dlopens B which deps on C +// main dlopens D which deps on C +// main dlopens E which deps on A +// main dlopens F which deps on D + +extern void a(const char*); +extern void setState(const char* from); + +int main() +{ + printf("[BEGIN] dlopen-intertwined\n"); + + a("main"); + + void* handle = dlopen(RUN_DIR "/libB.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); + exit(0); + } + + handle = dlopen(RUN_DIR "/libD.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); + exit(0); + } + + handle = dlopen(RUN_DIR "/libE.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); + exit(0); + } + + handle = dlopen(RUN_DIR "/libF.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlopen-intertwined: %s\n", dlerror()); + exit(0); + } + + setState("DONE"); + + printf("[PASS] dlopen-intertwined\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-long-error-message.dtest/main.c b/dyld/testing/test-cases/dlopen-long-error-message.dtest/main.c new file mode 100644 index 0000000..8b0a16a --- /dev/null +++ b/dyld/testing/test-cases/dlopen-long-error-message.dtest/main.c @@ -0,0 +1,30 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-long-error-message.exe + +// RUN: ./dlopen-long-error-message.exe + +#include +#include +#include +#include + + + +int main() +{ + printf("[BEGIN] dlopen-long-error-message\n"); + + for (int i=0; i < 10; ++i) { + void* handle = dlopen("/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/bogus/path/libbogus.dylib", RTLD_FIRST); + if ( handle != NULL ) { + printf("[FAIL] dlopen-long-error-message should have failed on non-existent file\n"); + return 0; + } + free(strdup("hello there")); + } + + printf("[PASS] dlopen-long-error-message\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-race.dtest/foo.c b/dyld/testing/test-cases/dlopen-race.dtest/foo.c new file mode 100644 index 0000000..c8e9924 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-race.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 10; +} + diff --git a/dyld/testing/test-cases/dlopen-race.dtest/main.c b/dyld/testing/test-cases/dlopen-race.dtest/main.c new file mode 100644 index 0000000..bad9ff7 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-race.dtest/main.c @@ -0,0 +1,33 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-race.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlopen-race.exe + +#include +#include +#include +#include + + + +int main() +{ + printf("[BEGIN] dlopen-read\n"); + + __block bool allGood = true; + dispatch_apply(6, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) { + for (int i=0; i < 500; ++i) { + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlopen-read: %s\n", dlerror()); + exit(0); + } + dlclose(handle); + } + }); + + printf("[PASS] dlopen-read\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-realpath.dtest/main.c b/dyld/testing/test-cases/dlopen-realpath.dtest/main.c new file mode 100644 index 0000000..b34e008 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-realpath.dtest/main.c @@ -0,0 +1,41 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-realpath.exe +// BUILD: cd $BUILD_DIR && ln -s ./IOKit.framework/IOKit IOKit && ln -s /System/Library/Frameworks/IOKit.framework IOKit.framework + +// RUN: ./dlopen-realpath.exe + +#include +#include + + +static void tryImage(const char* path) +{ + printf("[BEGIN] dlopen-realpath %s\n", path); + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-realpath %s\n", path); + return; + } + + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-realpath %s\n", path); + return; + } + + printf("[PASS] dlopen-realpath %s\n", path); +} + + + +int main() +{ + tryImage("./IOKit.framework/IOKit"); + tryImage("./././IOKit/../IOKit.framework/IOKit"); + tryImage("./IOKit"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-recurse.dtest/bar.c b/dyld/testing/test-cases/dlopen-recurse.dtest/bar.c new file mode 100644 index 0000000..c86856e --- /dev/null +++ b/dyld/testing/test-cases/dlopen-recurse.dtest/bar.c @@ -0,0 +1,5 @@ +int bar() +{ + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-recurse.dtest/foo.c b/dyld/testing/test-cases/dlopen-recurse.dtest/foo.c new file mode 100644 index 0000000..eb1114d --- /dev/null +++ b/dyld/testing/test-cases/dlopen-recurse.dtest/foo.c @@ -0,0 +1,16 @@ +#include + + +void myinit() __attribute__((constructor)); + +void myinit() +{ + // call dlopen() in initializer + void* handle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY); +} + +int foo() +{ + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-recurse.dtest/main.c b/dyld/testing/test-cases/dlopen-recurse.dtest/main.c new file mode 100644 index 0000000..53c244e --- /dev/null +++ b/dyld/testing/test-cases/dlopen-recurse.dtest/main.c @@ -0,0 +1,26 @@ + +// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -DRUN_DIR="$RUN_DIR" +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-recurse.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlopen-recurse.exe + +#include +#include +#include +#include + + + +int main() +{ + printf("[BEGIN] dlopen-recurse\n"); + + // libfoo's initializer calls dlopen(). If that hangs, we have a locking bug + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); + dlclose(handle); + + printf("[PASS] dlopen-recurse\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlopen-signing.dtest/dylib.c b/dyld/testing/test-cases/dlopen-signing.dtest/dylib.c new file mode 100644 index 0000000..9841a54 --- /dev/null +++ b/dyld/testing/test-cases/dlopen-signing.dtest/dylib.c @@ -0,0 +1,3 @@ +int foo() { + return 10; +} diff --git a/dyld/testing/test-cases/dlopen-signing.dtest/main.c b/dyld/testing/test-cases/dlopen-signing.dtest/main.c new file mode 100644 index 0000000..eaaa69b --- /dev/null +++ b/dyld/testing/test-cases/dlopen-signing.dtest/main.c @@ -0,0 +1,48 @@ +// BUILD: $CC dylib.c -dynamiclib -o $BUILD_DIR/signed.dylib +// BUILD: $CC dylib.c -dynamiclib -o $BUILD_DIR/unsigned.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-signed.exe +// BUILD: $CC main.c -o $BUILD_DIR/dlopen-unsigned.exe + +// FIXME: add builds that sign the executable and the dylib in in various ways +// At this time we don't have a way to do that, so this test must be run +// manually. + +#include +#include + +int main() { + printf("[BEGIN] dlopen-signing\n"); + void* handle = dlopen("signed.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-signing (signed loading signed)\n"); + return 0; + } else { + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-signing (signed unloading signed)\n"); + return 0; + } + } + + handle = dlopen("unsigned.dylib", RTLD_LAZY); + if ( handle != NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlopen-signing (signed loading unsigned)\n"); + return 0; + } else { + int result = dlclose(handle); + if ( result != 0 ) { + printf("dlclose() returned %c\n", result); + printf("[FAIL] dlopen-signing (signed unloading signed)\n"); + return 0; + } + } + + printf("[PASS] dlopen-signing\n"); + + return 0; +} + + diff --git a/dyld/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/foo.c b/dyld/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/foo.c new file mode 100644 index 0000000..1f6bcd8 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/foo.c @@ -0,0 +1,6 @@ + +void foo() { } + +#if DYN +void foo2() {} +#endif diff --git a/dyld/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c b/dyld/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c new file mode 100644 index 0000000..de667dd --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_DEFAULT.dtest/main.c @@ -0,0 +1,85 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN +// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_DEFAULT.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlsym-RTLD_DEFAULT.exe + +#include +#include +#include + + +// verify RTLD_DEFAULT search order + +int mainSymbol = 4; + + +// my local implemention of free +void free(void* p) { } + + +static bool symbolInImage(const char* symName, const char* image) +{ + void* sym = dlsym(RTLD_DEFAULT, symName); + if ( sym == NULL ) + return false; + const char* imagePath = dyld_image_path_containing_address(sym); + if ( imagePath == NULL ) + return false; + return (strstr(imagePath, image) != NULL); +} + + + + +int main() +{ + printf("[BEGIN] dlsym-RTLD_DEFAULT\n"); + + // verify mainSymbol is found in main executable + if ( !symbolInImage("mainSymbol", "dlsym-RTLD_DEFAULT") ) { + printf("[FAIL] dlsym-RTLD_DEFAULT: mainSymbol\n"); + return 0; + } + + // verify free is found in main executable, overrideing one in OS + if ( !symbolInImage("free", "dlsym-RTLD_DEFAULT") ) { + printf("[FAIL] dlsym-RTLD_DEFAULT: free\n"); + return 0; + } + + // verify foo is found in libfoo-static.dylib + if ( !symbolInImage("foo", "libfoo-static.dylib") ) { + printf("[FAIL] dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n"); + return 0; + } + + void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlsym-RTLD_DEFAULT: libfoo-dynamic.dylib could not be loaded\n"); + return 0; + } + + // verify foo is still found in statically linked lib + if ( !symbolInImage("foo", "libfoo-static.dylib") ) { + printf("[FAIL] dlsym-RTLD_DEFAULT: foo not in libfoo-static.dylib\n"); + return 0; + } + + // verify foo2 is found in libfoo-dynamic.dylib" + if ( !symbolInImage("foo2", "libfoo-dynamic.dylib") ) { + printf("[FAIL] dlsym-RTLD_DEFAULT: foo2 not in libfoo-dynamic.dylib\n"); + return 0; + } + + // renamed and re-exported symbols work + if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) { + printf("[FAIL] dlsym-RTLD_DEFAULT: strcmp not found\n"); + return 0; + } + + printf("[PASS] dlsym-RTLD_DEFAULT\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/foo.c b/dyld/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/foo.c new file mode 100644 index 0000000..1f6bcd8 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/foo.c @@ -0,0 +1,6 @@ + +void foo() { } + +#if DYN +void foo2() {} +#endif diff --git a/dyld/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c b/dyld/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c new file mode 100644 index 0000000..80c9ef5 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_MAIN_ONLY.dtest/main.c @@ -0,0 +1,79 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN +// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_MAIN_ONLY.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlsym-RTLD_MAIN_ONLY.exe + +#include +#include +#include + + +// verify RTLD_MAIN_ONLY search order + +int mainSymbol = 4; + + +// my local implemention of free +void free(void* p) { } + + +static bool symbolInImage(const char* symName, const char* image) +{ + void* sym = dlsym(RTLD_MAIN_ONLY, symName); + if ( sym == NULL ) + return false; + const char* imagePath = dyld_image_path_containing_address(sym); + if ( imagePath == NULL ) + return false; + return (strstr(imagePath, image) != NULL); +} + + + + +int main() +{ + printf("[BEGIN] dlsym-RTLD_MAIN_ONLY\n"); + + // verify mainSymbol is found + if ( !symbolInImage("mainSymbol", "dlsym-RTLD_MAIN_ONLY") ) { + printf("[FAIL] dlsym-RTLD_MAIN_ONLY: mainSymbol should have been found\n"); + return 0; + } + + // verify free is found in this program - not in OS + if ( !symbolInImage("free", "dlsym-RTLD_MAIN_ONLY") ) { + printf("[FAIL] dlsym-RTLD_MAIN_ONLY: free\n"); + return 0; + } + + // verify foo is not found + if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) { + printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo should not have been found\n"); + return 0; + } + + void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlsym-RTLD_MAIN_ONLY: libfoo-dynamic.dylib could not be loaded\n"); + return 0; + } + + // verify foo is still not found + if ( dlsym(RTLD_MAIN_ONLY, "foo") != NULL ) { + printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo should not have been found after dlopen\n"); + return 0; + } + + // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_MAIN_ONLY only searches main executable + if ( dlsym(RTLD_MAIN_ONLY, "foo2") != NULL ) { + printf("[FAIL] dlsym-RTLD_MAIN_ONLY: foo2 found but should not have been\n"); + return 0; + } + + printf("[PASS] dlsym-RTLD_MAIN_ONLY\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlsym-RTLD_NEXT.dtest/foo.c b/dyld/testing/test-cases/dlsym-RTLD_NEXT.dtest/foo.c new file mode 100644 index 0000000..1f6bcd8 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_NEXT.dtest/foo.c @@ -0,0 +1,6 @@ + +void foo() { } + +#if DYN +void foo2() {} +#endif diff --git a/dyld/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c b/dyld/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c new file mode 100644 index 0000000..7d81e99 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_NEXT.dtest/main.c @@ -0,0 +1,79 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN +// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_NEXT.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlsym-RTLD_NEXT.exe + +#include +#include +#include + + +// verify RTLD_NEXT search order + +int mainSymbol = 4; + + +// my local implemention of free +void free(void* p) { } + + +static bool symbolInImage(const char* symName, const char* image) +{ + void* sym = dlsym(RTLD_NEXT, symName); + if ( sym == NULL ) + return false; + const char* imagePath = dyld_image_path_containing_address(sym); + if ( imagePath == NULL ) + return false; + return (strstr(imagePath, image) != NULL); +} + + + + +int main() +{ + printf("[BEGIN] dlsym-RTLD_NEXT\n"); + + // verify mainSymbol is not found + if ( dlsym(RTLD_NEXT, "mainSymbol") != NULL ) { + printf("[FAIL] dlsym-RTLD_NEXT: mainSymbol should not have been found\n"); + return 0; + } + + // verify free is found in OS (not local one) + if ( !symbolInImage("free", "/usr/lib/") ) { + printf("[FAIL] dlsym-RTLD_NEXT: free\n"); + return 0; + } + + // verify foo is found in libfoo-static.dylib + if ( !symbolInImage("foo", "libfoo-static.dylib") ) { + printf("[FAIL] dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n"); + return 0; + } + + void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlsym-RTLD_NEXT: libfoo-dynamic.dylib could not be loaded\n"); + return 0; + } + + // verify foo is still found in statically linked lib + if ( !symbolInImage("foo", "libfoo-static.dylib") ) { + printf("[FAIL] dlsym-RTLD_NEXT: foo not in libfoo-static.dylib\n"); + return 0; + } + + // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_NEXT only searches thing this image would have seen + if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) { + printf("[FAIL] dlsym-RTLD_NEXT: foo2 found but should not have been\n"); + return 0; + } + + printf("[PASS] dlsym-RTLD_NEXT\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlsym-RTLD_SELF.dtest/foo.c b/dyld/testing/test-cases/dlsym-RTLD_SELF.dtest/foo.c new file mode 100644 index 0000000..1f6bcd8 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_SELF.dtest/foo.c @@ -0,0 +1,6 @@ + +void foo() { } + +#if DYN +void foo2() {} +#endif diff --git a/dyld/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c b/dyld/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c new file mode 100644 index 0000000..cd921ff --- /dev/null +++ b/dyld/testing/test-cases/dlsym-RTLD_SELF.dtest/main.c @@ -0,0 +1,79 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-static.dylib -o $BUILD_DIR/libfoo-static.dylib +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo-dynamic.dylib -o $BUILD_DIR/libfoo-dynamic.dylib -DDYN +// BUILD: $CC main.c $BUILD_DIR/libfoo-static.dylib -o $BUILD_DIR/dlsym-RTLD_SELF.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlsym-RTLD_SELF.exe + +#include +#include +#include + + +// verify RTLD_SELF search order + +int mainSymbol = 4; + + +// my local implemention of free +void free(void* p) { } + + +static bool symbolInImage(const char* symName, const char* image) +{ + void* sym = dlsym(RTLD_SELF, symName); + if ( sym == NULL ) + return false; + const char* imagePath = dyld_image_path_containing_address(sym); + if ( imagePath == NULL ) + return false; + return (strstr(imagePath, image) != NULL); +} + + + + +int main() +{ + printf("[BEGIN] dlsym-RTLD_SELF\n"); + + // verify mainSymbol is found + if ( dlsym(RTLD_SELF, "mainSymbol") == NULL ) { + printf("[FAIL] dlsym-RTLD_SELF: mainSymbol should have been found\n"); + return 0; + } + + // verify free is found in this program - not in OS + if ( !symbolInImage("free", "dlsym-RTLD_SELF") ) { + printf("[FAIL] dlsym-RTLD_SELF: free\n"); + return 0; + } + + // verify foo is found in libfoo-static.dylib + if ( !symbolInImage("foo", "libfoo-static.dylib") ) { + printf("[FAIL] dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n"); + return 0; + } + + void* handle = dlopen(RUN_DIR "/libfoo-dynamic.dylib", RTLD_LAZY); + if ( handle == NULL ) { + printf("[FAIL] dlsym-RTLD_SELF: libfoo-dynamic.dylib could not be loaded\n"); + return 0; + } + + // verify foo is still found in statically linked lib + if ( !symbolInImage("foo", "libfoo-static.dylib") ) { + printf("[FAIL] dlsym-RTLD_SELF: foo not in libfoo-static.dylib\n"); + return 0; + } + + // verify foo2 is not found in libfoo-dynamic.dylib", because RTLD_SELF only searches thing this image would have seen + if ( symbolInImage("foo2", "libfoo-dynamic.dylib") ) { + printf("[FAIL] dlsym-RTLD_SELF: foo2 found but should not have been\n"); + return 0; + } + + printf("[PASS] dlsym-RTLD_SELF\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlsym-handle.dtest/bar.c b/dyld/testing/test-cases/dlsym-handle.dtest/bar.c new file mode 100644 index 0000000..30ade8f --- /dev/null +++ b/dyld/testing/test-cases/dlsym-handle.dtest/bar.c @@ -0,0 +1,2 @@ + +void bar() { } diff --git a/dyld/testing/test-cases/dlsym-handle.dtest/base.c b/dyld/testing/test-cases/dlsym-handle.dtest/base.c new file mode 100644 index 0000000..e543e16 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-handle.dtest/base.c @@ -0,0 +1,3 @@ + +void base() { } + diff --git a/dyld/testing/test-cases/dlsym-handle.dtest/foo.c b/dyld/testing/test-cases/dlsym-handle.dtest/foo.c new file mode 100644 index 0000000..ab61e80 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-handle.dtest/foo.c @@ -0,0 +1,2 @@ + +void foo() { } diff --git a/dyld/testing/test-cases/dlsym-handle.dtest/main.c b/dyld/testing/test-cases/dlsym-handle.dtest/main.c new file mode 100644 index 0000000..52e7750 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-handle.dtest/main.c @@ -0,0 +1,99 @@ + +// BUILD: $CC base.c -dynamiclib -install_name $RUN_DIR/libbase.dylib -o $BUILD_DIR/libbase.dylib +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC bar.c -dynamiclib $BUILD_DIR/libbase.dylib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlsym-handle.exe -DRUN_DIR="$RUN_DIR" + +// RUN: ./dlsym-handle.exe + +#include +#include +#include +#include + + +// verify RTLD_DEFAULT search order + +int mainSymbol = 4; + +int main() +{ + printf("[BEGIN] dlsym-handle\n"); + + void* fooHandle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_LAZY); + if ( fooHandle == NULL ) { + printf("[FAIL] dlsym-handle: libfoo.dylib could not be loaded, %s\n", dlerror()); + return 0; + } + + void* barHandle = dlopen(RUN_DIR "/libbar.dylib", RTLD_LAZY); + if ( barHandle == NULL ) { + printf("[FAIL] dlsym-handle: libbar.dylib could not be loaded, %s\n", dlerror()); + return 0; + } + + // verify fooHandle does not find mainSymbol + if ( dlsym(fooHandle, "mainSymbol") != NULL ) { + printf("[FAIL] dlsym-handle: mainSymbol was found with fooHandle\n"); + return 0; + } + + // verify fooHandle can find foo + if ( dlsym(fooHandle, "foo") == NULL ) { + printf("[FAIL] dlsym-handle: foo not found with fooHandle\n"); + return 0; + } + + // verify fooHandle can find base + if ( dlsym(fooHandle, "base") == NULL ) { + printf("[FAIL] dlsym-handle: base not found with fooHandle\n"); + return 0; + } + + // verify fooHandle cannot find bar + if ( dlsym(fooHandle, "bar") != NULL ) { + printf("[FAIL] dlsym-handle: bar found with fooHandle\n"); + return 0; + } + + // verify barHandle can find bar + if ( dlsym(barHandle, "bar") == NULL ) { + printf("[FAIL] dlsym-handle: bar not found with barHandle\n"); + return 0; + } + + // verify barHandle can find base + if ( dlsym(barHandle, "base") == NULL ) { + printf("[FAIL] dlsym-handle: base not found with barHandle\n"); + return 0; + } + + // verify barHandle cannot find foo + if ( dlsym(barHandle, "foo") != NULL ) { + printf("[FAIL] dlsym-handle: foo found with barHandle\n"); + return 0; + } + + // verify renamed and re-exported symbols work + if ( dlsym(RTLD_DEFAULT, "strcmp") == NULL ) { + printf("[FAIL] dlsym-handle: strcmp not found\n"); + return 0; + } + + // verify bad handle errors + if ( dlsym((void*)0xdeadbeef, "malloc") != NULL ) { + printf("[FAIL] dlsym-handle: malloc found with bad handle\n"); + return 0; + } + else { + const char* message = dlerror(); + if ( strstr(message, "invalid") == NULL ) { + printf("[FAIL] dlsym-handle: invalid handle error message missing 'invalid'\n"); + return 0; + } + } + + printf("[PASS] dlsym-handle\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlsym-re-export.dtest/foo.c b/dyld/testing/test-cases/dlsym-re-export.dtest/foo.c new file mode 100644 index 0000000..c8e9924 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-re-export.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 10; +} + diff --git a/dyld/testing/test-cases/dlsym-re-export.dtest/main.c b/dyld/testing/test-cases/dlsym-re-export.dtest/main.c new file mode 100644 index 0000000..b1752d6 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-re-export.dtest/main.c @@ -0,0 +1,43 @@ + +// BUILD: mkdir -p $BUILD_DIR/sub1 $BUILD_DIR/sub2 +// BUILD: $CC sub1.c -dynamiclib -install_name @rpath/libsub1.dylib -o $BUILD_DIR/sub1/libsub1.dylib +// BUILD: $CC sub2.c -dynamiclib -install_name @rpath/libsub2.dylib -o $BUILD_DIR/sub2/libsub2.dylib +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib -rpath @loader_path/sub1 -Wl,-reexport_library,$BUILD_DIR/sub1/libsub1.dylib -Wl,-reexport_library,$BUILD_DIR/sub2/libsub2.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dlsym-reexport.exe -DRUN_DIR="$RUN_DIR" -rpath @loader_path/sub2 + +// RUN: ./dlsym-reexport.exe + +// rpath for sub1 is found in libfoo.dylib. rpath for sub2 is found in dlsym-reexport.exe + +#include +#include + +int main() +{ + printf("[BEGIN] dlsym-re-export\n"); + // RTLD_FIRST means dlsym() should only search libfoo.dylib (and any re-exports) + void* handle = dlopen(RUN_DIR "/libfoo.dylib", RTLD_FIRST); + if ( handle == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlsym-re-export\n"); + return 0; + } + + void* sym1 = dlsym(handle, "sub1"); + if ( sym1 == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlsym-re-export\n"); + return 0; + } + + void* sym2 = dlsym(handle, "sub2"); + if ( sym2 == NULL ) { + printf("dlerror(): %s\n", dlerror()); + printf("[FAIL] dlsym-re-export\n"); + return 0; + } + + printf("[PASS] dlsym-re-export\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dlsym-re-export.dtest/sub1.c b/dyld/testing/test-cases/dlsym-re-export.dtest/sub1.c new file mode 100644 index 0000000..98c93f6 --- /dev/null +++ b/dyld/testing/test-cases/dlsym-re-export.dtest/sub1.c @@ -0,0 +1,5 @@ +int sub1() +{ + return 1; +} + diff --git a/dyld/testing/test-cases/dlsym-re-export.dtest/sub2.c b/dyld/testing/test-cases/dlsym-re-export.dtest/sub2.c new file mode 100644 index 0000000..852b89b --- /dev/null +++ b/dyld/testing/test-cases/dlsym-re-export.dtest/sub2.c @@ -0,0 +1,5 @@ +int sub2() +{ + return 2; +} + diff --git a/dyld/testing/test-cases/dtrace.dtest/main.c b/dyld/testing/test-cases/dtrace.dtest/main.c new file mode 100644 index 0000000..237dbd1 --- /dev/null +++ b/dyld/testing/test-cases/dtrace.dtest/main.c @@ -0,0 +1,27 @@ +// BUILD: /usr/sbin/dtrace -h -s main.d -o $TEMP_DIR/probes.h +// BUILD: $CC main.c -I$TEMP_DIR -o $BUILD_DIR/dtrace.exe +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/dtrace.exe + +// RUN: $SUDO dtrace -l -n 'dyld_testing*:dtrace.exe:main:callback' -c ./dtrace.exe + + + +#include +#include +#include +#include + +#include "probes.h" + +int main() +{ + printf("[BEGIN] dtrace\n"); + + DYLD_TESTING_CALLBACK(); + + if (!DYLD_TESTING_CALLBACK_ENABLED()) + printf("[FAIL] !DYLD_TESTING_CALLBACK_ENABLED\n"); + + printf("[PASS] dtrace\n"); + return 0; +} diff --git a/dyld/testing/test-cases/dtrace.dtest/main.d b/dyld/testing/test-cases/dtrace.dtest/main.d new file mode 100644 index 0000000..5918888 --- /dev/null +++ b/dyld/testing/test-cases/dtrace.dtest/main.d @@ -0,0 +1,3 @@ +provider dyld_testing { + probe callback(); +}; diff --git a/dyld/testing/test-cases/dyld_abort_payload.dtest/defSymbol.c b/dyld/testing/test-cases/dyld_abort_payload.dtest/defSymbol.c new file mode 100644 index 0000000..2e575f3 --- /dev/null +++ b/dyld/testing/test-cases/dyld_abort_payload.dtest/defSymbol.c @@ -0,0 +1,5 @@ + + +#if HAS_SYMBOL +int slipperySymbol = 5; +#endif diff --git a/dyld/testing/test-cases/dyld_abort_payload.dtest/emptyMain.c b/dyld/testing/test-cases/dyld_abort_payload.dtest/emptyMain.c new file mode 100644 index 0000000..0ba8a5b --- /dev/null +++ b/dyld/testing/test-cases/dyld_abort_payload.dtest/emptyMain.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, const char* argv[]) +{ + return 1; +} + diff --git a/dyld/testing/test-cases/dyld_abort_payload.dtest/foo.c b/dyld/testing/test-cases/dyld_abort_payload.dtest/foo.c new file mode 100644 index 0000000..bd89548 --- /dev/null +++ b/dyld/testing/test-cases/dyld_abort_payload.dtest/foo.c @@ -0,0 +1,5 @@ + +void foo() +{ +} + diff --git a/dyld/testing/test-cases/dyld_abort_payload.dtest/main.c b/dyld/testing/test-cases/dyld_abort_payload.dtest/main.c new file mode 100644 index 0000000..e01234d --- /dev/null +++ b/dyld/testing/test-cases/dyld_abort_payload.dtest/main.c @@ -0,0 +1,173 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name /cant/find/me.dylib -o $BUILD_DIR/libmissing.dylib +// BUILD: $CC emptyMain.c $BUILD_DIR/libmissing.dylib -o $BUILD_DIR/prog_missing_dylib.exe +// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libMissingSymbols.dylib +// BUILD: $CC defSymbol.c -dynamiclib -install_name libMissingSymbols.dylib -o $BUILD_DIR/libHasSymbols.dylib -DHAS_SYMBOL +// BUILD: $CC useSymbol.c $BUILD_DIR/libHasSymbols.dylib -o $BUILD_DIR/prog_missing_symbol.exe +// BUILD: $CC main.c -o $BUILD_DIR/dyld_abort_tests.exe + +// RUN: ./dyld_abort_tests.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static bool sSignalCaught = false; +static bool sChildAbortInfoCorrect = false; +static pid_t sChildPid = 0; +static uint64_t sExpectedDyldReason = 0; +static const char* sExpectedDylibPath = NULL; +static const char* sExpectedSymbol = NULL; + + +static void childDied(int sig) +{ + sSignalCaught = true; + //printf("sigchld for pid=%d\n", sChildPid); + + struct proc_exitreasoninfo info; + bzero(&info, sizeof(info)); + uint8_t packReasonData[OS_REASON_BUFFER_MAX_SIZE]; + bzero(packReasonData, OS_REASON_BUFFER_MAX_SIZE); + info.eri_reason_buf_size = OS_REASON_BUFFER_MAX_SIZE; + info.eri_kcd_buf = (user_addr_t)packReasonData; + //fprintf(stderr, "info=%p\n", &info); + if ( proc_pidinfo(sChildPid, PROC_PIDEXITREASONINFO, 1, &info, PROC_PIDEXITREASONINFO_SIZE) != sizeof(struct proc_exitreasoninfo) ) { + printf("bad return size from proc_pidinfo()\n"); + return; + } + if ( info.eri_namespace != OS_REASON_DYLD ) { + printf("eri_namespace != OS_REASON_DYLD\n"); + return; + } + if ( info.eri_code != sExpectedDyldReason ) { + printf("eri_code != %lld\n", sExpectedDyldReason); + return; + } + kcdata_iter_t iter = kcdata_iter(packReasonData, info.eri_reason_buf_size); + + if ( !kcdata_iter_valid(iter) ) { + printf("invalid kcdata iterator from payload data\n"); + return; + } + + if ( kcdata_iter_type(iter) != KCDATA_BUFFER_BEGIN_OS_REASON ){ + printf("first kcdata from payload data is not KCDATA_BUFFER_BEGIN_OS_REASON\n"); + return; + } + + kcdata_iter_t payloadIter = kcdata_iter_find_type(iter, EXIT_REASON_USER_PAYLOAD); + if ( !kcdata_iter_valid(payloadIter) ) { + printf("invalid kcdata payload iterator from payload data\n"); + return; + } + const dyld_abort_payload* dyldInfo = (dyld_abort_payload*)kcdata_iter_payload(payloadIter); + + if ( dyldInfo->version != 1 ) { + printf("dyld payload is not version 1\n"); + return; + } + + if ( (dyldInfo->flags & 1) == 0 ) { + printf("dyld flags should have low bit set to me process terminated at launch\n"); + return; + } + + if ( sExpectedDylibPath != NULL ) { + if ( dyldInfo->targetDylibPathOffset != 0 ) { + const char* targetDylib = (char*)dyldInfo + dyldInfo->targetDylibPathOffset; + if ( strcmp(sExpectedDylibPath, targetDylib) != 0 ) { + printf("dylib path (%s) not what expected (%s)\n", targetDylib, sExpectedDylibPath); + return; + } + } + else { + printf("dylib path (%s) not provided by dyld\n", sExpectedDylibPath); + return; + } + } + + if ( sExpectedSymbol != NULL ) { + if ( dyldInfo->targetDylibPathOffset != 0 ) { + const char* missingSymbol = (char*)dyldInfo + dyldInfo->symbolOffset; + if ( strcmp(sExpectedSymbol, missingSymbol) != 0 ) { + printf("symbol (%s) not what expected (%s)\n", missingSymbol, sExpectedSymbol); + return; + } + } + else { + printf("symbol (%s) not provided by dyld\n", sExpectedSymbol); + return; + } + } + + sChildAbortInfoCorrect = true; +} + + +bool runTest(const char* prog, uint64_t dyldReason, const char* expectedDylibPath, const char* expectedSymbol) +{ + sSignalCaught = false; + sChildAbortInfoCorrect = false; + sExpectedDyldReason = dyldReason; + sExpectedDylibPath = expectedDylibPath; + sExpectedSymbol = expectedSymbol; + + // fork and exec child + sChildPid = fork(); + if ( sChildPid < 0 ) + err(EXIT_FAILURE, "fork"); + if ( sChildPid == 0 ) { + // child side + char* childArgv[] = { (char*)prog, NULL }; + int result = execvp(prog, childArgv); + err(EXIT_FAILURE, "exec(\"%s\",...)", prog); + } + for(int i=0; i < 10; ++i) { + if ( sSignalCaught ) + break; + sleep(1); + } + + return sChildAbortInfoCorrect; +} + + +int main(int argc, const char* argv[]) +{ + bool someTestFailed = false; + printf("[BEGIN] dyld_abort_payload\n"); + + // set up signal handler for catching child terminations + signal(SIGCHLD, childDied); + + // test launch program with missing library + if ( !runTest("./prog_missing_dylib.exe", DYLD_EXIT_REASON_DYLIB_MISSING, "/cant/find/me.dylib", NULL) ) { + printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_DYLIB_MISSING\n"); + someTestFailed = true; + } + + // test launch program with missing symbol + if ( !runTest("./prog_missing_symbol.exe", DYLD_EXIT_REASON_SYMBOL_MISSING, "libMissingSymbols.dylib", "_slipperySymbol") ) { + printf("[FAIL] dyld_abort_payload DYLD_EXIT_REASON_SYMBOL_MISSING\n"); + someTestFailed = true; + } + + if ( !someTestFailed ) + printf("[PASS] dyld_abort_payload\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dyld_abort_payload.dtest/useSymbol.c b/dyld/testing/test-cases/dyld_abort_payload.dtest/useSymbol.c new file mode 100644 index 0000000..3aada36 --- /dev/null +++ b/dyld/testing/test-cases/dyld_abort_payload.dtest/useSymbol.c @@ -0,0 +1,8 @@ + + +extern int slipperySymbol; + +int main() +{ + return slipperySymbol; +} diff --git a/dyld/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt b/dyld/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt new file mode 100644 index 0000000..43b3565 --- /dev/null +++ b/dyld/testing/test-cases/dyld_get_sdk_version.dtest/bad.txt @@ -0,0 +1 @@ +bad file diff --git a/dyld/testing/test-cases/dyld_get_sdk_version.dtest/main.c b/dyld/testing/test-cases/dyld_get_sdk_version.dtest/main.c new file mode 100644 index 0000000..efa9be1 --- /dev/null +++ b/dyld/testing/test-cases/dyld_get_sdk_version.dtest/main.c @@ -0,0 +1,33 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/sdk-check.exe + +// RUN: ./sdk-check.exe + +#include +#include +#include + +extern struct mach_header __dso_handle; + +int main() +{ + printf("[BEGIN] dyld_get_sdk_version\n"); + + // should succeed + if ( dyld_get_sdk_version(&__dso_handle) == 0 ) { + printf("[FAIL] dyld_get_sdk_version: expected SDK\n"); + return 0; + } + + // should fail + const char* text = "bad text"; + if ( dyld_get_sdk_version((struct mach_header*)text) != 0 ) { + printf("[FAIL] dyld_get_sdk_version: expected failure\n"); + return 0; + } + + printf("[PASS] dyld_get_sdk_version\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dyld_image_path_containing_address.dtest/main.c b/dyld/testing/test-cases/dyld_image_path_containing_address.dtest/main.c new file mode 100644 index 0000000..9064caf --- /dev/null +++ b/dyld/testing/test-cases/dyld_image_path_containing_address.dtest/main.c @@ -0,0 +1,32 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/dyld_image_path_containing_address-test.exe + +// RUN: ./dyld_image_path_containing_address-test.exe + + +#include +#include +#include +#include +#include + + +int main() +{ + printf("[BEGIN] dyld_image_path_containing_address-test\n"); + + int count = _dyld_image_count(); + for (int i=0; i < count; ++i) { + const struct mach_header* mh = _dyld_get_image_header(i); + const char* name1 = _dyld_get_image_name(i); + const char* name2 = dyld_image_path_containing_address(mh); + if ( strcmp(name1, name2) != 0 ) { + printf("[FAIL] dyld_image_path_containing_address-test: %s != %s\n", name1, name2); + return 0; + } + } + + printf("[PASS] dyld_image_path_containing_address-test\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/dyld_process_info.dtest/linksWithCF.c b/dyld/testing/test-cases/dyld_process_info.dtest/linksWithCF.c new file mode 100644 index 0000000..4f6e00a --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info.dtest/linksWithCF.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main() +{ + (void)kill(getpid(), SIGSTOP); + return 0; +} + diff --git a/dyld/testing/test-cases/dyld_process_info.dtest/main.c b/dyld/testing/test-cases/dyld_process_info.dtest/main.c new file mode 100644 index 0000000..41c5ccb --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info.dtest/main.c @@ -0,0 +1,206 @@ + +// BUILD: $CC linksWithCF.c -o $BUILD_DIR/linksWithCF.exe -framework CoreFoundation +// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info.exe +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info.exe + +// RUN: $SUDO ./dyld_process_info.exe $RUN_DIR/linksWithCF.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern char** environ; + +#if __x86_64__ + cpu_type_t otherArch[] = { CPU_TYPE_I386 }; +#elif __i386__ + cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; +#elif __arm64__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM }; +#elif __arm__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; +#endif + +struct task_and_pid { + pid_t pid; + task_t task; +}; + +static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended) +{ + posix_spawnattr_t attr = 0; + if ( posix_spawnattr_init(&attr) != 0 ) { + printf("[FAIL] dyld_process_info posix_spawnattr_init()\n"); + exit(0); + } + if ( launchSuspended ) { + if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) { + printf("[FAIL] dyld_process_info POSIX_SPAWN_START_SUSPENDED\n"); + exit(0); + } + } + if ( launchOtherArch ) { + size_t copied; + if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) { + printf("[FAIL] dyld_process_info posix_spawnattr_setbinpref_np()\n"); + exit(0); + } + } + + struct task_and_pid child = {0, 0}; + const char* argv[] = { testProgPath, NULL }; + int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ); + if ( psResult != 0 ) { + printf("[FAIL] dyld_process_info posix_spawn(%s) failed, err=%d\n", testProgPath, psResult); + exit(0); + } + if (posix_spawnattr_destroy(&attr) != 0) { + printf("[FAIL] dyld_process_info posix_spawnattr_destroy()\n"); + exit(0); + } + + if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) { + printf("[FAIL] dyld_process_info task_for_pid()\n"); + kill(child.pid, SIGKILL); + exit(0); + } + +#if __x86_64__ + //printf("child pid=%d task=%d (%s, %s)\n", child.pid, child.task, launchOtherArch ? "i386" : "x86_64", launchSuspended ? "suspended" : "active"); +#endif + + // wait until process is up and has suspended itself + struct task_basic_info info; + do { + unsigned count = TASK_BASIC_INFO_COUNT; + kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count); + sleep(1); + } while ( info.suspend_count == 0 ); + + return child; +} + +static void killTest(struct task_and_pid tp) { + int r = kill(tp.pid, SIGKILL); + waitpid(tp.pid, &r, 0); +} + +static bool hasCF(task_t task, bool launchedSuspended) +{ + kern_return_t result; + dyld_process_info info = _dyld_process_info_create(task, 0, &result); + if ( info == NULL ) { + printf("[FAIL] dyld_process_info _dyld_process_info_create(), kern_return_t=%d\n", result); + return false; + } + + dyld_process_state_info stateInfo; + _dyld_process_info_get_state(info, &stateInfo); + bool valueSaysLaunchedSuspended = (stateInfo.dyldState == dyld_process_state_not_started); + if ( valueSaysLaunchedSuspended != launchedSuspended ) { + printf("[FAIL] dyld_process_info suspend state mismatch\n"); + return false; + } + + __block bool foundDyld = false; + _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { + //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path); + if ( strstr(path, "/usr/lib/dyld") != NULL ) + foundDyld = true; + }); + + if ( launchedSuspended ) { + // fprintf(stderr, "launched suspended image list:\n"); + __block bool foundMain = false; + _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { + //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path); + if ( strstr(path, "/linksWithCF.exe") != NULL ) + foundMain = true; + }); + return foundMain && foundDyld; + } + + __block bool foundCF = false; + _dyld_process_info_for_each_image(info, ^(uint64_t machHeaderAddress, const uuid_t uuid, const char* path) { + //fprintf(stderr, "0x%llX %s\n", machHeaderAddress, path); + if ( strstr(path, "/CoreFoundation.framework/") != NULL ) + foundCF = true; + }); + + _dyld_process_info_release(info); + + return foundCF && foundDyld; +} + + +int main(int argc, const char* argv[]) +{ + kern_return_t kr = KERN_SUCCESS; + printf("[BEGIN] dyld_process_info\n"); + + if ( argc < 2 ) { + printf("[FAIL] dyld_process_info missing argument\n"); + exit(0); + } + + const char* testProgPath = argv[1]; + struct task_and_pid child; + + // launch test program same arch as this program + child = launchTest(testProgPath, false, false); + if ( ! hasCF(child.task, false) ) { + printf("[FAIL] dyld_process_info same arch does not link with CF and dyld\n"); + killTest(child); + exit(0); + } + killTest(child); + + // launch test program suspended + child = launchTest(testProgPath, false, true); + if ( ! hasCF(child.task, true) ) { + printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n"); + killTest(child); + exit(0); + } + (void)kill(child.pid, SIGCONT); + killTest(child); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // only mac supports multiple architectures, run test program as other arch too + child = launchTest(testProgPath, true, false); + if ( ! hasCF(child.task, false) ) { + printf("[FAIL] dyld_process_info other arch does not link with CF and dyld\n"); + killTest(child); + exit(0); + } + killTest(child); + + // launch test program suspended + child = launchTest(testProgPath, true, true); + if ( ! hasCF(child.task, true) ) { + printf("[FAIL] dyld_process_info suspended does not link with CF and dyld\n"); + killTest(child); + exit(0); + } + (void)kill(child.pid, SIGCONT); + killTest(child); +#endif + + // verify this program does not use CF + if ( hasCF(mach_task_self(), false) ) { + printf("[FAIL] dyld_process_info self links with CF and dyld\n"); + exit(0); + } + + printf("[PASS] dyld_process_info\n"); + return 0; +} diff --git a/dyld/testing/test-cases/dyld_process_info_notify.dtest/foo.c b/dyld/testing/test-cases/dyld_process_info_notify.dtest/foo.c new file mode 100644 index 0000000..ec11833 --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info_notify.dtest/foo.c @@ -0,0 +1,4 @@ + +void foo() { +} + diff --git a/dyld/testing/test-cases/dyld_process_info_notify.dtest/main.c b/dyld/testing/test-cases/dyld_process_info_notify.dtest/main.c new file mode 100644 index 0000000..d76d44f --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info_notify.dtest/main.c @@ -0,0 +1,342 @@ + +// BUILD: $CC target.c -o $BUILD_DIR/target.exe +// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib +// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_notify.exe +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_notify.exe + +// RUN_TIMEOUT: 2400 +// RUN: $SUDO ./dyld_process_info_notify.exe $RUN_DIR/target.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern char** environ; + +#if __x86_64__ + cpu_type_t otherArch[] = { CPU_TYPE_I386 }; +#elif __i386__ + cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; +#elif __arm64__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM }; +#elif __arm__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; +#endif + +struct task_and_pid { + pid_t pid; + task_t task; +}; + +static struct task_and_pid launchTest(const char* testProgPath, const char* arg1, bool launchOtherArch, bool launchSuspended) +{ + //fprintf(stderr, "launchTest() launchOtherArch=%d, launchSuspended=%d, arg=%s\n", launchOtherArch, launchSuspended, arg1); + posix_spawnattr_t attr = 0; + if ( posix_spawnattr_init(&attr) != 0 ) { + printf("[FAIL] dyld_process_info_notify posix_spawnattr_init()\n"); + exit(0); + } + if ( launchSuspended ) { + if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) { + printf("[FAIL] dyld_process_info_notify POSIX_SPAWN_START_SUSPENDED\n"); + exit(0); + } + } + if ( launchOtherArch ) { + size_t copied; + if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) { + printf("[FAIL] dyld_process_info_notify posix_spawnattr_setbinpref_np()\n"); + exit(0); + } + } + + struct task_and_pid child; + const char* argv[] = { testProgPath, arg1, NULL }; + int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ); + if ( psResult != 0 ) { + printf("[FAIL] dyld_process_info_notify posix_spawn(%s) failed, err=%d\n", testProgPath, psResult); + exit(0); + } + if (posix_spawnattr_destroy(&attr) != 0) { + printf("[FAIL] dyld_process_info_notify posix_spawnattr_destroy()\n"); + exit(0); + } + if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) { + printf("[FAIL] dyld_process_info_notify task_for_pid()\n"); + (void)kill(child.pid, SIGKILL); + exit(0); + } + + return child; +} + +static void killTest(struct task_and_pid tp) { + int r = kill(tp.pid, SIGKILL); + waitpid(tp.pid, &r, 0); +} + +static void wait_util_task_suspended(task_t task) +{ + struct task_basic_info info; + do { + unsigned count = TASK_BASIC_INFO_COUNT; + kern_return_t kr = task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count); + //fprintf(stderr, "task_info() => %d, suspendCount=%d\n", kr, info.suspend_count); + sleep(1); + } while ( info.suspend_count == 0 ); +} + + +static bool monitor(struct task_and_pid tp, bool disconnectEarly, bool attachLate) +{ + kern_return_t kr; + __block bool sawMainExecutable = false; + __block bool sawlibSystem = false; + __block bool gotTerminationNotice = false; + __block bool gotEarlyNotice = false; + __block bool gotMainNotice = false; + __block bool gotMainNoticeBeforeAllInitialDylibs = false; + __block bool gotFooNoticeBeforeMain = false; + + __block int libFooLoadCount = 0; + __block int libFooUnloadCount = 0; + dispatch_semaphore_t taskDone = dispatch_semaphore_create(0); + + dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + + unsigned count = 0; + dyld_process_info_notify handle; + do { + handle = _dyld_process_info_notify(tp.task, serviceQueue, + ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { + if ( strstr(path, "/target.exe") != NULL ) + sawMainExecutable = true; + if ( strstr(path, "/libSystem") != NULL ) + sawlibSystem = true; + if ( strstr(path, "/libfoo.dylib") != NULL ) { + if ( !gotMainNotice ) + gotFooNoticeBeforeMain = true; + if ( unload ) + ++libFooUnloadCount; + else + ++libFooLoadCount; + if ( disconnectEarly ) { + gotEarlyNotice = true; + dispatch_semaphore_signal(taskDone); + } + } + //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n", + // unload, machHeader, 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], path); + }, + ^{ + //fprintf(stderr, "target exited\n"); + gotTerminationNotice = true; + dispatch_semaphore_signal(taskDone); + }, + &kr); + ++count; + if ( handle == NULL ) + fprintf(stderr, "_dyld_process_info_notify() returned NULL, result=%d, count=%d\n", kr, count); + } while ( (handle == NULL) && (count < 5) ); + + if ( handle == NULL ) { + return false; + } + + if (!attachLate) { + // If the process starts suspended register for main(), + // otherwise skip since this test is a race between + // process setup and notification registration + _dyld_process_info_notify_main(handle, ^{ + //fprintf(stderr, "target entering main()\n"); + gotMainNotice = true; + if ( !sawMainExecutable || !sawlibSystem ) + gotMainNoticeBeforeAllInitialDylibs = true; + }); + } else { + // if process suspends itself, wait until it has done so + wait_util_task_suspended(tp.task); + } + + // resume from initial suspend + kill(tp.pid, SIGCONT); + + // block waiting for notification that target has exited + bool gotSignal = (dispatch_semaphore_wait(taskDone, dispatch_time(DISPATCH_TIME_NOW, 10LL * NSEC_PER_SEC)) == 0); + _dyld_process_info_notify_release(handle); + + + if ( !gotSignal ) { + fprintf(stderr, "did not get exit signal\n"); + return false; + } + + // Do not run any tests associated with startup unless the kernel suspended us + // before main() + if (!attachLate) { + if ( !sawMainExecutable ) { + fprintf(stderr, "did not get load notification of main executable\n"); + return false; + } + + if ( !gotMainNotice ) { + fprintf(stderr, "did not get notification of main()\n"); + return false; + } + + if ( gotMainNoticeBeforeAllInitialDylibs ) { + fprintf(stderr, "notification of main() arrived before all initial dylibs\n"); + return false; + } + + if ( gotFooNoticeBeforeMain ) { + fprintf(stderr, "notification of main() arrived after libfoo load notice\n"); + return false; + } + + if ( !sawlibSystem ) { + fprintf(stderr, "did not get load notification of libSystem\n"); + return false; + } + } + + if ( disconnectEarly ) { + if ( libFooLoadCount != 1 ) { + fprintf(stderr, "got %d load notifications about libFoo instead of 1\n", libFooLoadCount); + return false; + } + if ( libFooUnloadCount != 0 ) { + fprintf(stderr, "got %d unload notifications about libFoo instead of 1\n", libFooUnloadCount); + return false; + } + } + else { + if ( libFooLoadCount != 3 ) { + fprintf(stderr, "got %d load notifications about libFoo instead of 3\n", libFooLoadCount); + return false; + } + if ( libFooUnloadCount != 3 ) { + fprintf(stderr, "got %d unload notifications about libFoo instead of 3\n", libFooUnloadCount); + return false; + } + } + + return true; +} + +static void validateMaxNotifies(struct task_and_pid tp) +{ + dispatch_queue_t serviceQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); + dyld_process_info_notify handles[10]; + for (int i=0; i < 10; ++i) { + kern_return_t kr; + handles[i] = _dyld_process_info_notify(tp.task, serviceQueue, + ^(bool unload, uint64_t timestamp, uint64_t machHeader, const uuid_t uuid, const char* path) { + //fprintf(stderr, "unload=%d, 0x%012llX <%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X> %s\n", + // unload, machHeader, 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], path); + }, + ^{ + //fprintf(stderr, "target exited\n"); + }, + &kr); + if ( handles[i] == NULL ) { + if ( i == 8 ) { + // expected failure, because only 8 simultaneous connections allowed + // release one and try again + _dyld_process_info_notify_release(handles[4]); + handles[4] = NULL; + } + else { + fprintf(stderr, "_dyld_process_info_notify() returned NULL and kern_result=%d, on count=%d\n", kr, i); + killTest(tp); + exit(0); + } + } + } + // release all + for (int i=0; i < 10; ++i) { + if ( handles[i] != NULL ) { + _dyld_process_info_notify_release(handles[i]); + } + } + dispatch_release(serviceQueue); +} + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] dyld_process_info_notify\n"); + if ( argc < 2 ) { + printf("[FAIL] dyld_process_info_notify missing argument\n"); + exit(0); + } + const char* testProgPath = argv[1]; + + dispatch_async(dispatch_get_main_queue(), ^{ + struct task_and_pid child; + + // test 1) launch test program suspended in same arch as this program + child = launchTest(testProgPath, "", false, true); + if ( ! monitor(child, false, false) ) { + printf("[FAIL] dyld_process_info_notify launch suspended missed some notifications\n"); + killTest(child); + exit(0); + } + killTest(child); + + // test 2) launch test program in same arch as this program where it sleeps itself + child = launchTest(testProgPath, "suspend-in-main", false, false); + validateMaxNotifies(child); + if ( ! monitor(child, false, true) ) { + printf("[FAIL] dyld_process_info_notify launch suspend-in-main missed some notifications\n"); + killTest(child); + exit(0); + } + killTest(child); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + // test 3) launch test program suspended in opposite arch as this program + child = launchTest(testProgPath, "", true, true); + if ( ! monitor(child, false, false) ) { + printf("[FAIL] dyld_process_info_notify launch suspended other arch missed some notifications\n"); + killTest(child); + exit(0); + } + killTest(child); + + // test 4) launch test program in opposite arch as this program where it sleeps itself + child = launchTest(testProgPath, "suspend-in-main", true, false); + if ( ! monitor(child, false, true) ) { + printf("[FAIL] dyld_process_info_notify launch other arch suspend-in-main missed some notifications\n"); + killTest(child); + exit(0); + } + killTest(child); +#endif + + // test 5) launch test program where we disconnect from it after first dlopen + child = launchTest(testProgPath, "", false, true); + if ( ! monitor(child, true, false) ) { + printf("[FAIL] dyld_process_info_notify connect/disconnect missed some notifications\n"); + killTest(child); + exit(0); + } + killTest(child); + + printf("[PASS] dyld_process_info_notify\n"); + exit(0); + }); + + dispatch_main(); +} diff --git a/dyld/testing/test-cases/dyld_process_info_notify.dtest/target.c b/dyld/testing/test-cases/dyld_process_info_notify.dtest/target.c new file mode 100644 index 0000000..55fd668 --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info_notify.dtest/target.c @@ -0,0 +1,23 @@ +#include +#include +#include +#include +#include +#include +#include + + + +int main(int argc, const char* argv[]) +{ + if ( (argc > 1) && (strcmp(argv[1], "suspend-in-main") == 0) ) + (void)kill(getpid(), SIGSTOP); + + for (int i=0; i < 3; ++i) { + void* h = dlopen("./libfoo.dylib", 0); + dlclose(h); + } + + return 0; +} + diff --git a/dyld/testing/test-cases/dyld_process_info_unload.dtest/foo.c b/dyld/testing/test-cases/dyld_process_info_unload.dtest/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info_unload.dtest/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/dyld/testing/test-cases/dyld_process_info_unload.dtest/main.c b/dyld/testing/test-cases/dyld_process_info_unload.dtest/main.c new file mode 100644 index 0000000..6954ad1 --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info_unload.dtest/main.c @@ -0,0 +1,143 @@ + +// BUILD: $CC target.c -o $BUILD_DIR/target.exe +// BUILD: $CC foo.c -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dyld_process_info_unload.exe +// BUILD: $TASK_FOR_PID_ENABLE $BUILD_DIR/dyld_process_info_unload.exe + +// RUN: $SUDO ./dyld_process_info_unload.exe $RUN_DIR/target.exe + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern char** environ; + +#if __x86_64__ + cpu_type_t otherArch[] = { CPU_TYPE_I386 }; +#elif __i386__ + cpu_type_t otherArch[] = { CPU_TYPE_X86_64 }; +#elif __arm64__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM }; +#elif __arm__ + cpu_type_t otherArch[] = { CPU_TYPE_ARM64 }; +#endif + +struct task_and_pid { + pid_t pid; + task_t task; +}; + +static struct task_and_pid launchTest(const char* testProgPath, bool launchOtherArch, bool launchSuspended) +{ + posix_spawnattr_t attr = 0; + if ( posix_spawnattr_init(&attr) != 0 ) { + printf("[FAIL] dyld_process_info_unload posix_spawnattr_init()\n"); + exit(0); + } + if ( launchSuspended ) { + if ( posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED) != 0 ) { + printf("[FAIL] dyld_process_info_unload POSIX_SPAWN_START_SUSPENDED\n"); + exit(0); + } + } + if ( launchOtherArch ) { + size_t copied; + if ( posix_spawnattr_setbinpref_np(&attr, 1, otherArch, &copied) != 0 ) { + printf("[FAIL] dyld_process_info_unload posix_spawnattr_setbinpref_np()\n"); + exit(0); + } + } + + struct task_and_pid child; + const char* argv[] = { testProgPath, NULL }; + int psResult = posix_spawn(&child.pid, testProgPath, NULL, &attr, (char**)argv, environ); + if ( psResult != 0 ) { + printf("[FAIL] dyld_process_info_unload posix_spawn(%s) failed, err=%d\n", testProgPath, psResult); + exit(0); + } + if (posix_spawnattr_destroy(&attr) != 0) { + printf("[FAIL] dyld_process_info_unload posix_spawnattr_destroy()\n"); + exit(0); + } + + if ( task_for_pid(mach_task_self(), child.pid, &child.task) != KERN_SUCCESS ) { + printf("[FAIL] dyld_process_info_unload task_for_pid()\n"); + kill(child.pid, SIGKILL); + exit(0); + } + + // wait until process is up and has suspended itself + struct task_basic_info info; + do { + unsigned count = TASK_BASIC_INFO_COUNT; + kern_return_t kr = task_info(child.task, TASK_BASIC_INFO, (task_info_t)&info, &count); + sleep(1); + } while ( info.suspend_count == 0 ); + + return child; +} + +static void killTest(struct task_and_pid tp) { + int r = kill(tp.pid, SIGKILL); + waitpid(tp.pid, &r, 0); +} + +static bool alwaysGetImages(struct task_and_pid tp, bool launchedSuspended) +{ + int failCount = 0; + for (int i=0; i < 100; ++i ) { + kern_return_t result; + dyld_process_info info = _dyld_process_info_create(tp.task, 0, &result); + //fprintf(stderr, "info=%p, result=%08X\n", info, result); + if ( i == 0 ) + (void)kill(tp.pid, SIGCONT); + if ( info == NULL ) { + failCount++; + //fprintf(stderr, "info=%p, result=%08X\n", info, result); + } + else { + usleep(100); + _dyld_process_info_release(info); + } + } + // ideally the fail count would be zero. But the target is dlopen/dlclosing in a tight loop, so there may never be a stable set of images. + // The real bug driving this test case was _dyld_process_info_create() crashing when the the image list changed too fast. + // The important thing is to not crash. Getting NULL back is ok. + if ( failCount > 50 ) { + printf("[FAIL] dyld_process_info_unload %d out of 100 calls to _dyld_process_info_create() failed\n", failCount); + return false; + } + return true; +} + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] dyld_process_info_unload\n"); + + if ( argc < 2 ) { + printf("[FAIL] dyld_process_info_unload missing argument\n"); + exit(0); + } + const char* testProgPath = argv[1]; + struct task_and_pid child; + + // launch test program suspended + child = launchTest(testProgPath, false, true); + if ( ! alwaysGetImages(child, true) ) { + killTest(child); + exit(0); + } + killTest(child); + + printf("[PASS] dyld_process_info_unload\n"); + return 0; +} diff --git a/dyld/testing/test-cases/dyld_process_info_unload.dtest/target.c b/dyld/testing/test-cases/dyld_process_info_unload.dtest/target.c new file mode 100644 index 0000000..be923e7 --- /dev/null +++ b/dyld/testing/test-cases/dyld_process_info_unload.dtest/target.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include + + + +int main(int argc, const char* argv[]) +{ + //fprintf(stderr, "target starting\n"); + usleep(1000); + // load and unload in a loop + for (int i=1; i < 10000; ++i) { + void* h = dlopen("./libfoo.dylib", 0); + usleep(100000/(i*100)); + dlclose(h); + } + //fprintf(stderr, "target done\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/dylib-re-export-old-format.dtest/bar.c b/dyld/testing/test-cases/dylib-re-export-old-format.dtest/bar.c new file mode 100644 index 0000000..c78b45d --- /dev/null +++ b/dyld/testing/test-cases/dylib-re-export-old-format.dtest/bar.c @@ -0,0 +1,5 @@ + +int bar() { + return 42; +} + diff --git a/dyld/testing/test-cases/dylib-re-export-old-format.dtest/foo.c b/dyld/testing/test-cases/dylib-re-export-old-format.dtest/foo.c new file mode 100644 index 0000000..54c3a28 --- /dev/null +++ b/dyld/testing/test-cases/dylib-re-export-old-format.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 1; + diff --git a/dyld/testing/test-cases/dylib-re-export-old-format.dtest/main.c b/dyld/testing/test-cases/dylib-re-export-old-format.dtest/main.c new file mode 100644 index 0000000..06e73a7 --- /dev/null +++ b/dyld/testing/test-cases/dylib-re-export-old-format.dtest/main.c @@ -0,0 +1,26 @@ +// BUILD_ONLY: MacOSX +// BUILD_MIN_OS: 10.5 +// BUILD: $CC bar.c -dynamiclib -install_name $RUN_DIR/libbar.dylib -o $BUILD_DIR/libbar.dylib +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR + +// RUN: ./dylib-re-export.exe + + +#include + +extern int bar(); + + +int main() +{ + printf("[BEGIN] dylib-re-export\n"); + if ( bar() == 42 ) + printf("[PASS] dylib-re-export\n"); + else + printf("[FAIL] dylib-re-export, wrong value\n"); + + return 0; +} + + diff --git a/dyld/testing/test-cases/dylib-re-export.dtest/bar.c b/dyld/testing/test-cases/dylib-re-export.dtest/bar.c new file mode 100644 index 0000000..c78b45d --- /dev/null +++ b/dyld/testing/test-cases/dylib-re-export.dtest/bar.c @@ -0,0 +1,5 @@ + +int bar() { + return 42; +} + diff --git a/dyld/testing/test-cases/dylib-re-export.dtest/foo.c b/dyld/testing/test-cases/dylib-re-export.dtest/foo.c new file mode 100644 index 0000000..54c3a28 --- /dev/null +++ b/dyld/testing/test-cases/dylib-re-export.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 1; + diff --git a/dyld/testing/test-cases/dylib-re-export.dtest/main.c b/dyld/testing/test-cases/dylib-re-export.dtest/main.c new file mode 100644 index 0000000..879b0b6 --- /dev/null +++ b/dyld/testing/test-cases/dylib-re-export.dtest/main.c @@ -0,0 +1,26 @@ + + +// BUILD: $CC bar.c -dynamiclib -o $BUILD_DIR/libbar.dylib -install_name $RUN_DIR/libbar.dylib +// BUILD: $CC foo.c -dynamiclib $BUILD_DIR/libbar.dylib -sub_library libbar -install_name $RUN_DIR/libfoo.dylib -o $BUILD_DIR/libfoo.dylib +// BUILD: $CC main.c -o $BUILD_DIR/dylib-re-export.exe $BUILD_DIR/libfoo.dylib -L$BUILD_DIR + +// RUN: ./dylib-re-export.exe + + +#include + +extern int bar(); + + +int main() +{ + printf("[BEGIN] dylib-re-export\n"); + if ( bar() == 42 ) + printf("[PASS] dylib-re-export\n"); + else + printf("[FAIL] dylib-re-export, wrong value\n"); + + return 0; +} + + diff --git a/dyld/testing/test-cases/dylib-static-link.dtest/foo.c b/dyld/testing/test-cases/dylib-static-link.dtest/foo.c new file mode 100644 index 0000000..c42b10f --- /dev/null +++ b/dyld/testing/test-cases/dylib-static-link.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 42; + diff --git a/dyld/testing/test-cases/dylib-static-link.dtest/missing.c b/dyld/testing/test-cases/dylib-static-link.dtest/missing.c new file mode 100644 index 0000000..6a4a62d --- /dev/null +++ b/dyld/testing/test-cases/dylib-static-link.dtest/missing.c @@ -0,0 +1,12 @@ +#include +#include + +int main() +{ + printf("[BEGIN] dylib-static-link missing\n"); + printf("[FAIL] dylib-static-link missing, program should not have launched\n"); + + return 0; +} + + diff --git a/dyld/testing/test-cases/dylib-static-link.dtest/present.c b/dyld/testing/test-cases/dylib-static-link.dtest/present.c new file mode 100644 index 0000000..4e7aa2d --- /dev/null +++ b/dyld/testing/test-cases/dylib-static-link.dtest/present.c @@ -0,0 +1,28 @@ + + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib +// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-present.exe +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib +// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-missing.exe + +// RUN: ./dylib-static-present.exe +// RUN: NOCR_TEST_NAME="dylib-static-link missing" $REQUIRE_CRASH ./dylib-static-missing.exe + + +#include + +extern int foo; + + +int main() +{ + printf("[BEGIN] dylib-static-link present\n"); + if ( foo == 42 ) + printf("[PASS] dylib-static-link present\n"); + else + printf("[FAIL] dylib-static-link present, wrong value\n"); + + return 0; +} + + diff --git a/dyld/testing/test-cases/dylib-static-weak-link.dtest/foo.c b/dyld/testing/test-cases/dylib-static-weak-link.dtest/foo.c new file mode 100644 index 0000000..c42b10f --- /dev/null +++ b/dyld/testing/test-cases/dylib-static-weak-link.dtest/foo.c @@ -0,0 +1,3 @@ + +int foo = 42; + diff --git a/dyld/testing/test-cases/dylib-static-weak-link.dtest/missing.c b/dyld/testing/test-cases/dylib-static-weak-link.dtest/missing.c new file mode 100644 index 0000000..c633047 --- /dev/null +++ b/dyld/testing/test-cases/dylib-static-weak-link.dtest/missing.c @@ -0,0 +1,19 @@ +#include +#include + +extern int foo __attribute__((weak_import)); + + +int main() +{ + printf("[BEGIN] dylib-static-weak-link missing\n"); + // dylib won't be found at runtime, so &foo should be NULL + if ( &foo == NULL ) + printf("[PASS] dylib-static-weak-link missing\n"); + else + printf("[FAIL] dylib-static-weak-link missing, &foo != NULL\n"); + + return 0; +} + + diff --git a/dyld/testing/test-cases/dylib-static-weak-link.dtest/present.c b/dyld/testing/test-cases/dylib-static-weak-link.dtest/present.c new file mode 100644 index 0000000..1f4a6e2 --- /dev/null +++ b/dyld/testing/test-cases/dylib-static-weak-link.dtest/present.c @@ -0,0 +1,33 @@ +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name libfoo.dylib +// BUILD: $CC present.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/dylib-static-weak-present.exe +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name libfoomissing.dylib +// BUILD: $CC missing.c $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/dylib-static-weak-missing.exe + +// RUN: ./dylib-static-weak-present.exe +// RUN: ./dylib-static-weak-missing.exe + + +#include +#include + +extern int foo __attribute__((weak_import)); + + +int main() +{ + printf("[BEGIN] dylib-static-weak-link present\n"); + // dylib will be found at runtime, so &foo should never be NULL + if ( &foo != NULL ) { + if ( foo == 42 ) + printf("[PASS] dylib-static-weak-link present\n"); + else + printf("[FAIL] dylib-static-weak-link present, wrong value\n"); + } + else { + printf("[FAIL] dylib-static-weak-link present, &foo == NULL\n"); + } + + return 0; +} + + diff --git a/dyld/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/foo.c b/dyld/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/foo.c new file mode 100644 index 0000000..b6dfea2 --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return VALUE; +} + diff --git a/dyld/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c b/dyld/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c new file mode 100644 index 0000000..c26697b --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_FALLBACK_FRAMEWORK_PATH.dtest/main.c @@ -0,0 +1,25 @@ + +// BUILD: mkdir -p $TEMP_DIR/Foo.framework $BUILD_DIR/FallbackFrameworks/Foo.framework +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=1 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/FallbackFrameworks/Foo.framework/Foo -install_name $RUN_DIR/Foo.framework/Foo -DVALUE=42 +// BUILD: $CC main.c -o $BUILD_DIR/main.exe $TEMP_DIR/Foo.framework/Foo +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe + +// RUN: DYLD_FALLBACK_FRAMEWORK_PATH=$RUN_DIR/FallbackFrameworks/ ./main.exe + +#include +#include + +extern int foo(); + +int main() +{ + printf("[BEGIN] env-DYLD_FALLBACK_FRAMEWORK_PATH\n"); + + if ( foo() == 42 ) + printf("[PASS] env-DYLD_FALLBACK_FRAMEWORK_PATH\n"); + else + printf("[FAIL] env-DYLD_FALLBACK_FRAMEWORK_PATH\n"); + + return 0; +} diff --git a/dyld/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/foo.c b/dyld/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/foo.c new file mode 100644 index 0000000..b6dfea2 --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return VALUE; +} + diff --git a/dyld/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c b/dyld/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c new file mode 100644 index 0000000..1c94836 --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH.dtest/main.c @@ -0,0 +1,25 @@ + +// BUILD: mkdir -p $BUILD_DIR/fallback +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=1 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/fallback/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib -DVALUE=42 +// BUILD: $CC main.c -o $BUILD_DIR/main.exe $TEMP_DIR/libfoo.dylib +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe + +// RUN: DYLD_FALLBACK_LIBRARY_PATH=$RUN_DIR/fallback/ ./main.exe + +#include + +extern int foo(); + +int main() +{ + printf("[BEGIN] env-DYLD_FALLBACK_LIBRARY_PATH\n"); + + if ( foo() == 42 ) + printf("[PASS] env-DYLD_FALLBACK_LIBRARY_PATH\n"); + else + printf("[FAIL] env-DYLD_FALLBACK_LIBRARY_PATH\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/foo.c b/dyld/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/foo.c new file mode 100644 index 0000000..b6dfea2 --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return VALUE; +} + diff --git a/dyld/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c b/dyld/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c new file mode 100644 index 0000000..915729f --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_FRAMEWORK_PATH.dtest/main.c @@ -0,0 +1,29 @@ + +// BUILD: mkdir -p $BUILD_DIR/Frameworks/Foo.framework $BUILD_DIR/Frameworks-alt/Foo.framework +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=1 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/Frameworks-alt/Foo.framework/Foo -install_name $RUN_DIR/Frameworks/Foo.framework/Foo -DVALUE=42 +// BUILD: $CC main.c -o $BUILD_DIR/main.exe $BUILD_DIR/Frameworks/Foo.framework/Foo +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe + +// RUN: ./main.exe +// RUN: DYLD_FRAMEWORK_PATH=$RUN_DIR/Frameworks-alt/ ./main.exe + +#include +#include + +extern int foo(); + +int main() +{ + int expected = (getenv("DYLD_FRAMEWORK_PATH") != NULL) ? 42 : 1; + + printf("[BEGIN] env-DYLD_FRAMEWORK_PATH, expect %d\n", expected); + + if ( foo() == expected ) + printf("[PASS] env-DYLD_FRAMEWORK_PATH\n"); + else + printf("[FAIL] env-DYLD_FRAMEWORK_PATH\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/foo.c b/dyld/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/foo.c new file mode 100644 index 0000000..b6dfea2 --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return VALUE; +} + diff --git a/dyld/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c b/dyld/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c new file mode 100644 index 0000000..69d199e --- /dev/null +++ b/dyld/testing/test-cases/env-DYLD_LIBRARY_PATH.dtest/main.c @@ -0,0 +1,29 @@ + +// BUILD: mkdir -p $BUILD_DIR/door1 $BUILD_DIR/door2 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door1/libfoo.dylib -install_name $RUN_DIR/door1/libfoo.dylib -DVALUE=1 +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/door2/libfoo.dylib -install_name $RUN_DIR/door2/libfoo.dylib -DVALUE=42 +// BUILD: $CC main.c -o $BUILD_DIR/main.exe $BUILD_DIR/door1/libfoo.dylib +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/main.exe + +// RUN: ./main.exe +// RUN: DYLD_LIBRARY_PATH=$RUN_DIR/door2/ ./main.exe + +#include +#include + +extern int foo(); + +int main() +{ + int expected = (getenv("DYLD_LIBRARY_PATH") != NULL) ? 42 : 1; + + printf("[BEGIN] env-DYLD_LIBRARY_PATH, expect %d\n", expected); + + if ( foo() == expected ) + printf("[PASS] env-DYLD_LIBRARY_PATH\n"); + else + printf("[FAIL] env-DYLD_LIBRARY_PATH\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/flat-namespace.dtest/foo.c b/dyld/testing/test-cases/flat-namespace.dtest/foo.c new file mode 100644 index 0000000..20ae519 --- /dev/null +++ b/dyld/testing/test-cases/flat-namespace.dtest/foo.c @@ -0,0 +1,21 @@ + +#include +#include + +char buffer[100000]; +char* p = buffer; + +void* malloc(size_t size) +{ + // bump ptr allocate and fill second half with '#' + char* result = p; + p += size; + memset(result, '#', size); + p = (char*)(((long)p + 15) & (-16)); // 16-byte align next malloc + return result; +} + +void free(void* p) +{ +} + diff --git a/dyld/testing/test-cases/flat-namespace.dtest/main.c b/dyld/testing/test-cases/flat-namespace.dtest/main.c new file mode 100644 index 0000000..37c99fc --- /dev/null +++ b/dyld/testing/test-cases/flat-namespace.dtest/main.c @@ -0,0 +1,28 @@ +// BUILD_ONLY: MacOSX + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/flat-namespace.exe -flat_namespace + +// RUN: ./flat-namespace.exe + + + +#include +#include +#include + +int main() +{ + printf("[BEGIN] flat-namespace\n"); + + // check that the malloc in libfoo.dylib was used by looking at the content the allocated buffer + // strncmp is tricky for flat namespace because it is re-exporte and renamed + char* p1 = malloc(10); + if ( strncmp(p1, "##########", 10) != 0 ) { + printf("[FAIL] malloc() from main executable not interposed\n"); + return 0; + } + + printf("[PASS] flat-namespace\n"); + return 0; +} diff --git a/dyld/testing/test-cases/interpose-malloc.dtest/foo.c b/dyld/testing/test-cases/interpose-malloc.dtest/foo.c new file mode 100644 index 0000000..d539c96 --- /dev/null +++ b/dyld/testing/test-cases/interpose-malloc.dtest/foo.c @@ -0,0 +1,15 @@ + +#include + +void* myalloc1(size_t sz) +{ + return malloc(sz); +} + +void* (*pMalloc)(size_t) = &malloc; + +void* myalloc2(size_t sz) +{ + return (*pMalloc)(sz); +} + diff --git a/dyld/testing/test-cases/interpose-malloc.dtest/interposer.c b/dyld/testing/test-cases/interpose-malloc.dtest/interposer.c new file mode 100644 index 0000000..ceac979 --- /dev/null +++ b/dyld/testing/test-cases/interpose-malloc.dtest/interposer.c @@ -0,0 +1,25 @@ +#include +#include +#include + + +char buffer[100000]; +char* p = buffer; + +void* mymalloc(size_t size) +{ + // bump ptr allocate twice the size and fill second half with '#' + char* result = p; + p += size; + memset(p, '#', size); + p += size; + p = (char*)(((long)p + 15) & (-16)); // 16-byte align next malloc + return result; +} + +void myfree(void* p) +{ +} + +DYLD_INTERPOSE(mymalloc, malloc) +DYLD_INTERPOSE(myfree, free) diff --git a/dyld/testing/test-cases/interpose-malloc.dtest/main.c b/dyld/testing/test-cases/interpose-malloc.dtest/main.c new file mode 100644 index 0000000..9ef1842 --- /dev/null +++ b/dyld/testing/test-cases/interpose-malloc.dtest/main.c @@ -0,0 +1,48 @@ +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/interpose-malloc.exe +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-malloc.exe +// BUILD: $CC interposer.c -dynamiclib -o $BUILD_DIR/libmyalloc.dylib -install_name libmyalloc.dylib + +// RUN: DYLD_INSERT_LIBRARIES=libmyalloc.dylib ./interpose-malloc.exe + + + +#include +#include +#include + +extern void* myalloc1(size_t); +extern void* myalloc2(size_t); + +int main() +{ + printf("[BEGIN] interpose-malloc\n"); + + char* p1 = malloc(10); + if ( strncmp(p1+10, "##########", 10) != 0 ) { + printf("[FAIL] interpose-malloc malloc() from main executable not interposed\n"); + return 0; + } + + void* p2 = myalloc1(6); + if ( strncmp(p2+6, "######", 6) != 0 ) { + printf("[FAIL] interpose-malloc myalloc1() from libfoo.dylib not interposed\n"); + return 0; + } + + void* p3 = myalloc2(10); + if ( strncmp(p3+10, "##########", 10) != 0 ) { + printf("[FAIL] interpose-malloc myalloc2() from libfoo.dylib not interposed\n"); + return 0; + } + + void* p4 = strdup("hello"); + if ( strncmp(p4+6, "#######", 6) != 0 ) { + printf("[FAIL] interpose-malloc malloc() from strdup not interposed\n"); + return 0; + } + + //printf("%p %p %p %p\n", p1, p2, p3, p4); + printf("[PASS] interpose-malloc\n"); + return 0; +} diff --git a/dyld/testing/test-cases/interpose-weak.dtest/foo.c b/dyld/testing/test-cases/interpose-weak.dtest/foo.c new file mode 100644 index 0000000..ae32a9f --- /dev/null +++ b/dyld/testing/test-cases/interpose-weak.dtest/foo.c @@ -0,0 +1,12 @@ + + + +int foo1() { return 1; } + +int foo2() { return 2; } + +#ifndef NO_FOO34 + int foo3() { return 3; } + int foo4() { return 4; } +#endif + diff --git a/dyld/testing/test-cases/interpose-weak.dtest/interposer.c b/dyld/testing/test-cases/interpose-weak.dtest/interposer.c new file mode 100644 index 0000000..cf92d90 --- /dev/null +++ b/dyld/testing/test-cases/interpose-weak.dtest/interposer.c @@ -0,0 +1,15 @@ + +#include + +extern int foo2(); +extern int foo4() __attribute__((weak_import)); + + + +int myfoo2() { return 12; } +int myfoo4() { return 14; } + + + +DYLD_INTERPOSE(myfoo2, foo2) +DYLD_INTERPOSE(myfoo4, foo4) diff --git a/dyld/testing/test-cases/interpose-weak.dtest/main.c b/dyld/testing/test-cases/interpose-weak.dtest/main.c new file mode 100644 index 0000000..5248427 --- /dev/null +++ b/dyld/testing/test-cases/interpose-weak.dtest/main.c @@ -0,0 +1,72 @@ +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/interpose-weak-present.exe +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-weak-present.exe +// BUILD: $CC interposer.c -dynamiclib $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/libinterposer.dylib -install_name libinterposer.dylib + +// BUILD: $CC foo.c -dynamiclib -o $TEMP_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib +// BUILD: $CC foo.c -DNO_FOO34 -dynamiclib -o $BUILD_DIR/libfoo2.dylib -install_name $RUN_DIR/libfoo2.dylib +// BUILD: $CC main.c -DNO_FOO34 $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/interpose-weak-missing.exe +// BUILD: $DYLD_ENV_VARS_ENABLE $BUILD_DIR/interpose-weak-missing.exe +// BUILD: $CC interposer.c -dynamiclib $TEMP_DIR/libfoo2.dylib -o $BUILD_DIR/libinterposer2.dylib -install_name libinterposer.dylib + + +// RUN: DYLD_INSERT_LIBRARIES=libinterposer.dylib ./interpose-weak-present.exe +// RUN: DYLD_INSERT_LIBRARIES=libinterposer2.dylib ./interpose-weak-missing.exe + + + +#include +#include +#include + + +extern int foo1(); +extern int foo2(); +extern int foo3() __attribute__((weak_import)); +extern int foo4() __attribute__((weak_import)); + +#ifndef NO_FOO34 + #define MODE "present" +#else + #define MODE "missing" +#endif + +int main() +{ + printf("[BEGIN] interpose-weak-" MODE "\n"); + + if ( foo1() != 1 ) { + printf("[FAIL] interpose-weak-" MODE ", foo1() != 1\n"); + return 0; + } + + if ( foo2() != 12 ) { + printf("[FAIL] interpose-weak-" MODE ", foo2() != 12\n"); + return 0; + } + +#ifndef NO_FOO34 + if ( foo3() != 3 ) { + printf("[FAIL] interpose-weak-" MODE ", foo3() != 3\n"); + return 0; + } + + if ( foo4() != 14 ) { + printf("[FAIL] interpose-weak-" MODE ", foo4() != 14\n"); + return 0; + } +#else + if ( &foo3 != NULL ) { + printf("[FAIL] interpose-weak-" MODE ", &foo3 != NULL\n"); + return 0; + } + + if ( &foo4 != NULL ) { + printf("[FAIL] interpose-weak-" MODE ", &foo4 != NULL\n"); + return 0; + } +#endif + + printf("[PASS] interpose-weak-" MODE "\n"); + return 0; +} diff --git a/dyld/testing/test-cases/operator-new.dtest/main.cxx b/dyld/testing/test-cases/operator-new.dtest/main.cxx new file mode 100644 index 0000000..d985e8f --- /dev/null +++ b/dyld/testing/test-cases/operator-new.dtest/main.cxx @@ -0,0 +1,37 @@ + +// BUILD: $CXX main.cxx -o $BUILD_DIR/operator-new.exe + +// RUN: ./operator-new.exe + +#include +#include + + + +// +// This test case verifies that calling operator new[] in libstdc++.dylib +// will turn around and call operator new in this main exectuable +// + +static void* ptr; + +void* operator new(size_t s) throw (std::bad_alloc) +{ + ptr = malloc(s); + return ptr; +} + +int main() +{ + printf("[BEGIN] operator-new\n"); + + char* stuff = new char[24]; + if ( (void*)stuff == ptr ) + printf("[PASS] operator-new\n"); + else + printf("[FAIL] operator-new\n"); + + return 0; +} + + diff --git a/dyld/testing/test-cases/restrict-search.dtest/foo.c b/dyld/testing/test-cases/restrict-search.dtest/foo.c new file mode 100644 index 0000000..c8e9924 --- /dev/null +++ b/dyld/testing/test-cases/restrict-search.dtest/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 10; +} + diff --git a/dyld/testing/test-cases/restrict-search.dtest/main.c b/dyld/testing/test-cases/restrict-search.dtest/main.c new file mode 100644 index 0000000..e8355ca --- /dev/null +++ b/dyld/testing/test-cases/restrict-search.dtest/main.c @@ -0,0 +1,54 @@ +// BUILD_ONLY: MacOSX + +// BUILD: mkdir $BUILD_DIR/lc +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/lc/libfoo.dylib -install_name /blah/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-find -DSHOULD_BE_FOUND=1 +// BUILD: $CC main.c $BUILD_DIR/lc/libfoo.dylib -o $BUILD_DIR/restrict-search-lc-no-find.exe -Wl,-dyld_env,DYLD_LIBRARY_PATH=@loader_path/lc -DMODE=lc-no-find -sectcreate __RESTRICT __restrict /dev/null +// BUILD: mkdir $BUILD_DIR/rpath +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/rpath/libfoo.dylib -install_name @rpath/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-find -DSHOULD_BE_FOUND=1 +// BUILD: $CC main.c $BUILD_DIR/rpath/libfoo.dylib -o $BUILD_DIR/restrict-search-rpath-no-find.exe -Wl,-rpath,@loader_path/rpath/ -DMODE=rpath-no-find -sectcreate __RESTRICT __restrict /dev/null + + +// RUN: ./restrict-search-lc-find.exe +// RUN: ./restrict-search-lc-no-find.exe +// RUN: ./restrict-search-rpath-find.exe +// RUN: ./restrict-search-rpath-no-find.exe + + + +#include +#include + +// Two ways to find libfoo.dylb: @rpath or DYLD_LIBRARY_PATH (set via LC_DYLD_ENVIRONMENT) +// These should work for non-restrictured programs. +// These should fail for restricted programs. +// By weak linking we can test if libfoo.dylib was found or not. + + +extern int foo() __attribute__((weak_import)); + + +#define STRINGIFY2( x) #x +#define STRINGIFY(x) STRINGIFY2(x) + + +int main(int argc, const char* argv[]) +{ + printf("[BEGIN] restrict-search %s\n", STRINGIFY(MODE)); +#if SHOULD_BE_FOUND + if ( &foo == NULL ) + printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE)); + else + printf("[PASS] restrict-search %s\n", STRINGIFY(MODE)); +#else + // dylib won't be found at runtime, so &foo should be NULL + if ( &foo == NULL ) + printf("[PASS] restrict-search %s\n", STRINGIFY(MODE)); + else + printf("[FAIL] restrict-search %s\n", STRINGIFY(MODE)); +#endif + + return 0; +} + diff --git a/dyld/testing/test-cases/shared_cache_range.dtest/main.c b/dyld/testing/test-cases/shared_cache_range.dtest/main.c new file mode 100644 index 0000000..693de54 --- /dev/null +++ b/dyld/testing/test-cases/shared_cache_range.dtest/main.c @@ -0,0 +1,59 @@ + +// BUILD: $CC main.c -o $BUILD_DIR/shared_cache_range.exe + +// RUN: ./shared_cache_range.exe + +#include +#include +#include +#include +#include + + +int main() +{ + printf("[BEGIN] shared_cache_range\n"); + + // see if image containing malloc is in the dyld cache + Dl_info info; + if ( dladdr(&malloc, &info) == 0 ) { + printf("[FAIL] shared_cache_range: dladdr(&malloc, xx) failed"); + return 0; + } + const struct mach_header* mh = (struct mach_header*)info.dli_fbase; + printf("image with malloc=%p\n", mh); + if ( mh == NULL ) { + printf("[FAIL] shared_cache_range: dladdr(&malloc, xx) => dli_fbase==NULL"); + return 0; + } + bool haveSharedCache = (mh->flags & 0x80000000); + printf("haveSharedCache=%d\n", haveSharedCache); + + size_t cacheLen; + const void* cacheStart = _dyld_get_shared_cache_range(&cacheLen); + + if ( haveSharedCache ) { + if ( cacheStart == NULL ) { + printf("[FAIL] _dyld_get_shared_cache_range() returned NULL even though we have a cache\n"); + return 0; + } + printf("shared cache start=%p, len=0x%0lX\n", cacheStart, cacheLen); + const void* cacheEnd = (char*)cacheStart + cacheLen; + + // verify malloc is in shared cache + if ( ((void*)&malloc < cacheStart) || ((void*)&malloc > cacheEnd) ) { + printf("[FAIL] shared_cache_range: malloc is outside range of cache\n"); + return 0; + } + } + else { + if ( cacheStart != NULL ) { + printf("[FAIL] _dyld_get_shared_cache_range() returned non-NULL even though we don't have a cache\n"); + return 0; + } + } + + printf("[PASS] shared_cache_range\n"); + return 0; +} + diff --git a/dyld/testing/test-cases/thread-local-cleanup.dtest/foo.c b/dyld/testing/test-cases/thread-local-cleanup.dtest/foo.c new file mode 100644 index 0000000..ac354a5 --- /dev/null +++ b/dyld/testing/test-cases/thread-local-cleanup.dtest/foo.c @@ -0,0 +1,6 @@ + +__thread int a; +__thread int b = 5; + + + diff --git a/dyld/testing/test-cases/thread-local-cleanup.dtest/main.c b/dyld/testing/test-cases/thread-local-cleanup.dtest/main.c new file mode 100644 index 0000000..e301792 --- /dev/null +++ b/dyld/testing/test-cases/thread-local-cleanup.dtest/main.c @@ -0,0 +1,36 @@ + +// BUILD: $CC foo.c -dynamiclib -install_name $RUN_DIR/libtlv.dylib -o $BUILD_DIR/libtlv.dylib +// BUILD: $CC main.c -DRUN_DIR="$RUN_DIR" -o $BUILD_DIR/thread-local-cleanup.exe + +// RUN: ./thread-local-cleanup.exe + +#include +#include + + + + + +int main() +{ + printf("[BEGIN] thread-local-cleanup\n"); + + for (int i=0; i < 1000; ++i) { + void* handle = dlopen(RUN_DIR "/libtlv.dylib", RTLD_FIRST); + if ( handle == NULL ) { + printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror()); + return 0; + } + + int result = dlclose(handle); + if ( result != 0 ) { + printf("[FAIL] thread-local-cleanup: iteration %d %s\n", i, dlerror()); + return 0; + } + } + + printf("[PASS] thread-local-cleanup\n"); + + return 0; +} + diff --git a/dyld/testing/test-cases/thread-local-variables.dtest/foo.c b/dyld/testing/test-cases/thread-local-variables.dtest/foo.c new file mode 100644 index 0000000..60bbbb4 --- /dev/null +++ b/dyld/testing/test-cases/thread-local-variables.dtest/foo.c @@ -0,0 +1,8 @@ + + + +__thread int a; +__thread int b = 5; + + + diff --git a/dyld/testing/test-cases/thread-local-variables.dtest/main.c b/dyld/testing/test-cases/thread-local-variables.dtest/main.c new file mode 100644 index 0000000..d7c73b2 --- /dev/null +++ b/dyld/testing/test-cases/thread-local-variables.dtest/main.c @@ -0,0 +1,115 @@ + + +// BUILD: $CC foo.c -dynamiclib -o $BUILD_DIR/libfoo.dylib -install_name $RUN_DIR/libfoo.dylib +// BUILD: $CC main.c $BUILD_DIR/libfoo.dylib -o $BUILD_DIR/thread-local-variables.exe + + +// RUN: ./thread-local-variables.exe + + +#include +#include +#include +#include +#include +#include + + +extern __thread int a; +extern __thread int b; +static __thread int c; +static __thread int d = 5; + + +static int* sAddr1[4]; +static int* sAddr2[4]; +static int* sAddr3[4]; + +static pthread_t sWorker1; +static pthread_t sWorker2; + + +// verify all initial TLV values are correct +static void checkValues() +{ + assert(a == 0); + assert(b == 5); + assert(c == 0); + assert(d == 5); +} + + +// return addresses of all TLVs +static void getAddresses(int* array[]) +{ + array[0] = &a; + array[1] = &b; + array[2] = &c; + array[3] = &d; +} + +static void* work2(void* arg) +{ + checkValues(); + getAddresses(sAddr2); + + return NULL; +} + +static void* work1(void* arg) +{ + checkValues(); + + if ( pthread_create(&sWorker2, NULL, work2, NULL) != 0 ) { + printf("[FAIL] thread-local-variables, pthread_create\n"); + exit(0); + } + void* dummy; + pthread_join(sWorker2, &dummy); + + getAddresses(sAddr1); + + return NULL; +} + +static bool someMatch(int* t1, int* t2, int* t3) +{ + if ( t1 == t2 ) + return true; + if ( t1 == t3 ) + return true; + if ( t2 == t3 ) + return true; + return false; +} + +int main() +{ + printf("[BEGIN] thread-local-variables\n"); + + checkValues(); + + if ( pthread_create(&sWorker1, NULL, work1, NULL) != 0 ) { + printf("[FAIL] thread-local-variables, pthread_create\n"); + return 0; + } + + getAddresses(sAddr3); + + void* dummy; + pthread_join(sWorker1, &dummy); + + // validate each thread had different addresses for all TLVs + if ( someMatch(sAddr1[0], sAddr2[0], sAddr3[0]) ) + printf("[FAIL] thread-local-variables, &a is same across some threads\n"); + else if ( someMatch(sAddr1[1], sAddr2[1], sAddr3[1]) ) + printf("[FAIL] thread-local-variables, &b is same across some threads\n"); + else if ( someMatch(sAddr1[2], sAddr2[2], sAddr3[2]) ) + printf("[FAIL] thread-local-variables, &c is same across some threads\n"); + else if ( someMatch(sAddr1[3], sAddr2[3], sAddr3[3]) ) + printf("[FAIL] thread-local-variables, &d is same across some threads\n"); + else + printf("[PASS] thread-local-variables\n"); + return 0; +} + diff --git a/dyld/unit-tests/bin/build-results-filter.pl b/dyld/unit-tests/bin/build-results-filter.pl new file mode 100755 index 0000000..fa6d106 --- /dev/null +++ b/dyld/unit-tests/bin/build-results-filter.pl @@ -0,0 +1,114 @@ +#!/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++; +} diff --git a/dyld/unit-tests/bin/exit-non-zero-pass.pl b/dyld/unit-tests/bin/exit-non-zero-pass.pl new file mode 100755 index 0000000..7a09a1b --- /dev/null +++ b/dyld/unit-tests/bin/exit-non-zero-pass.pl @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w + +use strict; + +sub PASS +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("PASS \"$format\"\n", @$args); +} + +sub FAIL +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("FAIL \"$format\"\n", @$args); +} + +my $pass_string = shift @ARGV; +my $fail_string = shift @ARGV; + + +# redirect stderr to stdout +open(STDERR, ">/tmp/exit-non-zero.tmp") || die("$!"); +if(0 != system(@ARGV)) +{ + PASS($pass_string); +} +else +{ + FAIL($fail_string); +} +close(STDERR) || die("$!"); +open(OUT, ") +{ + print $_; +} +close(OUT) || die("$!"); +unlink "/tmp/exit-non-zero.tmp"; +exit 0; + diff --git a/dyld/unit-tests/bin/exit-zero-pass.pl b/dyld/unit-tests/bin/exit-zero-pass.pl new file mode 100755 index 0000000..bc0a99d --- /dev/null +++ b/dyld/unit-tests/bin/exit-zero-pass.pl @@ -0,0 +1,43 @@ +#!/usr/bin/perl -w + +use strict; + +sub PASS +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("PASS \"$format\"\n", @$args); +} + +sub FAIL +{ + my ($format, $args) = @_; + if(!defined $args) + { $args = []; } + printf("FAIL \"$format\"\n", @$args); +} + +my $pass_string = shift @ARGV; +my $fail_string = shift @ARGV; + +# redirect stderr to stdout +open(STDERR, ">/tmp/exit-zero-pass.tmp") || die("$!"); +if(0 == system(@ARGV)) +{ + PASS($pass_string); +} +else +{ + FAIL($fail_string); +} +close(STDERR) || die("$!"); +open(OUT, ") +{ + print $_; +} +close(OUT) || die("$!"); +unlink "/tmp/exit-zero-pass.tmp"; +exit 0; + diff --git a/dyld/unit-tests/bin/fail-if-non-zero.pl b/dyld/unit-tests/bin/fail-if-non-zero.pl new file mode 100755 index 0000000..f34f3a1 --- /dev/null +++ b/dyld/unit-tests/bin/fail-if-non-zero.pl @@ -0,0 +1,14 @@ +#!/usr/bin/perl -w + +use strict; + +# first arg is fail string, rest of args are command line to invoke +my $fail_string = shift @ARGV; + +if(system(@ARGV) != 0) +{ + printf("FAIL \"$fail_string\"\n"); +} + +exit 0; + diff --git a/dyld/unit-tests/bin/make-recursive.pl b/dyld/unit-tests/bin/make-recursive.pl new file mode 100755 index 0000000..a441350 --- /dev/null +++ b/dyld/unit-tests/bin/make-recursive.pl @@ -0,0 +1,120 @@ +#!/usr/bin/perl + +use strict; +use Data::Dumper; +use File::Find; +use Cwd qw(realpath); + +my @args = @ARGV; + +my $makefiles = +{ + 'makefile' => undef, + 'Makefile' => undef, +}; + +my $find_opts = +{ + 'wanted' => \&find_callback, +}; + +my $keywords = +{ + 'root' => '', + 'cwd' => '', + 'cmd' => '', + 'exit' => '', + 'stdout' => [], + 'stderr' => [], +}; + +my $keyword; +my $max_keyword_len = 0; +foreach $keyword (keys %$keywords) +{ if($max_keyword_len < length($keyword)) { $max_keyword_len = length($keyword); } } +my $delim = ':'; +$max_keyword_len += length($delim) + length(' '); + +my $last_keyword = ''; + +sub print_line +{ + my ($keyword, $val) = @_; + + if(!exists($$keywords{$keyword})) + { + print STDERR "error: keyword $keyword not in \$keywords set\n"; + exit(1); + } + + my $keyword_len = 0; + + if($keyword ne $last_keyword) + { + print("$keyword"); print($delim); + $keyword_len = length($keyword) + length($delim); + } + if($max_keyword_len > $keyword_len) + { + my $num_spaces = $max_keyword_len - $keyword_len; + print(' ' x $num_spaces); + } + print("$val"); + if(0) + { + $last_keyword = $keyword; + } +} + +my $root = '.'; +$root = &realpath($root); +print_line("root", "$root\n"); + +find($find_opts, $root); + +sub find_callback +{ + if(exists($$makefiles{$_})) + { + my $makefile = $_; + my $reldir = $File::Find::dir; + $reldir =~ s|^$root/||; + + &print_line("cwd", "\$root/$reldir\n"); + my $cmd = [ "make" ]; + + my $arg; foreach $arg (@ARGV) { push @$cmd, $arg; } # better way to do this? + &print_line("cmd", "@$cmd\n"); + + open(SAVEOUT, ">&STDOUT") || die("$!"); + open(SAVEERR, ">&STDERR") || die("$!"); + open(STDOUT, ">/tmp/unit-tests-stdout") || die("$!"); + open(STDERR, ">/tmp/unit-tests-stderr") || die("$!"); + + $ENV{UNIT_TEST_NAME} = $reldir; + my $exit = system(@$cmd); + + close(STDOUT) || die("$!"); + close(STDERR) || die("$!"); + open(STDOUT, ">&SAVEOUT") || die("$!"); + open(STDERR, ">&SAVEERR") || die("$!"); + + &print_line("exit", "$exit\n"); + + open(OUT, ") + { + &print_line("stdout", "$_"); + } + close(OUT) || die("$!"); + unlink("/tmp/unit-tests-stdout"); + + open(ERR, ") + { + &print_line("stderr", "$_"); + } + close(ERR) || die("$!"); + } + unlink("/tmp/unit-tests-stderr"); +} diff --git a/dyld/unit-tests/bin/pass-iff-exit-zero.pl b/dyld/unit-tests/bin/pass-iff-exit-zero.pl new file mode 100755 index 0000000..07854b5 --- /dev/null +++ b/dyld/unit-tests/bin/pass-iff-exit-zero.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl -w + +# +# Usage: +# +# ${PASS_IFF} command +# + +use strict; + +my $test_name = ""; +if ( exists $ENV{UNIT_TEST_NAME} ) { + $test_name = $ENV{UNIT_TEST_NAME}; +} + +if(0 != system(@ARGV)) +{ + printf("FAIL $test_name\n"); + exit 1; +} + +printf("PASS $test_name\n"); +exit 0; diff --git a/dyld/unit-tests/bin/result-filter.pl b/dyld/unit-tests/bin/result-filter.pl new file mode 100755 index 0000000..7d6007f --- /dev/null +++ b/dyld/unit-tests/bin/result-filter.pl @@ -0,0 +1,131 @@ +#!/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 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; + } + my $seen_result = 0; + + #if there was any output to stderr, mark this as a failure + foreach $line (@{$$tbl{stderr}}) + { + printf "%-40s FAIL spurious stderr failure: %s\n", $test_name, $line; + $total_count++; + return; + } + + # scan all stdout looking for lines that start with PASS or FAIL + foreach $line (@{$$tbl{stdout}}) + { + if($line =~ m/^(PASS|XPASS|FAIL|XFAIL).+/) + { + $total_count++; + if($line =~ m/^PASS.+/) + { + $pass_count++; + } + else + { + # only print failure lines + printf "%-40s %s\n", $test_name, $line; + } + $seen_result = 1; + } + } + if(!$seen_result) + { + printf "%-40s AMBIGIOUS missing [X]PASS/[X]FAIL\n", $test_name; + $total_count++; + } +} diff --git a/dyld/unit-tests/build-and-run-iPhoneOS-unit-tests b/dyld/unit-tests/build-and-run-iPhoneOS-unit-tests new file mode 100755 index 0000000..a572912 --- /dev/null +++ b/dyld/unit-tests/build-and-run-iPhoneOS-unit-tests @@ -0,0 +1,14 @@ +#!/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 + diff --git a/dyld/unit-tests/build-iPhoneOS-unit-tests b/dyld/unit-tests/build-iPhoneOS-unit-tests new file mode 100755 index 0000000..f597f13 --- /dev/null +++ b/dyld/unit-tests/build-iPhoneOS-unit-tests @@ -0,0 +1,52 @@ +#!/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 "8.0" +do + for arch in armv7 arm64 + 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 + diff --git a/dyld/unit-tests/include/common.makefile b/dyld/unit-tests/include/common.makefile new file mode 100644 index 0000000..3d6535a --- /dev/null +++ b/dyld/unit-tests/include/common.makefile @@ -0,0 +1,79 @@ +# stuff to include in every test Makefile + +SHELL = /bin/sh + +# set default OS to be current Mac OS X +OS_NAME ?= MacOSX +ifeq "$(OS_NAME)" "iPhoneOS" + OS_VERSION ?= 3.1 + ifeq "$(findstring -$(OS_VERSION)-,-3.0-3.1-3.2-4.0-4.1-4.2-4.3-)" "" + OS_LION_FEATURES = 1 + endif + ARCH ?= armv7 + VALID_ARCHS ?= armv7 +else + OS_VERSION ?= 10.7 + ifeq "$(findstring -$(OS_VERSION)-,-10.4-10.5-10.6-)" "" + OS_LION_FEATURES = 1 + endif + ARCH ?= x86_64 + VALID_ARCHS ?= "i386 x86_64" +endif + +IOSROOT = + +ifeq "$(OS_NAME)" "iPhoneOS" + #IOSROOT = $(shell xcodebuild -version -sdk iphoneos.internal Path) + IOSROOT = /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.Internal.sdk + CC = $(shell xcrun -sdk iphoneos.internal -find cc) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) + CXX = $(shell xcrun -sdk iphoneos.internal -find c++) -arch ${ARCH} -miphoneos-version-min=$(OS_VERSION) -isysroot $(IOSROOT) + LIPO = $(shell xcrun -sdk iphoneos.internal -find lipo) + STRIP = $(shell xcrun -sdk iphoneos.internal -find strip) + INSTALL_NAME_TOOL = $(shell xcrun -sdk iphoneos.internal -find install_name_tool) +else + ifeq "$(OSX_SDK_ROOT)" "" + OSX_SDK_ROOT = $(shell xcodebuild -version -sdk macosx.internal Path) + endif + CC = $(shell xcrun -find cc) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT) + CXX = $(shell xcrun -find c++) -arch ${ARCH} -mmacosx-version-min=$(OS_VERSION) -isysroot $(OSX_SDK_ROOT) + LIPO = $(shell xcrun -find lipo) + STRIP = $(shell xcrun -find strip) + INSTALL_NAME_TOOL = $(shell xcrun -find install_name_tool) +endif + +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 + diff --git a/dyld/unit-tests/include/test.h b/dyld/unit-tests/include/test.h new file mode 100644 index 0000000..faca714 --- /dev/null +++ b/dyld/unit-tests/include/test.h @@ -0,0 +1,35 @@ +#include +#include + +#define DEFINE_TEST_FUNC(name) \ + static \ + inline \ + void \ + name(const char *format, ...) \ + { \ + va_list args; \ + va_start(args, format); \ + common(stdout, #name, format, args); \ + va_end(args); \ + return; \ + } + +static +inline +void +common(FILE *file, const char *prefix, const char *format, va_list args) +{ + fprintf(file, "%s \"", prefix); + vfprintf(file, format, args); + fprintf(file, "\"\n"); // should check for trailing newline + return; +} + +DEFINE_TEST_FUNC(PASS); +DEFINE_TEST_FUNC(XPASS); +DEFINE_TEST_FUNC(FAIL); +DEFINE_TEST_FUNC(XFAIL); + +DEFINE_TEST_FUNC(UNTESTED); +DEFINE_TEST_FUNC(UNSUPPORTED); +DEFINE_TEST_FUNC(UNRESOLVED); diff --git a/dyld/unit-tests/run-all-unit-tests b/dyld/unit-tests/run-all-unit-tests new file mode 100755 index 0000000..397178c --- /dev/null +++ b/dyld/unit-tests/run-all-unit-tests @@ -0,0 +1,42 @@ +#!/bin/sh + +# cd into test-cases directory +cd `echo "$0" | sed 's/run-all-unit-tests/test-cases/'` + +# save crash reporter state +CRSTATE=`defaults read com.apple.CrashReporter DialogType` +defaults write com.apple.CrashReporter DialogType basic + +SDK=`xcodebuild -version -sdk macosx.internal Path` + +# run test targeting different OS versions +for OSVERSION in 10.9 10.8 10.7 10.6 10.5 10.4 +do + echo "" + echo " * * * Running all unit tests i386 built for $OSVERSION * * *" + + # make clean + ../bin/make-recursive.pl clean > /dev/null + + # build default architecture + ../bin/make-recursive.pl ARCH="i386" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../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 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" OS_VERSION=$OSVERSION OS_NAME=MacOSX OSX_SDK_ROOT=${SDK} | ../bin/result-filter.pl + fi + +done + +# restore crash reporter state +defaults write com.apple.CrashReporter DialogType ${CRSTATE} + + diff --git a/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/Makefile b/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/Makefile new file mode 100644 index 0000000..44befa7 --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/Makefile @@ -0,0 +1,61 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c b/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c b/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c new file mode 100644 index 0000000..224d93e --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_LIBRARY_PATH-dyld_env/main.c @@ -0,0 +1,49 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + 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 +#endif + PASS("DYLD_VERSIONED_LIBRARY_PATH-basic"); + + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile new file mode 100644 index 0000000..67ef91c --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/Makefile @@ -0,0 +1,64 @@ +## +# 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) + +ifeq "$(OS_NAME)" "iPhoneOS" + CHECK = check-ios +else + CHECK = check-macosx +endif + + +all-check: all $(CHECK) + +check: $(CHECK) + +check-ios: + ./main 10 + +check-macosx: + ./main 10 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11" && ./main 11 "alt11/Foo.framework/Versions/A/Foo" + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9" && ./main 10 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt9:${PWD}/alt11" && ./main 11 + export DYLD_VERSIONED_FRAMEWORK_PATH="${PWD}/alt11:${PWD}/alt12" && ./main 12 + +all: + mkdir -p Foo.framework alt11/Foo.framework/Versions/A alt9/Foo.framework alt12/Foo.framework Bar.framework alt11/Bar.framework/Versions/A/ + ${CC} ${CCFLAGS} -dynamiclib bar.c -DRESULT=10 -current_version 10 -install_name "${PWD}/Bar.framework/Bar" -o Bar.framework/Bar + ${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 Bar.framework/Bar Foo.framework/Foo + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/Foo.framework/Foo" -o alt11/Foo.framework/Versions/A/Foo + ${CC} ${CCFLAGS} -dynamiclib bar.c -DRESULT=11 -current_version 11 -install_name "${PWD}/Bar.framework/Foo" -o alt11/Bar.framework/Versions/A/Bar + cd alt11/Foo.framework && ln -sf Versions/A/Foo + cd alt11/Bar.framework && ln -sf Versions/A/Bar + ${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 Bar.framework alt9 alt11 alt12 + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c new file mode 100644 index 0000000..4684921 --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/bar.c @@ -0,0 +1,5 @@ + +int bar() +{ + return 0; +} diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c new file mode 100644 index 0000000..26a1e09 --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_FRAMEWORK_PATH-basic/main.c @@ -0,0 +1,63 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + if ( argc > 2 ) { + bool found = false; + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, argv[2]) != NULL ) + found = true; + //fprintf(stderr, "image[%d]=%s\n", i, name); + } + if ( !found ) { + FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic dylib has wrong path"); + return EXIT_SUCCESS; + } + } + + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("DYLD_VERSIONED_FRAMEWORK_PATH-basic using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("DYLD_VERSIONED_FRAMEWORK_PATH-basic"); + + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/Makefile b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/Makefile new file mode 100644 index 0000000..4206cb7 --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/Makefile @@ -0,0 +1,61 @@ +## +# 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) + +ifeq "$(OS_NAME)" "iPhoneOS" + CHECK = check-ios +else + CHECK = check-macosx +endif + +all-check: all check + +check: $(CHECK) + +check-ios: + ./main 10 + +check-macosx: + ./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} -dynamiclib bar.c -DRESULT=10 -current_version 10 -o "${PWD}/libbar.dylib" + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=11 -current_version 11 -install_name "${PWD}/libfoo.dylib" -o alt11/libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -DRESULT=11 -current_version 11 -install_name "${PWD}/libbar.dylib" -o alt11/libbar.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 libbar.dylib alt9 alt11 alt12 + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c new file mode 100644 index 0000000..c57608f --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/bar.c @@ -0,0 +1,5 @@ + +int bar() +{ + return RESULT; +} diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-basic/main.c @@ -0,0 +1,47 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("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; +} + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/Makefile b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/Makefile new file mode 100644 index 0000000..eeaef6e --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/Makefile @@ -0,0 +1,58 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env-restrict/main.c @@ -0,0 +1,47 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("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; +} + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/Makefile b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/Makefile new file mode 100644 index 0000000..357a34e --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/Makefile @@ -0,0 +1,61 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c new file mode 100644 index 0000000..a903b04 --- /dev/null +++ b/dyld/unit-tests/test-cases/DYLD_VERSIONED_LIBRARY_PATH-dyld_env/main.c @@ -0,0 +1,47 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("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; +} + diff --git a/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile new file mode 100644 index 0000000..2a47c2b --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +all: main + +main : main.c libbar.dylib libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main + +libfoo.dylib : foo.c libbar.dylib + ${CC} -I${TESTROOT}/include -dynamiclib foo.c -o libfoo.dylib libbar.dylib + +libbar.dylib : bar.c + ${CC} -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib -install_name /usr/local/hide/libbar.dylib + +check: + ./main + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib diff --git a/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/bar.c b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/bar.c new file mode 100644 index 0000000..fa1d6fe --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/bar.c @@ -0,0 +1,23 @@ +/* + * 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@ + */ + diff --git a/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/foo.c b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/foo.c new file mode 100644 index 0000000..fa1d6fe --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/foo.c @@ -0,0 +1,23 @@ +/* + * 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@ + */ + diff --git a/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c new file mode 100644 index 0000000..bcb2bea --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-MATCH_BY_INSTALLNAME/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#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 ) + FAIL("NSAddImage-MATCH_BY_INSTALLNAME"); + else +#endif + PASS("NSAddImage-MATCH_BY_INSTALLNAME"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile b/dyld/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile new file mode 100644 index 0000000..72b6a43 --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ main diff --git a/dyld/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c b/dyld/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c new file mode 100644 index 0000000..3f84ea1 --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-RETURN_ONLY_IF_LOADED/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" + +/// rdar://problem/3748251 + +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 ) + FAIL("NSAddImage-RETURN_ONLY_IF_LOADED"); + else +#endif + PASS("NSAddImage-RETURN_ONLY_IF_LOADED"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/NSAddImage-leafname/Makefile b/dyld/unit-tests/test-cases/NSAddImage-leafname/Makefile new file mode 100644 index 0000000..c31f202 --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-leafname/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH="hide" && ./main + +all: main hide/libzzz.dylib + +main : main.c + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main + +hide/libzzz.dylib: zzz.c + mkdir -p hide + ${CC} zzz.c -dynamiclib -o hide/libzzz.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main hide diff --git a/dyld/unit-tests/test-cases/NSAddImage-leafname/main.c b/dyld/unit-tests/test-cases/NSAddImage-leafname/main.c new file mode 100644 index 0000000..f1ad630 --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-leafname/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" + +/// rdar://problem/3751226 + +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 ) + FAIL("NSAddImage-leafname"); + else +#endif + PASS("NSAddImage-leafname"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/NSAddImage-leafname/zzz.c b/dyld/unit-tests/test-cases/NSAddImage-leafname/zzz.c new file mode 100644 index 0000000..80420c7 --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddImage-leafname/zzz.c @@ -0,0 +1,23 @@ +/* + * 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@ + */ +void zzz() {} diff --git a/dyld/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile b/dyld/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile new file mode 100644 index 0000000..1590fdc --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddressOfSymbol-NULL/Makefile @@ -0,0 +1,19 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wno-deprecated-declarations + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c b/dyld/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c new file mode 100644 index 0000000..09fea73 --- /dev/null +++ b/dyld/unit-tests/test-cases/NSAddressOfSymbol-NULL/main.c @@ -0,0 +1,43 @@ +/* + * 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@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +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; +} diff --git a/dyld/unit-tests/test-cases/absolute-symbol/Makefile b/dyld/unit-tests/test-cases/absolute-symbol/Makefile new file mode 100644 index 0000000..1c06dda --- /dev/null +++ b/dyld/unit-tests/test-cases/absolute-symbol/Makefile @@ -0,0 +1,21 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: + + ${CC} ${CCFLAGS} foo.c abs.s -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/absolute-symbol/abs.s b/dyld/unit-tests/test-cases/absolute-symbol/abs.s new file mode 100644 index 0000000..2352520 --- /dev/null +++ b/dyld/unit-tests/test-cases/absolute-symbol/abs.s @@ -0,0 +1,12 @@ + + .global _myAbs1 +_myAbs1 = 0 + + + + .global _myAbs2 +_myAbs2 = 1 + + + +_myLocalAbs = 3 diff --git a/dyld/unit-tests/test-cases/absolute-symbol/foo.c b/dyld/unit-tests/test-cases/absolute-symbol/foo.c new file mode 100644 index 0000000..8d231e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/absolute-symbol/foo.c @@ -0,0 +1,5 @@ + + +int var = 5; +void func() { } + diff --git a/dyld/unit-tests/test-cases/absolute-symbol/main.c b/dyld/unit-tests/test-cases/absolute-symbol/main.c new file mode 100644 index 0000000..e6b61f3 --- /dev/null +++ b/dyld/unit-tests/test-cases/absolute-symbol/main.c @@ -0,0 +1,31 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int var; +extern void func(); +extern int myAbs1 __attribute__((weak_import)); +extern int myAbs2; + + +int main() +{ + + if (&myAbs1 != 0 ) { + FAIL("absolute-symbol: &myAbs1 != 0"); + return 0; + } + + + if ((uintptr_t)&myAbs2 != 1 ) { + FAIL("absolute-symbol: &myAbs2 != 1"); + return 0; + } + + + PASS("absolute-symbol"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/addend/Makefile b/dyld/unit-tests/test-cases/addend/Makefile new file mode 100644 index 0000000..db2bdbf --- /dev/null +++ b/dyld/unit-tests/test-cases/addend/Makefile @@ -0,0 +1,23 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/addend/foo.c b/dyld/unit-tests/test-cases/addend/foo.c new file mode 100644 index 0000000..efdd301 --- /dev/null +++ b/dyld/unit-tests/test-cases/addend/foo.c @@ -0,0 +1,7 @@ + + +const char a = 10; +const char b = 11; +const char c = 12; +const char d = 13; +const char e = 14; diff --git a/dyld/unit-tests/test-cases/addend/main.c b/dyld/unit-tests/test-cases/addend/main.c new file mode 100644 index 0000000..72b3710 --- /dev/null +++ b/dyld/unit-tests/test-cases/addend/main.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern const char a; +extern const char b; +extern const char c; +extern const char d; +extern const char e; + + +const char* pc = &c; +const char* pd_2 = &d - 2; +const char* pb2 = &b + 2; + +const char* pd_1234567890 = &d - 1234567890; +const char* pd1234567890 = &d + 1234567890; + +#if __LP64__ +const char* pd_12345678901234 = &d - 12345678901234; +const char* pd12345678901234 = &d + 12345678901234; +#endif + +int main() +{ + if (*pc != 12 ) { + FAIL("addend: *pc != 12"); + return 0; + } + + if (*pd_2 != 11 ) { + FAIL("addend: *pd_2 != 11"); + return 0; + } + + if (*pb2 != 13 ) { + FAIL("addend: *pb2 != 13"); + return 0; + } + + if (pd_1234567890[1234567890] != 13 ) { + FAIL("addend: pd_1234567890[1234567890] != 13"); + return 0; + } + + if (pd1234567890[-1234567890] != 13 ) { + FAIL("addend: pd1234567890[-1234567890] != 13"); + return 0; + } + +#if __LP64__ + if (pd_12345678901234[12345678901234] != 13 ) { + FAIL("addend: pd_12345678901234[12345678901234] != 13"); + return 0; + } + + if (pd12345678901234[-12345678901234] != 13 ) { + FAIL("addend: pd12345678901234[-12345678901234] != 13"); + return 0; + } +#endif + + PASS("addend"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/all_image_infos-cache-slide/Makefile b/dyld/unit-tests/test-cases/all_image_infos-cache-slide/Makefile new file mode 100644 index 0000000..0babb7e --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-cache-slide/Makefile @@ -0,0 +1,37 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/all_image_infos-cache-slide/main.c b/dyld/unit-tests/test-cases/all_image_infos-cache-slide/main.c new file mode 100644 index 0000000..0a550e4 --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-cache-slide/main.c @@ -0,0 +1,82 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/all_image_infos-duplicate/Makefile b/dyld/unit-tests/test-cases/all_image_infos-duplicate/Makefile new file mode 100644 index 0000000..b0f89b7 --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-duplicate/Makefile @@ -0,0 +1,38 @@ +## +# 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 diff --git a/dyld/unit-tests/test-cases/all_image_infos-duplicate/foo.c b/dyld/unit-tests/test-cases/all_image_infos-duplicate/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-duplicate/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/all_image_infos-duplicate/main.c b/dyld/unit-tests/test-cases/all_image_infos-duplicate/main.c new file mode 100644 index 0000000..b70ac9c --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-duplicate/main.c @@ -0,0 +1,70 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/all_image_infos-paths/Makefile b/dyld/unit-tests/test-cases/all_image_infos-paths/Makefile new file mode 100644 index 0000000..e384ae6 --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-paths/Makefile @@ -0,0 +1,40 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/all_image_infos-paths/foo.c b/dyld/unit-tests/test-cases/all_image_infos-paths/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-paths/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/all_image_infos-paths/main.c b/dyld/unit-tests/test-cases/all_image_infos-paths/main.c new file mode 100644 index 0000000..7e38b8c --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos-paths/main.c @@ -0,0 +1,68 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/all_image_infos/Makefile b/dyld/unit-tests/test-cases/all_image_infos/Makefile new file mode 100644 index 0000000..b625c20 --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib diff --git a/dyld/unit-tests/test-cases/all_image_infos/foo.c b/dyld/unit-tests/test-cases/all_image_infos/foo.c new file mode 100644 index 0000000..c1f5255 --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/dyld/unit-tests/test-cases/all_image_infos/main.c b/dyld/unit-tests/test-cases/all_image_infos/main.c new file mode 100644 index 0000000..efff2b8 --- /dev/null +++ b/dyld/unit-tests/test-cases/all_image_infos/main.c @@ -0,0 +1,86 @@ +/* + * 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@ + */ +#include // EXIT_SUCCESS +#include +#include +#include +#include +#include + +#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* infos = getImageInfosFromKernel(); + if ( infos->version < 9 ) { + FAIL("all_image_infos: dyld_all_image_infos is not version >= 9, is %d", infos->version); + exit(0); + } + + if ( infos->infoArrayCount < 2 ) { + FAIL("all_image_infos: dyld_all_image_infos.infoArrayCount is < 2"); + 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, infos->infoArray[0].imageLoadAddress=0x%08lX vs 0x%08X", + infos->infoArray[0].imageLoadAddress, &__dso_handle); + 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; +} + + diff --git a/dyld/unit-tests/test-cases/always-libSystem/Makefile b/dyld/unit-tests/test-cases/always-libSystem/Makefile new file mode 100644 index 0000000..287a47b --- /dev/null +++ b/dyld/unit-tests/test-cases/always-libSystem/Makefile @@ -0,0 +1,45 @@ +## +# 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 +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 + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/always-libSystem/main.c b/dyld/unit-tests/test-cases/always-libSystem/main.c new file mode 100644 index 0000000..5626783 --- /dev/null +++ b/dyld/unit-tests/test-cases/always-libSystem/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// app crashes when libSystem cannot be found +// + +int main() +{ + // see if libSystem is in list of images + uint32_t count = _dyld_image_count(); + for(uint32_t i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, "/libSystem.") != NULL ) { + PASS("always-libSystem"); + return 0; + } + } + + FAIL("always-libSystem"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/big-jump-table/Makefile b/dyld/unit-tests/test-cases/big-jump-table/Makefile new file mode 100644 index 0000000..d968239 --- /dev/null +++ b/dyld/unit-tests/test-cases/big-jump-table/Makefile @@ -0,0 +1,62 @@ +## +# 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 + +### +### The point of this test is to check an edge case for i386 architecture +### with "fast stubs". We want to verify that a fast stub that ends +### near the page boundary for the __IMPORT segment does not cause an +### accidental read beyond the __IMPORT segment +### rdar://problem/4653725 +### + +main: main.c libtest1.dylib libtest2.dylib libtest3.dylib libtest4.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libtest1.dylib libtest2.dylib libtest3.dylib libtest4.dylib + +libtest1.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=1 -o libtest1.dylib libfoo.dylib + +libtest2.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=2 -o libtest2.dylib libfoo.dylib + +libtest3.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=3 -o libtest3.dylib libfoo.dylib + +libtest4.dylib: pointers.c funcs.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib pointers.c funcs.c -DCASE=4 -o libtest4.dylib libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libtest1.dylib libtest2.dylib libtest3.dylib libtest4.dylib libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/big-jump-table/foo.c b/dyld/unit-tests/test-cases/big-jump-table/foo.c new file mode 100644 index 0000000..c1a6c6c --- /dev/null +++ b/dyld/unit-tests/test-cases/big-jump-table/foo.c @@ -0,0 +1,827 @@ +#include "foo.h" + +void foo000() {} +void foo001() {} +void foo002() {} +void foo003() {} +void foo004() {} +void foo005() {} +void foo006() {} +void foo007() {} +void foo008() {} +void foo009() {} +void foo010() {} +void foo011() {} +void foo012() {} +void foo013() {} +void foo014() {} +void foo015() {} +void foo016() {} +void foo017() {} +void foo018() {} +void foo019() {} +void foo020() {} +void foo021() {} +void foo022() {} +void foo023() {} +void foo024() {} +void foo025() {} +void foo026() {} +void foo027() {} +void foo028() {} +void foo029() {} +void foo030() {} +void foo031() {} +void foo032() {} +void foo033() {} +void foo034() {} +void foo035() {} +void foo036() {} +void foo037() {} +void foo038() {} +void foo039() {} +void foo040() {} +void foo041() {} +void foo042() {} +void foo043() {} +void foo044() {} +void foo045() {} +void foo046() {} +void foo047() {} +void foo048() {} +void foo049() {} +void foo050() {} +void foo051() {} +void foo052() {} +void foo053() {} +void foo054() {} +void foo055() {} +void foo056() {} +void foo057() {} +void foo058() {} +void foo059() {} +void foo060() {} +void foo061() {} +void foo062() {} +void foo063() {} +void foo064() {} +void foo065() {} +void foo066() {} +void foo067() {} +void foo068() {} +void foo069() {} +void foo070() {} +void foo071() {} +void foo072() {} +void foo073() {} +void foo074() {} +void foo075() {} +void foo076() {} +void foo077() {} +void foo078() {} +void foo079() {} +void foo080() {} +void foo081() {} +void foo082() {} +void foo083() {} +void foo084() {} +void foo085() {} +void foo086() {} +void foo087() {} +void foo088() {} +void foo089() {} +void foo090() {} +void foo091() {} +void foo092() {} +void foo093() {} +void foo094() {} +void foo095() {} +void foo096() {} +void foo097() {} +void foo098() {} +void foo099() {} +void foo100() {} +void foo101() {} +void foo102() {} +void foo103() {} +void foo104() {} +void foo105() {} +void foo106() {} +void foo107() {} +void foo108() {} +void foo109() {} +void foo110() {} +void foo111() {} +void foo112() {} +void foo113() {} +void foo114() {} +void foo115() {} +void foo116() {} +void foo117() {} +void foo118() {} +void foo119() {} +void foo120() {} +void foo121() {} +void foo122() {} +void foo123() {} +void foo124() {} +void foo125() {} +void foo126() {} +void foo127() {} +void foo128() {} +void foo129() {} +void foo130() {} +void foo131() {} +void foo132() {} +void foo133() {} +void foo134() {} +void foo135() {} +void foo136() {} +void foo137() {} +void foo138() {} +void foo139() {} +void foo140() {} +void foo141() {} +void foo142() {} +void foo143() {} +void foo144() {} +void foo145() {} +void foo146() {} +void foo147() {} +void foo148() {} +void foo149() {} +void foo150() {} +void foo151() {} +void foo152() {} +void foo153() {} +void foo154() {} +void foo155() {} +void foo156() {} +void foo157() {} +void foo158() {} +void foo159() {} +void foo160() {} +void foo161() {} +void foo162() {} +void foo163() {} +void foo164() {} +void foo165() {} +void foo166() {} +void foo167() {} +void foo168() {} +void foo169() {} +void foo170() {} +void foo171() {} +void foo172() {} +void foo173() {} +void foo174() {} +void foo175() {} +void foo176() {} +void foo177() {} +void foo178() {} +void foo179() {} +void foo180() {} +void foo181() {} +void foo182() {} +void foo183() {} +void foo184() {} +void foo185() {} +void foo186() {} +void foo187() {} +void foo188() {} +void foo189() {} +void foo190() {} +void foo191() {} +void foo192() {} +void foo193() {} +void foo194() {} +void foo195() {} +void foo196() {} +void foo197() {} +void foo198() {} +void foo199() {} +void foo200() {} +void foo201() {} +void foo202() {} +void foo203() {} +void foo204() {} +void foo205() {} +void foo206() {} +void foo207() {} +void foo208() {} +void foo209() {} +void foo210() {} +void foo211() {} +void foo212() {} +void foo213() {} +void foo214() {} +void foo215() {} +void foo216() {} +void foo217() {} +void foo218() {} +void foo219() {} +void foo220() {} +void foo221() {} +void foo222() {} +void foo223() {} +void foo224() {} +void foo225() {} +void foo226() {} +void foo227() {} +void foo228() {} +void foo229() {} +void foo230() {} +void foo231() {} +void foo232() {} +void foo233() {} +void foo234() {} +void foo235() {} +void foo236() {} +void foo237() {} +void foo238() {} +void foo239() {} +void foo240() {} +void foo241() {} +void foo242() {} +void foo243() {} +void foo244() {} +void foo245() {} +void foo246() {} +void foo247() {} +void foo248() {} +void foo249() {} +void foo250() {} +void foo251() {} +void foo252() {} +void foo253() {} +void foo254() {} +void foo255() {} +void foo256() {} +void foo257() {} +void foo258() {} +void foo259() {} +void foo260() {} +void foo261() {} +void foo262() {} +void foo263() {} +void foo264() {} +void foo265() {} +void foo266() {} +void foo267() {} +void foo268() {} +void foo269() {} +void foo270() {} +void foo271() {} +void foo272() {} +void foo273() {} +void foo274() {} +void foo275() {} +void foo276() {} +void foo277() {} +void foo278() {} +void foo279() {} +void foo280() {} +void foo281() {} +void foo282() {} +void foo283() {} +void foo284() {} +void foo285() {} +void foo286() {} +void foo287() {} +void foo288() {} +void foo289() {} +void foo290() {} +void foo291() {} +void foo292() {} +void foo293() {} +void foo294() {} +void foo295() {} +void foo296() {} +void foo297() {} +void foo298() {} +void foo299() {} +void foo300() {} +void foo301() {} +void foo302() {} +void foo303() {} +void foo304() {} +void foo305() {} +void foo306() {} +void foo307() {} +void foo308() {} +void foo309() {} +void foo310() {} +void foo311() {} +void foo312() {} +void foo313() {} +void foo314() {} +void foo315() {} +void foo316() {} +void foo317() {} +void foo318() {} +void foo319() {} +void foo320() {} +void foo321() {} +void foo322() {} +void foo323() {} +void foo324() {} +void foo325() {} +void foo326() {} +void foo327() {} +void foo328() {} +void foo329() {} +void foo330() {} +void foo331() {} +void foo332() {} +void foo333() {} +void foo334() {} +void foo335() {} +void foo336() {} +void foo337() {} +void foo338() {} +void foo339() {} +void foo340() {} +void foo341() {} +void foo342() {} +void foo343() {} +void foo344() {} +void foo345() {} +void foo346() {} +void foo347() {} +void foo348() {} +void foo349() {} +void foo350() {} +void foo351() {} +void foo352() {} +void foo353() {} +void foo354() {} +void foo355() {} +void foo356() {} +void foo357() {} +void foo358() {} +void foo359() {} +void foo360() {} +void foo361() {} +void foo362() {} +void foo363() {} +void foo364() {} +void foo365() {} +void foo366() {} +void foo367() {} +void foo368() {} +void foo369() {} +void foo370() {} +void foo371() {} +void foo372() {} +void foo373() {} +void foo374() {} +void foo375() {} +void foo376() {} +void foo377() {} +void foo378() {} +void foo379() {} +void foo380() {} +void foo381() {} +void foo382() {} +void foo383() {} +void foo384() {} +void foo385() {} +void foo386() {} +void foo387() {} +void foo388() {} +void foo389() {} +void foo390() {} +void foo391() {} +void foo392() {} +void foo393() {} +void foo394() {} +void foo395() {} +void foo396() {} +void foo397() {} +void foo398() {} +void foo399() {} +void foo400() {} +void foo401() {} +void foo402() {} +void foo403() {} +void foo404() {} +void foo405() {} +void foo406() {} +void foo407() {} +void foo408() {} +void foo409() {} +void foo410() {} +void foo411() {} +void foo412() {} +void foo413() {} +void foo414() {} +void foo415() {} +void foo416() {} +void foo417() {} +void foo418() {} +void foo419() {} +void foo420() {} +void foo421() {} +void foo422() {} +void foo423() {} +void foo424() {} +void foo425() {} +void foo426() {} +void foo427() {} +void foo428() {} +void foo429() {} +void foo430() {} +void foo431() {} +void foo432() {} +void foo433() {} +void foo434() {} +void foo435() {} +void foo436() {} +void foo437() {} +void foo438() {} +void foo439() {} +void foo440() {} +void foo441() {} +void foo442() {} +void foo443() {} +void foo444() {} +void foo445() {} +void foo446() {} +void foo447() {} +void foo448() {} +void foo449() {} +void foo450() {} +void foo451() {} +void foo452() {} +void foo453() {} +void foo454() {} +void foo455() {} +void foo456() {} +void foo457() {} +void foo458() {} +void foo459() {} +void foo460() {} +void foo461() {} +void foo462() {} +void foo463() {} +void foo464() {} +void foo465() {} +void foo466() {} +void foo467() {} +void foo468() {} +void foo469() {} +void foo470() {} +void foo471() {} +void foo472() {} +void foo473() {} +void foo474() {} +void foo475() {} +void foo476() {} +void foo477() {} +void foo478() {} +void foo479() {} +void foo480() {} +void foo481() {} +void foo482() {} +void foo483() {} +void foo484() {} +void foo485() {} +void foo486() {} +void foo487() {} +void foo488() {} +void foo489() {} +void foo490() {} +void foo491() {} +void foo492() {} +void foo493() {} +void foo494() {} +void foo495() {} +void foo496() {} +void foo497() {} +void foo498() {} +void foo499() {} +void foo500() {} +void foo501() {} +void foo502() {} +void foo503() {} +void foo504() {} +void foo505() {} +void foo506() {} +void foo507() {} +void foo508() {} +void foo509() {} +void foo510() {} +void foo511() {} +void foo512() {} +void foo513() {} +void foo514() {} +void foo515() {} +void foo516() {} +void foo517() {} +void foo518() {} +void foo519() {} +void foo520() {} +void foo521() {} +void foo522() {} +void foo523() {} +void foo524() {} +void foo525() {} +void foo526() {} +void foo527() {} +void foo528() {} +void foo529() {} +void foo530() {} +void foo531() {} +void foo532() {} +void foo533() {} +void foo534() {} +void foo535() {} +void foo536() {} +void foo537() {} +void foo538() {} +void foo539() {} +void foo540() {} +void foo541() {} +void foo542() {} +void foo543() {} +void foo544() {} +void foo545() {} +void foo546() {} +void foo547() {} +void foo548() {} +void foo549() {} +void foo550() {} +void foo551() {} +void foo552() {} +void foo553() {} +void foo554() {} +void foo555() {} +void foo556() {} +void foo557() {} +void foo558() {} +void foo559() {} +void foo560() {} +void foo561() {} +void foo562() {} +void foo563() {} +void foo564() {} +void foo565() {} +void foo566() {} +void foo567() {} +void foo568() {} +void foo569() {} +void foo570() {} +void foo571() {} +void foo572() {} +void foo573() {} +void foo574() {} +void foo575() {} +void foo576() {} +void foo577() {} +void foo578() {} +void foo579() {} +void foo580() {} +void foo581() {} +void foo582() {} +void foo583() {} +void foo584() {} +void foo585() {} +void foo586() {} +void foo587() {} +void foo588() {} +void foo589() {} +void foo590() {} +void foo591() {} +void foo592() {} +void foo593() {} +void foo594() {} +void foo595() {} +void foo596() {} +void foo597() {} +void foo598() {} +void foo599() {} +void foo600() {} +void foo601() {} +void foo602() {} +void foo603() {} +void foo604() {} +void foo605() {} +void foo606() {} +void foo607() {} +void foo608() {} +void foo609() {} +void foo610() {} +void foo611() {} +void foo612() {} +void foo613() {} +void foo614() {} +void foo615() {} +void foo616() {} +void foo617() {} +void foo618() {} +void foo619() {} +void foo620() {} +void foo621() {} +void foo622() {} +void foo623() {} +void foo624() {} +void foo625() {} +void foo626() {} +void foo627() {} +void foo628() {} +void foo629() {} +void foo630() {} +void foo631() {} +void foo632() {} +void foo633() {} +void foo634() {} +void foo635() {} +void foo636() {} +void foo637() {} +void foo638() {} +void foo639() {} +void foo640() {} +void foo641() {} +void foo642() {} +void foo643() {} +void foo644() {} +void foo645() {} +void foo646() {} +void foo647() {} +void foo648() {} +void foo649() {} +void foo650() {} +void foo651() {} +void foo652() {} +void foo653() {} +void foo654() {} +void foo655() {} +void foo656() {} +void foo657() {} +void foo658() {} +void foo659() {} +void foo660() {} +void foo661() {} +void foo662() {} +void foo663() {} +void foo664() {} +void foo665() {} +void foo666() {} +void foo667() {} +void foo668() {} +void foo669() {} +void foo670() {} +void foo671() {} +void foo672() {} +void foo673() {} +void foo674() {} +void foo675() {} +void foo676() {} +void foo677() {} +void foo678() {} +void foo679() {} +void foo680() {} +void foo681() {} +void foo682() {} +void foo683() {} +void foo684() {} +void foo685() {} +void foo686() {} +void foo687() {} +void foo688() {} +void foo689() {} +void foo690() {} +void foo691() {} +void foo692() {} +void foo693() {} +void foo694() {} +void foo695() {} +void foo696() {} +void foo697() {} +void foo698() {} +void foo699() {} +void foo700() {} +void foo701() {} +void foo702() {} +void foo703() {} +void foo704() {} +void foo705() {} +void foo706() {} +void foo707() {} +void foo708() {} +void foo709() {} +void foo710() {} +void foo711() {} +void foo712() {} +void foo713() {} +void foo714() {} +void foo715() {} +void foo716() {} +void foo717() {} +void foo718() {} +void foo719() {} +void foo720() {} +void foo721() {} +void foo722() {} +void foo723() {} +void foo724() {} +void foo725() {} +void foo726() {} +void foo727() {} +void foo728() {} +void foo729() {} +void foo730() {} +void foo731() {} +void foo732() {} +void foo733() {} +void foo734() {} +void foo735() {} +void foo736() {} +void foo737() {} +void foo738() {} +void foo739() {} +void foo740() {} +void foo741() {} +void foo742() {} +void foo743() {} +void foo744() {} +void foo745() {} +void foo746() {} +void foo747() {} +void foo748() {} +void foo749() {} +void foo750() {} +void foo751() {} +void foo752() {} +void foo753() {} +void foo754() {} +void foo755() {} +void foo756() {} +void foo757() {} +void foo758() {} +void foo759() {} +void foo760() {} +void foo761() {} +void foo762() {} +void foo763() {} +void foo764() {} +void foo765() {} +void foo766() {} +void foo767() {} +void foo768() {} +void foo769() {} +void foo770() {} +void foo771() {} +void foo772() {} +void foo773() {} +void foo774() {} +void foo775() {} +void foo776() {} +void foo777() {} +void foo778() {} +void foo779() {} +void foo780() {} +void foo781() {} +void foo782() {} +void foo783() {} +void foo784() {} +void foo785() {} +void foo786() {} +void foo787() {} +void foo788() {} +void foo789() {} +void foo790() {} +void foo791() {} +void foo792() {} +void foo793() {} +void foo794() {} +void foo795() {} +void foo796() {} +void foo797() {} +void foo798() {} +void foo799() {} +void foo800() {} +void foo801() {} +void foo802() {} +void foo803() {} +void foo804() {} +void foo805() {} +void foo806() {} +void foo807() {} +void foo808() {} +void foo809() {} +void foo810() {} +void foo811() {} +void foo812() {} +void foo813() {} +void foo814() {} +void foo815() {} +void foo816() {} +void foo817() {} +void foo818() {} +void foo819() {} +void foo820() {} +void foo821() {} +void foo822() {} +void foo823() {} +void foo824() {} diff --git a/dyld/unit-tests/test-cases/big-jump-table/foo.h b/dyld/unit-tests/test-cases/big-jump-table/foo.h new file mode 100644 index 0000000..e9b155b --- /dev/null +++ b/dyld/unit-tests/test-cases/big-jump-table/foo.h @@ -0,0 +1,825 @@ +extern void foo000(); +extern void foo001(); +extern void foo002(); +extern void foo003(); +extern void foo004(); +extern void foo005(); +extern void foo006(); +extern void foo007(); +extern void foo008(); +extern void foo009(); +extern void foo010(); +extern void foo011(); +extern void foo012(); +extern void foo013(); +extern void foo014(); +extern void foo015(); +extern void foo016(); +extern void foo017(); +extern void foo018(); +extern void foo019(); +extern void foo020(); +extern void foo021(); +extern void foo022(); +extern void foo023(); +extern void foo024(); +extern void foo025(); +extern void foo026(); +extern void foo027(); +extern void foo028(); +extern void foo029(); +extern void foo030(); +extern void foo031(); +extern void foo032(); +extern void foo033(); +extern void foo034(); +extern void foo035(); +extern void foo036(); +extern void foo037(); +extern void foo038(); +extern void foo039(); +extern void foo040(); +extern void foo041(); +extern void foo042(); +extern void foo043(); +extern void foo044(); +extern void foo045(); +extern void foo046(); +extern void foo047(); +extern void foo048(); +extern void foo049(); +extern void foo050(); +extern void foo051(); +extern void foo052(); +extern void foo053(); +extern void foo054(); +extern void foo055(); +extern void foo056(); +extern void foo057(); +extern void foo058(); +extern void foo059(); +extern void foo060(); +extern void foo061(); +extern void foo062(); +extern void foo063(); +extern void foo064(); +extern void foo065(); +extern void foo066(); +extern void foo067(); +extern void foo068(); +extern void foo069(); +extern void foo070(); +extern void foo071(); +extern void foo072(); +extern void foo073(); +extern void foo074(); +extern void foo075(); +extern void foo076(); +extern void foo077(); +extern void foo078(); +extern void foo079(); +extern void foo080(); +extern void foo081(); +extern void foo082(); +extern void foo083(); +extern void foo084(); +extern void foo085(); +extern void foo086(); +extern void foo087(); +extern void foo088(); +extern void foo089(); +extern void foo090(); +extern void foo091(); +extern void foo092(); +extern void foo093(); +extern void foo094(); +extern void foo095(); +extern void foo096(); +extern void foo097(); +extern void foo098(); +extern void foo099(); +extern void foo100(); +extern void foo101(); +extern void foo102(); +extern void foo103(); +extern void foo104(); +extern void foo105(); +extern void foo106(); +extern void foo107(); +extern void foo108(); +extern void foo109(); +extern void foo110(); +extern void foo111(); +extern void foo112(); +extern void foo113(); +extern void foo114(); +extern void foo115(); +extern void foo116(); +extern void foo117(); +extern void foo118(); +extern void foo119(); +extern void foo120(); +extern void foo121(); +extern void foo122(); +extern void foo123(); +extern void foo124(); +extern void foo125(); +extern void foo126(); +extern void foo127(); +extern void foo128(); +extern void foo129(); +extern void foo130(); +extern void foo131(); +extern void foo132(); +extern void foo133(); +extern void foo134(); +extern void foo135(); +extern void foo136(); +extern void foo137(); +extern void foo138(); +extern void foo139(); +extern void foo140(); +extern void foo141(); +extern void foo142(); +extern void foo143(); +extern void foo144(); +extern void foo145(); +extern void foo146(); +extern void foo147(); +extern void foo148(); +extern void foo149(); +extern void foo150(); +extern void foo151(); +extern void foo152(); +extern void foo153(); +extern void foo154(); +extern void foo155(); +extern void foo156(); +extern void foo157(); +extern void foo158(); +extern void foo159(); +extern void foo160(); +extern void foo161(); +extern void foo162(); +extern void foo163(); +extern void foo164(); +extern void foo165(); +extern void foo166(); +extern void foo167(); +extern void foo168(); +extern void foo169(); +extern void foo170(); +extern void foo171(); +extern void foo172(); +extern void foo173(); +extern void foo174(); +extern void foo175(); +extern void foo176(); +extern void foo177(); +extern void foo178(); +extern void foo179(); +extern void foo180(); +extern void foo181(); +extern void foo182(); +extern void foo183(); +extern void foo184(); +extern void foo185(); +extern void foo186(); +extern void foo187(); +extern void foo188(); +extern void foo189(); +extern void foo190(); +extern void foo191(); +extern void foo192(); +extern void foo193(); +extern void foo194(); +extern void foo195(); +extern void foo196(); +extern void foo197(); +extern void foo198(); +extern void foo199(); +extern void foo200(); +extern void foo201(); +extern void foo202(); +extern void foo203(); +extern void foo204(); +extern void foo205(); +extern void foo206(); +extern void foo207(); +extern void foo208(); +extern void foo209(); +extern void foo210(); +extern void foo211(); +extern void foo212(); +extern void foo213(); +extern void foo214(); +extern void foo215(); +extern void foo216(); +extern void foo217(); +extern void foo218(); +extern void foo219(); +extern void foo220(); +extern void foo221(); +extern void foo222(); +extern void foo223(); +extern void foo224(); +extern void foo225(); +extern void foo226(); +extern void foo227(); +extern void foo228(); +extern void foo229(); +extern void foo230(); +extern void foo231(); +extern void foo232(); +extern void foo233(); +extern void foo234(); +extern void foo235(); +extern void foo236(); +extern void foo237(); +extern void foo238(); +extern void foo239(); +extern void foo240(); +extern void foo241(); +extern void foo242(); +extern void foo243(); +extern void foo244(); +extern void foo245(); +extern void foo246(); +extern void foo247(); +extern void foo248(); +extern void foo249(); +extern void foo250(); +extern void foo251(); +extern void foo252(); +extern void foo253(); +extern void foo254(); +extern void foo255(); +extern void foo256(); +extern void foo257(); +extern void foo258(); +extern void foo259(); +extern void foo260(); +extern void foo261(); +extern void foo262(); +extern void foo263(); +extern void foo264(); +extern void foo265(); +extern void foo266(); +extern void foo267(); +extern void foo268(); +extern void foo269(); +extern void foo270(); +extern void foo271(); +extern void foo272(); +extern void foo273(); +extern void foo274(); +extern void foo275(); +extern void foo276(); +extern void foo277(); +extern void foo278(); +extern void foo279(); +extern void foo280(); +extern void foo281(); +extern void foo282(); +extern void foo283(); +extern void foo284(); +extern void foo285(); +extern void foo286(); +extern void foo287(); +extern void foo288(); +extern void foo289(); +extern void foo290(); +extern void foo291(); +extern void foo292(); +extern void foo293(); +extern void foo294(); +extern void foo295(); +extern void foo296(); +extern void foo297(); +extern void foo298(); +extern void foo299(); +extern void foo300(); +extern void foo301(); +extern void foo302(); +extern void foo303(); +extern void foo304(); +extern void foo305(); +extern void foo306(); +extern void foo307(); +extern void foo308(); +extern void foo309(); +extern void foo310(); +extern void foo311(); +extern void foo312(); +extern void foo313(); +extern void foo314(); +extern void foo315(); +extern void foo316(); +extern void foo317(); +extern void foo318(); +extern void foo319(); +extern void foo320(); +extern void foo321(); +extern void foo322(); +extern void foo323(); +extern void foo324(); +extern void foo325(); +extern void foo326(); +extern void foo327(); +extern void foo328(); +extern void foo329(); +extern void foo330(); +extern void foo331(); +extern void foo332(); +extern void foo333(); +extern void foo334(); +extern void foo335(); +extern void foo336(); +extern void foo337(); +extern void foo338(); +extern void foo339(); +extern void foo340(); +extern void foo341(); +extern void foo342(); +extern void foo343(); +extern void foo344(); +extern void foo345(); +extern void foo346(); +extern void foo347(); +extern void foo348(); +extern void foo349(); +extern void foo350(); +extern void foo351(); +extern void foo352(); +extern void foo353(); +extern void foo354(); +extern void foo355(); +extern void foo356(); +extern void foo357(); +extern void foo358(); +extern void foo359(); +extern void foo360(); +extern void foo361(); +extern void foo362(); +extern void foo363(); +extern void foo364(); +extern void foo365(); +extern void foo366(); +extern void foo367(); +extern void foo368(); +extern void foo369(); +extern void foo370(); +extern void foo371(); +extern void foo372(); +extern void foo373(); +extern void foo374(); +extern void foo375(); +extern void foo376(); +extern void foo377(); +extern void foo378(); +extern void foo379(); +extern void foo380(); +extern void foo381(); +extern void foo382(); +extern void foo383(); +extern void foo384(); +extern void foo385(); +extern void foo386(); +extern void foo387(); +extern void foo388(); +extern void foo389(); +extern void foo390(); +extern void foo391(); +extern void foo392(); +extern void foo393(); +extern void foo394(); +extern void foo395(); +extern void foo396(); +extern void foo397(); +extern void foo398(); +extern void foo399(); +extern void foo400(); +extern void foo401(); +extern void foo402(); +extern void foo403(); +extern void foo404(); +extern void foo405(); +extern void foo406(); +extern void foo407(); +extern void foo408(); +extern void foo409(); +extern void foo410(); +extern void foo411(); +extern void foo412(); +extern void foo413(); +extern void foo414(); +extern void foo415(); +extern void foo416(); +extern void foo417(); +extern void foo418(); +extern void foo419(); +extern void foo420(); +extern void foo421(); +extern void foo422(); +extern void foo423(); +extern void foo424(); +extern void foo425(); +extern void foo426(); +extern void foo427(); +extern void foo428(); +extern void foo429(); +extern void foo430(); +extern void foo431(); +extern void foo432(); +extern void foo433(); +extern void foo434(); +extern void foo435(); +extern void foo436(); +extern void foo437(); +extern void foo438(); +extern void foo439(); +extern void foo440(); +extern void foo441(); +extern void foo442(); +extern void foo443(); +extern void foo444(); +extern void foo445(); +extern void foo446(); +extern void foo447(); +extern void foo448(); +extern void foo449(); +extern void foo450(); +extern void foo451(); +extern void foo452(); +extern void foo453(); +extern void foo454(); +extern void foo455(); +extern void foo456(); +extern void foo457(); +extern void foo458(); +extern void foo459(); +extern void foo460(); +extern void foo461(); +extern void foo462(); +extern void foo463(); +extern void foo464(); +extern void foo465(); +extern void foo466(); +extern void foo467(); +extern void foo468(); +extern void foo469(); +extern void foo470(); +extern void foo471(); +extern void foo472(); +extern void foo473(); +extern void foo474(); +extern void foo475(); +extern void foo476(); +extern void foo477(); +extern void foo478(); +extern void foo479(); +extern void foo480(); +extern void foo481(); +extern void foo482(); +extern void foo483(); +extern void foo484(); +extern void foo485(); +extern void foo486(); +extern void foo487(); +extern void foo488(); +extern void foo489(); +extern void foo490(); +extern void foo491(); +extern void foo492(); +extern void foo493(); +extern void foo494(); +extern void foo495(); +extern void foo496(); +extern void foo497(); +extern void foo498(); +extern void foo499(); +extern void foo500(); +extern void foo501(); +extern void foo502(); +extern void foo503(); +extern void foo504(); +extern void foo505(); +extern void foo506(); +extern void foo507(); +extern void foo508(); +extern void foo509(); +extern void foo510(); +extern void foo511(); +extern void foo512(); +extern void foo513(); +extern void foo514(); +extern void foo515(); +extern void foo516(); +extern void foo517(); +extern void foo518(); +extern void foo519(); +extern void foo520(); +extern void foo521(); +extern void foo522(); +extern void foo523(); +extern void foo524(); +extern void foo525(); +extern void foo526(); +extern void foo527(); +extern void foo528(); +extern void foo529(); +extern void foo530(); +extern void foo531(); +extern void foo532(); +extern void foo533(); +extern void foo534(); +extern void foo535(); +extern void foo536(); +extern void foo537(); +extern void foo538(); +extern void foo539(); +extern void foo540(); +extern void foo541(); +extern void foo542(); +extern void foo543(); +extern void foo544(); +extern void foo545(); +extern void foo546(); +extern void foo547(); +extern void foo548(); +extern void foo549(); +extern void foo550(); +extern void foo551(); +extern void foo552(); +extern void foo553(); +extern void foo554(); +extern void foo555(); +extern void foo556(); +extern void foo557(); +extern void foo558(); +extern void foo559(); +extern void foo560(); +extern void foo561(); +extern void foo562(); +extern void foo563(); +extern void foo564(); +extern void foo565(); +extern void foo566(); +extern void foo567(); +extern void foo568(); +extern void foo569(); +extern void foo570(); +extern void foo571(); +extern void foo572(); +extern void foo573(); +extern void foo574(); +extern void foo575(); +extern void foo576(); +extern void foo577(); +extern void foo578(); +extern void foo579(); +extern void foo580(); +extern void foo581(); +extern void foo582(); +extern void foo583(); +extern void foo584(); +extern void foo585(); +extern void foo586(); +extern void foo587(); +extern void foo588(); +extern void foo589(); +extern void foo590(); +extern void foo591(); +extern void foo592(); +extern void foo593(); +extern void foo594(); +extern void foo595(); +extern void foo596(); +extern void foo597(); +extern void foo598(); +extern void foo599(); +extern void foo600(); +extern void foo601(); +extern void foo602(); +extern void foo603(); +extern void foo604(); +extern void foo605(); +extern void foo606(); +extern void foo607(); +extern void foo608(); +extern void foo609(); +extern void foo610(); +extern void foo611(); +extern void foo612(); +extern void foo613(); +extern void foo614(); +extern void foo615(); +extern void foo616(); +extern void foo617(); +extern void foo618(); +extern void foo619(); +extern void foo620(); +extern void foo621(); +extern void foo622(); +extern void foo623(); +extern void foo624(); +extern void foo625(); +extern void foo626(); +extern void foo627(); +extern void foo628(); +extern void foo629(); +extern void foo630(); +extern void foo631(); +extern void foo632(); +extern void foo633(); +extern void foo634(); +extern void foo635(); +extern void foo636(); +extern void foo637(); +extern void foo638(); +extern void foo639(); +extern void foo640(); +extern void foo641(); +extern void foo642(); +extern void foo643(); +extern void foo644(); +extern void foo645(); +extern void foo646(); +extern void foo647(); +extern void foo648(); +extern void foo649(); +extern void foo650(); +extern void foo651(); +extern void foo652(); +extern void foo653(); +extern void foo654(); +extern void foo655(); +extern void foo656(); +extern void foo657(); +extern void foo658(); +extern void foo659(); +extern void foo660(); +extern void foo661(); +extern void foo662(); +extern void foo663(); +extern void foo664(); +extern void foo665(); +extern void foo666(); +extern void foo667(); +extern void foo668(); +extern void foo669(); +extern void foo670(); +extern void foo671(); +extern void foo672(); +extern void foo673(); +extern void foo674(); +extern void foo675(); +extern void foo676(); +extern void foo677(); +extern void foo678(); +extern void foo679(); +extern void foo680(); +extern void foo681(); +extern void foo682(); +extern void foo683(); +extern void foo684(); +extern void foo685(); +extern void foo686(); +extern void foo687(); +extern void foo688(); +extern void foo689(); +extern void foo690(); +extern void foo691(); +extern void foo692(); +extern void foo693(); +extern void foo694(); +extern void foo695(); +extern void foo696(); +extern void foo697(); +extern void foo698(); +extern void foo699(); +extern void foo700(); +extern void foo701(); +extern void foo702(); +extern void foo703(); +extern void foo704(); +extern void foo705(); +extern void foo706(); +extern void foo707(); +extern void foo708(); +extern void foo709(); +extern void foo710(); +extern void foo711(); +extern void foo712(); +extern void foo713(); +extern void foo714(); +extern void foo715(); +extern void foo716(); +extern void foo717(); +extern void foo718(); +extern void foo719(); +extern void foo720(); +extern void foo721(); +extern void foo722(); +extern void foo723(); +extern void foo724(); +extern void foo725(); +extern void foo726(); +extern void foo727(); +extern void foo728(); +extern void foo729(); +extern void foo730(); +extern void foo731(); +extern void foo732(); +extern void foo733(); +extern void foo734(); +extern void foo735(); +extern void foo736(); +extern void foo737(); +extern void foo738(); +extern void foo739(); +extern void foo740(); +extern void foo741(); +extern void foo742(); +extern void foo743(); +extern void foo744(); +extern void foo745(); +extern void foo746(); +extern void foo747(); +extern void foo748(); +extern void foo749(); +extern void foo750(); +extern void foo751(); +extern void foo752(); +extern void foo753(); +extern void foo754(); +extern void foo755(); +extern void foo756(); +extern void foo757(); +extern void foo758(); +extern void foo759(); +extern void foo760(); +extern void foo761(); +extern void foo762(); +extern void foo763(); +extern void foo764(); +extern void foo765(); +extern void foo766(); +extern void foo767(); +extern void foo768(); +extern void foo769(); +extern void foo770(); +extern void foo771(); +extern void foo772(); +extern void foo773(); +extern void foo774(); +extern void foo775(); +extern void foo776(); +extern void foo777(); +extern void foo778(); +extern void foo779(); +extern void foo780(); +extern void foo781(); +extern void foo782(); +extern void foo783(); +extern void foo784(); +extern void foo785(); +extern void foo786(); +extern void foo787(); +extern void foo788(); +extern void foo789(); +extern void foo790(); +extern void foo791(); +extern void foo792(); +extern void foo793(); +extern void foo794(); +extern void foo795(); +extern void foo796(); +extern void foo797(); +extern void foo798(); +extern void foo799(); +extern void foo800(); +extern void foo801(); +extern void foo802(); +extern void foo803(); +extern void foo804(); +extern void foo805(); +extern void foo806(); +extern void foo807(); +extern void foo808(); +extern void foo809(); +extern void foo810(); +extern void foo811(); +extern void foo812(); +extern void foo813(); +extern void foo814(); +extern void foo815(); +extern void foo816(); +extern void foo817(); +extern void foo818(); +extern void foo819(); +extern void foo820(); +extern void foo821(); +extern void foo822(); +extern void foo823(); +extern void foo824(); diff --git a/dyld/unit-tests/test-cases/big-jump-table/funcs.c b/dyld/unit-tests/test-cases/big-jump-table/funcs.c new file mode 100644 index 0000000..64cd71a --- /dev/null +++ b/dyld/unit-tests/test-cases/big-jump-table/funcs.c @@ -0,0 +1,859 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include "foo.h" + +static void __attribute__((constructor)) myinit() +{ +// uint64_t t1 = mach_absolute_time(); + foo002(); + foo003(); + foo004(); + foo005(); + foo006(); + foo007(); + foo008(); + foo009(); + foo010(); + foo011(); + foo012(); + foo013(); + foo014(); + foo015(); + foo016(); + foo017(); + foo018(); + foo019(); + foo020(); + foo021(); + foo022(); + foo023(); + foo024(); + foo025(); + foo026(); + foo027(); + foo028(); + foo029(); + foo030(); + foo031(); + foo032(); + foo033(); + foo034(); + foo035(); + foo036(); + foo037(); + foo038(); + foo039(); + foo040(); + foo041(); + foo042(); + foo043(); + foo044(); + foo045(); + foo046(); + foo047(); + foo048(); + foo049(); + foo050(); + foo051(); + foo052(); + foo053(); + foo054(); + foo055(); + foo056(); + foo057(); + foo058(); + foo059(); + foo060(); + foo061(); + foo062(); + foo063(); + foo064(); + foo065(); + foo066(); + foo067(); + foo068(); + foo069(); + foo070(); + foo071(); + foo072(); + foo073(); + foo074(); + foo075(); + foo076(); + foo077(); + foo078(); + foo079(); + foo080(); + foo081(); + foo082(); + foo083(); + foo084(); + foo085(); + foo086(); + foo087(); + foo088(); + foo089(); + foo090(); + foo091(); + foo092(); + foo093(); + foo094(); + foo095(); + foo096(); + foo097(); + foo098(); + foo099(); + foo100(); + foo101(); + foo102(); + foo103(); + foo104(); + foo105(); + foo106(); + foo107(); + foo108(); + foo109(); + foo110(); + foo111(); + foo112(); + foo113(); + foo114(); + foo115(); + foo116(); + foo117(); + foo118(); + foo119(); + foo120(); + foo121(); + foo122(); + foo123(); + foo124(); + foo125(); + foo126(); + foo127(); + foo128(); + foo129(); + foo130(); + foo131(); + foo132(); + foo133(); + foo134(); + foo135(); + foo136(); + foo137(); + foo138(); + foo139(); + foo140(); + foo141(); + foo142(); + foo143(); + foo144(); + foo145(); + foo146(); + foo147(); + foo148(); + foo149(); + foo150(); + foo151(); + foo152(); + foo153(); + foo154(); + foo155(); + foo156(); + foo157(); + foo158(); + foo159(); + foo160(); + foo161(); + foo162(); + foo163(); + foo164(); + foo165(); + foo166(); + foo167(); + foo168(); + foo169(); + foo170(); + foo171(); + foo172(); + foo173(); + foo174(); + foo175(); + foo176(); + foo177(); + foo178(); + foo179(); + foo180(); + foo181(); + foo182(); + foo183(); + foo184(); + foo185(); + foo186(); + foo187(); + foo188(); + foo189(); + foo190(); + foo191(); + foo192(); + foo193(); + foo194(); + foo195(); + foo196(); + foo197(); + foo198(); + foo199(); + foo200(); + foo201(); + foo202(); + foo203(); + foo204(); + foo205(); + foo206(); + foo207(); + foo208(); + foo209(); + foo210(); + foo211(); + foo212(); + foo213(); + foo214(); + foo215(); + foo216(); + foo217(); + foo218(); + foo219(); + foo220(); + foo221(); + foo222(); + foo223(); + foo224(); + foo225(); + foo226(); + foo227(); + foo228(); + foo229(); + foo230(); + foo231(); + foo232(); + foo233(); + foo234(); + foo235(); + foo236(); + foo237(); + foo238(); + foo239(); + foo240(); + foo241(); + foo242(); + foo243(); + foo244(); + foo245(); + foo246(); + foo247(); + foo248(); + foo249(); + foo250(); + foo251(); + foo252(); + foo253(); + foo254(); + foo255(); + foo256(); + foo257(); + foo258(); + foo259(); + foo260(); + foo261(); + foo262(); + foo263(); + foo264(); + foo265(); + foo266(); + foo267(); + foo268(); + foo269(); + foo270(); + foo271(); + foo272(); + foo273(); + foo274(); + foo275(); + foo276(); + foo277(); + foo278(); + foo279(); + foo280(); + foo281(); + foo282(); + foo283(); + foo284(); + foo285(); + foo286(); + foo287(); + foo288(); + foo289(); + foo290(); + foo291(); + foo292(); + foo293(); + foo294(); + foo295(); + foo296(); + foo297(); + foo298(); + foo299(); + foo300(); + foo301(); + foo302(); + foo303(); + foo304(); + foo305(); + foo306(); + foo307(); + foo308(); + foo309(); + foo310(); + foo311(); + foo312(); + foo313(); + foo314(); + foo315(); + foo316(); + foo317(); + foo318(); + foo319(); + foo320(); + foo321(); + foo322(); + foo323(); + foo324(); + foo325(); + foo326(); + foo327(); + foo328(); + foo329(); + foo330(); + foo331(); + foo332(); + foo333(); + foo334(); + foo335(); + foo336(); + foo337(); + foo338(); + foo339(); + foo340(); + foo341(); + foo342(); + foo343(); + foo344(); + foo345(); + foo346(); + foo347(); + foo348(); + foo349(); + foo350(); + foo351(); + foo352(); + foo353(); + foo354(); + foo355(); + foo356(); + foo357(); + foo358(); + foo359(); + foo360(); + foo361(); + foo362(); + foo363(); + foo364(); + foo365(); + foo366(); + foo367(); + foo368(); + foo369(); + foo370(); + foo371(); + foo372(); + foo373(); + foo374(); + foo375(); + foo376(); + foo377(); + foo378(); + foo379(); + foo380(); + foo381(); + foo382(); + foo383(); + foo384(); + foo385(); + foo386(); + foo387(); + foo388(); + foo389(); + foo390(); + foo391(); + foo392(); + foo393(); + foo394(); + foo395(); + foo396(); + foo397(); + foo398(); + foo399(); + foo400(); + foo401(); + foo402(); + foo403(); + foo404(); + foo405(); + foo406(); + foo407(); + foo408(); + foo409(); + foo410(); + foo411(); + foo412(); + foo413(); + foo414(); + foo415(); + foo416(); + foo417(); + foo418(); + foo419(); + foo420(); + foo421(); + foo422(); + foo423(); + foo424(); + foo425(); + foo426(); + foo427(); + foo428(); + foo429(); + foo430(); + foo431(); + foo432(); + foo433(); + foo434(); + foo435(); + foo436(); + foo437(); + foo438(); + foo439(); + foo440(); + foo441(); + foo442(); + foo443(); + foo444(); + foo445(); + foo446(); + foo447(); + foo448(); + foo449(); + foo450(); + foo451(); + foo452(); + foo453(); + foo454(); + foo455(); + foo456(); + foo457(); + foo458(); + foo459(); + foo460(); + foo461(); + foo462(); + foo463(); + foo464(); + foo465(); + foo466(); + foo467(); + foo468(); + foo469(); + foo470(); + foo471(); + foo472(); + foo473(); + foo474(); + foo475(); + foo476(); + foo477(); + foo478(); + foo479(); + foo480(); + foo481(); + foo482(); + foo483(); + foo484(); + foo485(); + foo486(); + foo487(); + foo488(); + foo489(); + foo490(); + foo491(); + foo492(); + foo493(); + foo494(); + foo495(); + foo496(); + foo497(); + foo498(); + foo499(); + foo500(); + foo501(); + foo502(); + foo503(); + foo504(); + foo505(); + foo506(); + foo507(); + foo508(); + foo509(); + foo510(); + foo511(); + foo512(); + foo513(); + foo514(); + foo515(); + foo516(); + foo517(); + foo518(); + foo519(); + foo520(); + foo521(); + foo522(); + foo523(); + foo524(); + foo525(); + foo526(); + foo527(); + foo528(); + foo529(); + foo530(); + foo531(); + foo532(); + foo533(); + foo534(); + foo535(); + foo536(); + foo537(); + foo538(); + foo539(); + foo540(); + foo541(); + foo542(); + foo543(); + foo544(); + foo545(); + foo546(); + foo547(); + foo548(); + foo549(); + foo550(); + foo551(); + foo552(); + foo553(); + foo554(); + foo555(); + foo556(); + foo557(); + foo558(); + foo559(); + foo560(); + foo561(); + foo562(); + foo563(); + foo564(); + foo565(); + foo566(); + foo567(); + foo568(); + foo569(); + foo570(); + foo571(); + foo572(); + foo573(); + foo574(); + foo575(); + foo576(); + foo577(); + foo578(); + foo579(); + foo580(); + foo581(); + foo582(); + foo583(); + foo584(); + foo585(); + foo586(); + foo587(); + foo588(); + foo589(); + foo590(); + foo591(); + foo592(); + foo593(); + foo594(); + foo595(); + foo596(); + foo597(); + foo598(); + foo599(); + foo600(); + foo601(); + foo602(); + foo603(); + foo604(); + foo605(); + foo606(); + foo607(); + foo608(); + foo609(); + foo610(); + foo611(); + foo612(); + foo613(); + foo614(); + foo615(); + foo616(); + foo617(); + foo618(); + foo619(); + foo620(); + foo621(); + foo622(); + foo623(); + foo624(); + foo625(); + foo626(); + foo627(); + foo628(); + foo629(); + foo630(); + foo631(); + foo632(); + foo633(); + foo634(); + foo635(); + foo636(); + foo637(); + foo638(); + foo639(); + foo640(); + foo641(); + foo642(); + foo643(); + foo644(); + foo645(); + foo646(); + foo647(); + foo648(); + foo649(); + foo650(); + foo651(); + foo652(); + foo653(); + foo654(); + foo655(); + foo656(); + foo657(); + foo658(); + foo659(); + foo660(); + foo661(); + foo662(); + foo663(); + foo664(); + foo665(); + foo666(); + foo667(); + foo668(); + foo669(); + foo670(); + foo671(); + foo672(); + foo673(); + foo674(); + foo675(); + foo676(); + foo677(); + foo678(); + foo679(); + foo680(); + foo681(); + foo682(); + foo683(); + foo684(); + foo685(); + foo686(); + foo687(); + foo688(); + foo689(); + foo690(); + foo691(); + foo692(); + foo693(); + foo694(); + foo695(); + foo696(); + foo697(); + foo698(); + foo699(); + foo700(); + foo701(); + foo702(); + foo703(); + foo704(); + foo705(); + foo706(); + foo707(); + foo708(); + foo709(); + foo710(); + foo711(); + foo712(); + foo713(); + foo714(); + foo715(); + foo716(); + foo717(); + foo718(); + foo719(); + foo720(); + foo721(); + foo722(); + foo723(); + foo724(); + foo725(); + foo726(); + foo727(); + foo728(); + foo729(); + foo730(); + foo731(); + foo732(); + foo733(); + foo734(); + foo735(); + foo736(); + foo737(); + foo738(); + foo739(); + foo740(); + foo741(); + foo742(); + foo743(); + foo744(); + foo745(); + foo746(); + foo747(); + foo748(); + foo749(); + foo750(); + foo751(); + foo752(); + foo753(); + foo754(); + foo755(); + foo756(); + foo757(); + foo758(); + foo759(); + foo760(); + foo761(); + foo762(); + foo763(); + foo764(); + foo765(); + foo766(); + foo767(); + foo768(); + foo769(); + foo770(); + foo771(); + foo772(); + foo773(); + foo774(); + foo775(); + foo776(); + foo777(); + foo778(); + foo779(); + foo780(); + foo781(); + foo782(); + foo783(); + foo784(); + foo785(); + foo786(); + foo787(); + foo788(); + foo789(); + foo790(); + foo791(); + foo792(); + foo793(); + foo794(); + foo795(); + foo796(); + foo797(); + foo798(); + foo799(); + foo800(); + foo801(); + foo802(); + foo803(); + foo804(); + foo805(); + foo806(); + foo807(); + foo808(); + foo809(); + foo810(); + foo811(); + foo812(); + foo813(); +#if CASE <= 4 + foo814(); +#endif +#if CASE <= 3 + foo815(); +#endif +#if CASE <= 2 + foo816(); +#endif +#if CASE <= 1 + foo817(); +#endif + +// uint64_t t2 = mach_absolute_time(); +// fprintf(stderr, "total time = %lld\n", t2-t1); + +} + diff --git a/dyld/unit-tests/test-cases/big-jump-table/main.c b/dyld/unit-tests/test-cases/big-jump-table/main.c new file mode 100644 index 0000000..6241ef4 --- /dev/null +++ b/dyld/unit-tests/test-cases/big-jump-table/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + PASS("big-jump-table"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/big-jump-table/pointers.c b/dyld/unit-tests/test-cases/big-jump-table/pointers.c new file mode 100644 index 0000000..5484be0 --- /dev/null +++ b/dyld/unit-tests/test-cases/big-jump-table/pointers.c @@ -0,0 +1,23 @@ + +#include "foo.h" + +long useNonLazy() +{ + long result = 0; + result += (long)&foo001; + result += (long)&foo002; + result += (long)&foo003; +#if CASE > 0 + result += (long)&foo818; +#endif +#if CASE > 1 + result += (long)&foo817; +#endif +#if CASE > 2 + result += (long)&foo816; +#endif +#if CASE > 3 + result += (long)&foo815; +#endif + return result; +} diff --git a/dyld/unit-tests/test-cases/big-stack/Makefile b/dyld/unit-tests/test-cases/big-stack/Makefile new file mode 100644 index 0000000..4f8264a --- /dev/null +++ b/dyld/unit-tests/test-cases/big-stack/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2006-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# rosetta does not support very large stack sizes +STACK_SIZE = 0x83000000 +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + STACK_SIZE = 0x02100000 + endif +endif + + +ifeq "iPhoneOS" "$(OS_NAME)" + STACK_SIZE = 0x20000000 +endif + + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "big stack" "big stack failed" ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -Wl,-w -Wl,-stack_size -Wl,${STACK_SIZE} -DSTACK_SIZE=${STACK_SIZE} + +clean: + ${RM} ${RMFLAGS} main diff --git a/dyld/unit-tests/test-cases/big-stack/main.c b/dyld/unit-tests/test-cases/big-stack/main.c new file mode 100644 index 0000000..2c83e2d --- /dev/null +++ b/dyld/unit-tests/test-cases/big-stack/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +// +// This builds an executable that needs > 2GB of stack +// + + +char* keepAlive; // to keep compiler from optimizing away stack variable + +// keep recursing until desired stack size achieved +void foo(unsigned long long stackSize, char* stackStart) +{ + char buffer[32*1024*1024]; + keepAlive = buffer; + // only recursive if there is enough room for next buffer + intptr_t freeStackSpace = (buffer - sizeof(buffer)) - (stackStart - stackSize); + //fprintf(stderr, "&buffer=%p, stackStart=%p, freeStackSpace=0x%lx\n", buffer, stackStart, freeStackSpace); + if ( freeStackSpace < sizeof(buffer) ) + return; + else + foo(stackSize, stackStart); +} + + +int +main() +{ + char start; + foo(STACK_SIZE, &start); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/branch-islands/Makefile b/dyld/unit-tests/test-cases/branch-islands/Makefile new file mode 100644 index 0000000..7b746fd --- /dev/null +++ b/dyld/unit-tests/test-cases/branch-islands/Makefile @@ -0,0 +1,40 @@ +## +# 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 diff --git a/dyld/unit-tests/test-cases/branch-islands/extra.c b/dyld/unit-tests/test-cases/branch-islands/extra.c new file mode 100644 index 0000000..c90affa --- /dev/null +++ b/dyld/unit-tests/test-cases/branch-islands/extra.c @@ -0,0 +1,8 @@ +#include + + +bool test1() +{ + return false; +} + diff --git a/dyld/unit-tests/test-cases/branch-islands/main.c b/dyld/unit-tests/test-cases/branch-islands/main.c new file mode 100644 index 0000000..9a265a0 --- /dev/null +++ b/dyld/unit-tests/test-cases/branch-islands/main.c @@ -0,0 +1,26 @@ +#include +#include // exit(), EXIT_SUCCESS +#include +#include + +#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; +} + diff --git a/dyld/unit-tests/test-cases/branch-islands/space.s b/dyld/unit-tests/test-cases/branch-islands/space.s new file mode 100644 index 0000000..4fdd630 --- /dev/null +++ b/dyld/unit-tests/test-cases/branch-islands/space.s @@ -0,0 +1,77 @@ + +#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 diff --git a/dyld/unit-tests/test-cases/bundle-basic/Makefile b/dyld/unit-tests/test-cases/bundle-basic/Makefile new file mode 100644 index 0000000..c05f64e --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-basic/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-basic/bundle.c b/dyld/unit-tests/test-cases/bundle-basic/bundle.c new file mode 100644 index 0000000..64232df --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-basic/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/dyld/unit-tests/test-cases/bundle-basic/main.c b/dyld/unit-tests/test-cases/bundle-basic/main.c new file mode 100644 index 0000000..49d30c3 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-basic/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// 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"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } +#endif + PASS("bundle-basic"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-dont-gc/Makefile b/dyld/unit-tests/test-cases/bundle-dont-gc/Makefile new file mode 100644 index 0000000..5f4f05f --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-dont-gc/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c foo.bundle bar.bundle + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c + +bar.bundle : bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle bar.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-dont-gc/bar.c b/dyld/unit-tests/test-cases/bundle-dont-gc/bar.c new file mode 100644 index 0000000..3aee7ab --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-dont-gc/bar.c @@ -0,0 +1,3 @@ + +void bar() {} + diff --git a/dyld/unit-tests/test-cases/bundle-dont-gc/foo.c b/dyld/unit-tests/test-cases/bundle-dont-gc/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-dont-gc/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/bundle-dont-gc/main.c b/dyld/unit-tests/test-cases/bundle-dont-gc/main.c new file mode 100644 index 0000000..c79afe0 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-dont-gc/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ +// 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("bundle-dont-gc: NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "foo.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("bundle-dont-gc: NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_foo"); + if ( sym == NULL ) { + FAIL("bundle-dont-gc: NSLookupSymbolInModule failed"); + return 1; + } + + void* fooAddr = NSAddressOfSymbol(sym); + if ( fooAddr == NULL ) { + FAIL("bundle-dont-gc: NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("bundle-dont-gc: NSDestroyObjectFileImage failed"); + return 1; + } + + // open and close bar.bundle with new API, which causes a gc of images + void* h1 = dlopen("bar.bundle", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("bundle-dont-gc: can't dlopen bar.bundle: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h1); + + // make sure foo is still loaded + Dl_info info; + if ( dladdr(fooAddr, &info) == 0 ) { + FAIL("bundle-dont-gc: dladdr() failed"); + exit(0); + } + + // unload foo.bundle with old API + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } +#endif + + PASS("bundle-dont-gc"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/Makefile b/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/Makefile new file mode 100644 index 0000000..0a4e679 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c b/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c new file mode 100644 index 0000000..64232df --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/main.c b/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/main.c new file mode 100644 index 0000000..87eacca --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-all-infos/main.c @@ -0,0 +1,126 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-bad/Makefile b/dyld/unit-tests/test-cases/bundle-memory-load-bad/Makefile new file mode 100644 index 0000000..a6b0db5 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-bad/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## + + +# +# The test case verifies that dyld can cleanly recover from +# a main executable (test.bundle) being used with +# NSCreateObjectFileImageFromMemory() +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +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} -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-bad/bundle.c b/dyld/unit-tests/test-cases/bundle-memory-load-bad/bundle.c new file mode 100644 index 0000000..7556138 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-bad/bundle.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int main() +{ + return 0; +} diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-bad/main.c b/dyld/unit-tests/test-cases/bundle-memory-load-bad/main.c new file mode 100644 index 0000000..7e8c93c --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-bad/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 1; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 1; + } + + close(fd); + + // we are using a file not of type MH_BUNDLE, so NSCreateObjectFileImageFromMemory should fail + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) == NSObjectFileImageSuccess ) + FAIL("bundle-memory-load-bad"); + else +#endif + PASS("bundle-memory-load-bad"); + + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-fat/Makefile b/dyld/unit-tests/test-cases/bundle-memory-load-fat/Makefile new file mode 100644 index 0000000..00641d0 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-fat/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2005-2011 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 + +FATFLAGS = $(shell ${LIPO} -detailed_info $(IOSROOT)/usr/lib/libSystem.B.dylib | grep architecture | sed -e 's/architecture/-arch/') + +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 + echo ${IOSROOT} + echo $(IOSROOT) + ${CC} ${FATFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-fat/bundle.c b/dyld/unit-tests/test-cases/bundle-memory-load-fat/bundle.c new file mode 100644 index 0000000..3f96d53 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-fat/bundle.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// test to see if bss section is properly expanded + + +void check() +{ + +} diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-fat/main.c b/dyld/unit-tests/test-cases/bundle-memory-load-fat/main.c new file mode 100644 index 0000000..05ef670 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-fat/main.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +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"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 1; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 1; + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_check"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( func == NULL ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load-fat"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-malloc/Makefile b/dyld/unit-tests/test-cases/bundle-memory-load-malloc/Makefile new file mode 100644 index 0000000..0a4e679 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-malloc/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c b/dyld/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c new file mode 100644 index 0000000..64232df --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-malloc/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/dyld/unit-tests/test-cases/bundle-memory-load-malloc/main.c b/dyld/unit-tests/test-cases/bundle-memory-load-malloc/main.c new file mode 100644 index 0000000..47e8792 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load-malloc/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int fd = open("test.bundle", O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("open() failed"); + return 1; + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("fstat() failed"); + return 1; + } + + void* loadAddress = malloc((stat_buf.st_size+4095) & (-4096)); + if ( loadAddress == NULL ) { + FAIL("malloc failed"); + return 1; + } + + if ( pread(fd, loadAddress, stat_buf.st_size, 0) != stat_buf.st_size ) { + FAIL("pread() failed"); + return 1; + } + + //void* loadAddress2 = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + //fprintf(stderr, "loadAddress=%p\n", loadAddress); + //fprintf(stderr, "malloc_size(loadAddress) => 0x%08X\n", malloc_size(loadAddress)); + //fprintf(stderr, "loadAddress2=%p\n", loadAddress2); + //fprintf(stderr, "malloc_size(loadAddress2) => 0x%08X\n", malloc_size(loadAddress2)); + + + //free(loadAddress); + //fprintf(stderr, "malloc_size(loadAddress) => 0x%08X\n", malloc_size(loadAddress)); + if ( malloc_size(loadAddress) != 0 ) { + FAIL("malloc_size(loadAddress) => 0x%08X", malloc_size(loadAddress)); + FAIL("malloc still thinks it owns this block"); + return 1; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load-malloc"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-memory-load/Makefile b/dyld/unit-tests/test-cases/bundle-memory-load/Makefile new file mode 100644 index 0000000..0a4e679 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-memory-load/bundle.c b/dyld/unit-tests/test-cases/bundle-memory-load/bundle.c new file mode 100644 index 0000000..64232df --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/dyld/unit-tests/test-cases/bundle-memory-load/main.c b/dyld/unit-tests/test-cases/bundle-memory-load/main.c new file mode 100644 index 0000000..7af9854 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-memory-load/main.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +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 1; + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("mmap() failed"); + return 1; + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromMemory failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "he_he", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + + // Should check that loadAddress is unmmaped now (by call to NSDestroyObjectFileImage) +#endif + + PASS("bundle-memory-load"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-multi-link/Makefile b/dyld/unit-tests/test-cases/bundle-multi-link/Makefile new file mode 100644 index 0000000..02b7dca --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-multi-link/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/dyld/unit-tests/test-cases/bundle-multi-link/bundle.c b/dyld/unit-tests/test-cases/bundle-multi-link/bundle.c new file mode 100644 index 0000000..9871eea --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-multi-link/bundle.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +static int value = 0; + +int getValue() +{ + return value; +} + +void setValue(int v) +{ + value = v; +} + diff --git a/dyld/unit-tests/test-cases/bundle-multi-link/main.c b/dyld/unit-tests/test-cases/bundle-multi-link/main.c new file mode 100644 index 0000000..c15bd03 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-multi-link/main.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + + +/// +/// The point of this test case is to "load" a bundle once, but link it multiple times. +/// Each link creats a new instantiation of the bundle (e.g. new base address and new globals). +/// +/// + +typedef void (*setter)(int); +typedef int (*getter)(void); + +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 0; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_setValue"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 0; + } + + setter func = NSAddressOfSymbol(sym); + (*func)(1); + //fprintf(stderr, "address of foo() = %p in bundle first load %p\n", func, mod); + + + NSModule mod2 = NSLinkModule(ofi, "test2.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod2 == NULL ) { + NSLinkEditErrors c; int errorNumber; const char* fileName; const char* errorString; + NSLinkEditError(&c, &errorNumber, &fileName, &errorString); + FAIL("2nd NSLinkModule failed: %s", errorString); + return 0; + } + if ( mod == mod2 ) { + FAIL("2nd NSLinkModule return same function address as first"); + return 0; + } + + NSSymbol sym2getter = NSLookupSymbolInModule(mod2, "_getValue"); + if ( sym2getter == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed"); + return 0; + } + getter func2getter = NSAddressOfSymbol(sym2getter); + if ( (*func2getter)() != 0 ) { + FAIL("_getValue() on second link returned non-zero"); + return 0; + } + + NSSymbol sym2 = NSLookupSymbolInModule(mod2, "_setValue"); + if ( sym2 == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed"); + return 0; + } + setter func2 = NSAddressOfSymbol(sym2); + (*func2)(2); + + //fprintf(stderr, "address of foo() = %p in bundle second load %p\n", func2, mod2); + if ( func == func2 ) { + FAIL("2nd NSAddressOfSymbol return same function address as 1st"); + return 0; + } + + + NSModule mod3 = NSLinkModule(ofi, "test3.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod3 == NULL ) { + FAIL("3rd NSLinkModule failed"); + return 0; + } + if ( mod3 == mod ) { + FAIL("3rd NSLinkModule return same function address as 1st"); + return 0; + } + if ( mod3 == mod2 ) { + FAIL("3rd NSLinkModule return same function address as 2nd"); + return 0; + } + + NSSymbol sym3 = NSLookupSymbolInModule(mod3, "_setValue"); + if ( sym3 == NULL ) { + FAIL("3rd NSLookupSymbolInModule failed"); + return 0; + } + setter func3 = NSAddressOfSymbol(sym3); + (*func3)(3); + //fprintf(stderr, "address of foo() = %p in bundle third load %p\n", func3, mod3); + if ( func3 == func ) { + FAIL("3rd NSAddressOfSymbol return same function address as 1st"); + return 0; + } + if ( func3 == func2 ) { + FAIL("3rd NSAddressOfSymbol return same function address as 2nd"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSUnLinkModule(mod3, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("3rd NSUnLinkModule failed"); + return 0; + } + + if ( !NSUnLinkModule(mod2, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("2nd NSUnLinkModule failed"); + return 0; + } + + // now link again after unlinking everything + NSModule mod4 = NSLinkModule(ofi, "test4.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod4 == NULL ) { + FAIL("4th NSLinkModule failed"); + return 0; + } + + // check that this is really a new copy by verifying the getValue() returns zero + NSSymbol sym4getter = NSLookupSymbolInModule(mod4, "_getValue"); + if ( sym4getter == NULL ) { + FAIL("4th NSLookupSymbolInModule failed"); + return 0; + } + getter func4getter = NSAddressOfSymbol(sym4getter); + if ( (*func4getter)() != 0 ) { + FAIL("_getValue() on fourth link returned non-zero"); + return 0; + } + + if ( !NSUnLinkModule(mod4, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("4th NSUnLinkModule failed"); + return 0; + } + + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 0; + } +#endif + + PASS("bundle-multi-link"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/bundle-multi-load/Makefile b/dyld/unit-tests/test-cases/bundle-multi-load/Makefile new file mode 100644 index 0000000..02b7dca --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-multi-load/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/dyld/unit-tests/test-cases/bundle-multi-load/bundle.c b/dyld/unit-tests/test-cases/bundle-multi-load/bundle.c new file mode 100644 index 0000000..84955c4 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-multi-load/bundle.c @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +int foo() +{ + return 4; +} + diff --git a/dyld/unit-tests/test-cases/bundle-multi-load/main.c b/dyld/unit-tests/test-cases/bundle-multi-load/main.c new file mode 100644 index 0000000..ab0382a --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-multi-load/main.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +/// +/// The point of this test is to load the same bundle file multiple times and +/// verify each time it is linked is a new instantiations (new globals, etc) +/// + +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 0; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_foo"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 0; + } + + void* func = NSAddressOfSymbol(sym); + //fprintf(stderr, "1st address of foo() = %p in module %p in OFI %p\n", func, mod, ofi); + + + NSObjectFileImage ofi2; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi2) != NSObjectFileImageSuccess ) { + FAIL("2nd NSCreateObjectFileImageFromFile failed"); + return 0; + } + + NSModule mod2 = NSLinkModule(ofi2, "test2.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod2 == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed"); + return 0; + } + if ( mod == mod2 ) { + FAIL("2nd NSLinkModule return same function address as first\n"); + return 0; + } + + NSSymbol sym2 = NSLookupSymbolInModule(mod2, "_foo"); + if ( sym2 == NULL ) { + FAIL("2nd NSLookupSymbolInModule failed\n"); + return 0; + } + + void* func2 = NSAddressOfSymbol(sym2); + //fprintf(stderr, "2nd address of foo() = %p in module %p in OFI %p\n", func2, mod2, ofi2); + if ( func == func2 ) { + FAIL("2nd NSAddressOfSymbol return same function address as 1st\n"); + return 0; + } + + + NSObjectFileImage ofi3; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi3) != NSObjectFileImageSuccess ) { + FAIL("3rd NSCreateObjectFileImageFromFile failed"); + return 0; + } + NSModule mod3 = NSLinkModule(ofi3, "test3.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod3 == NULL ) { + FAIL("3rd NSLinkModule failed\n"); + return 0; + } + if ( mod3 == mod ) { + FAIL("3rd NSLinkModule return same function address as 1st\n"); + return 0; + } + if ( mod3 == mod2 ) { + FAIL("3rd NSLinkModule return same function address as 2nd\n"); + return 0; + } + + NSSymbol sym3 = NSLookupSymbolInModule(mod3, "_foo"); + if ( sym3 == NULL ) { + FAIL("3rd NSLookupSymbolInModule failed\n"); + return 0; + } + void* func3 = NSAddressOfSymbol(sym3); + //fprintf(stderr, "3rd address of foo() = %p in module %p in OFI %p\n", func3, mod3, ofi3); + if ( func3 == func ) { + FAIL("3rd NSAddressOfSymbol return same function address as 1st\n"); + return 0; + } + if ( func3 == func2 ) { + FAIL("3rd NSAddressOfSymbol return same function address as 2nd\n"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSUnLinkModule(mod3, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("3rd NSUnLinkModule failed"); + return 0; + } + + // note, we are calling NSDestroyObjectFileImage() before NSUnLinkModule() + if ( !NSDestroyObjectFileImage(ofi2) ) { + FAIL("2nd NSDestroyObjectFileImage failed"); + return 0; + } + if ( !NSUnLinkModule(mod2, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("2nd NSUnLinkModule failed"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("1st NSDestroyObjectFileImage failed"); + return 0; + } + if ( !NSDestroyObjectFileImage(ofi3) ) { + FAIL("3rd NSDestroyObjectFileImage failed"); + return 0; + } +#endif + PASS("bundle-multi-load"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/bundle-name-ownership/Makefile b/dyld/unit-tests/test-cases/bundle-name-ownership/Makefile new file mode 100644 index 0000000..d5b047f --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-name-ownership/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-name-ownership/bundle.c b/dyld/unit-tests/test-cases/bundle-name-ownership/bundle.c new file mode 100644 index 0000000..64232df --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-name-ownership/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[1000000]; + +bool checkdata() +{ + return ( mydata[500000] == 0 ); +} diff --git a/dyld/unit-tests/test-cases/bundle-name-ownership/main.c b/dyld/unit-tests/test-cases/bundle-name-ownership/main.c new file mode 100644 index 0000000..15c5563 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-name-ownership/main.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// 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; + if ( NSCreateObjectFileImageFromFile(path, &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 0; + } + + NSModule mod = NSLinkModule(ofi, path, NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 0; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 0; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 0; + } + + Dl_info info; + if ( dladdr(func, &info) == 0 ) { + FAIL("dladdr(func, &info) failed"); + return 0; + } + + if ( info.dli_fname == path ) { + FAIL("NSLinkModule() did not make a copy of the path"); + return 0; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 0; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 0; + } +#endif + PASS("bundle-name-ownership"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-private/Makefile b/dyld/unit-tests/test-cases/bundle-private/Makefile new file mode 100644 index 0000000..72ca403 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-private/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-private/bundle.c b/dyld/unit-tests/test-cases/bundle-private/bundle.c new file mode 100644 index 0000000..b7cdc4f --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-private/bundle.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// test to see if NSLINKMODULE_OPTION_PRIVATE works + +void findme() +{ +} diff --git a/dyld/unit-tests/test-cases/bundle-private/main.c b/dyld/unit-tests/test-cases/bundle-private/main.c new file mode 100644 index 0000000..2a35990 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-private/main.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule modPriv = NSLinkModule(ofi, "test.bundle-private", NSLINKMODULE_OPTION_PRIVATE); + if ( modPriv == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol symPriv = NSLookupSymbolInModule(modPriv, "_findme"); + if ( symPriv == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + if ( NSIsSymbolNameDefined("_findme") ) { + FAIL("NSIsSymbolNameDefined (incorrectly) found symbol in private bundle"); + return 1; + } + + NSModule modPublic = NSLinkModule(ofi, "test.bundle-public", NSLINKMODULE_OPTION_NONE); + if ( modPublic == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol symPublic = NSLookupSymbolInModule(modPublic, "_findme"); + if ( symPublic == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + if ( !NSIsSymbolNameDefined("_findme") ) { + FAIL("NSIsSymbolNameDefined did not found symbol in public bundle"); + return 1; + } + + if ( !NSUnLinkModule(modPriv, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSUnLinkModule(modPublic, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( NSIsSymbolNameDefined("_findme") ) { + FAIL("NSIsSymbolNameDefined found unlinked symbol in public bundle"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } +#endif + PASS("bundle-private"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-reload/Makefile b/dyld/unit-tests/test-cases/bundle-reload/Makefile new file mode 100644 index 0000000..723c2c6 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-reload/Makefile @@ -0,0 +1,48 @@ +## +# 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 + +ifeq "ppc" "$(ARCH)" + CXX_VERSION = g++-3.3 +else + CXX_VERSION = ${CXX} +endif + +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.cxx + ${CXX} ${CXXFLAGS} -bundle -o test.bundle bundle.cxx + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/dyld/unit-tests/test-cases/bundle-reload/bundle.cxx b/dyld/unit-tests/test-cases/bundle-reload/bundle.cxx new file mode 100644 index 0000000..a8651c4 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-reload/bundle.cxx @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern "C" +void foo() +{ +} + +int __attribute__((weak)) blah = 10; diff --git a/dyld/unit-tests/test-cases/bundle-reload/main.c b/dyld/unit-tests/test-cases/bundle-reload/main.c new file mode 100644 index 0000000..9f0f0c1 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-reload/main.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +/// +/// The point of this test is to load and unload a bundle and +/// verify that the address->image cache is properly invalidated. rdar://problem/4212667 +/// + +typedef void (*fooProc)(); + +// 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"); + exit(0); + } + + NSModule mod = NSLinkModule(ofi,"test.bundle", NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + exit(0); + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_foo"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + exit(0); + } + + fooProc func = (fooProc)NSAddressOfSymbol(sym); + (*func)(); + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_NONE) ) { + FAIL("NSUnLinkModule failed"); + exit(0); + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + 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 +} + + +int main() +{ + _dyld_register_func_for_remove_image(&myRemoveImage); + + doit(); + doit(); + doit(); + + PASS("bundle-reload"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/bundle-terminator/Makefile b/dyld/unit-tests/test-cases/bundle-terminator/Makefile new file mode 100644 index 0000000..8e11cff --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-terminator/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CXX} ${CXXFLAGS} -bundle -o test.bundle bundle.cxx + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-terminator/bundle.cxx b/dyld/unit-tests/test-cases/bundle-terminator/bundle.cxx new file mode 100644 index 0000000..b94ced2 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-terminator/bundle.cxx @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +class Foo { +public: + Foo() { buffer = malloc(100); } + ~Foo() { free(buffer); } + void* get() { return buffer; } +private: + void* buffer; + +}; + + +Foo myObject; + diff --git a/dyld/unit-tests/test-cases/bundle-terminator/main.c b/dyld/unit-tests/test-cases/bundle-terminator/main.c new file mode 100644 index 0000000..d7fd707 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-terminator/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + +#endif + + PASS("bundle-terminator"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-unlinkable/Makefile b/dyld/unit-tests/test-cases/bundle-unlinkable/Makefile new file mode 100644 index 0000000..b872e6c --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-unlinkable/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +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 libstuff.dylib + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c libstuff.dylib + +libstuff.dylib : lib.c + ${CC} ${CCFLAGS} lib.c -dynamiclib -o libstuff.dylib -install_name libcantfind.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle libstuff.dylib + diff --git a/dyld/unit-tests/test-cases/bundle-unlinkable/bundle.c b/dyld/unit-tests/test-cases/bundle-unlinkable/bundle.c new file mode 100644 index 0000000..8f91e11 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-unlinkable/bundle.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +extern int bar(); + + +int foo() +{ + return bar(); +} + diff --git a/dyld/unit-tests/test-cases/bundle-unlinkable/lib.c b/dyld/unit-tests/test-cases/bundle-unlinkable/lib.c new file mode 100644 index 0000000..541b964 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-unlinkable/lib.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + + +int bar() +{ + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-unlinkable/main.c b/dyld/unit-tests/test-cases/bundle-unlinkable/main.c new file mode 100644 index 0000000..bccdf8d --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-unlinkable/main.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + // make sure not-yet-linked-ofi is not visible through _dyld_get_image_name + int count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strcmp(name, "test.bundle") == 0 ) { + FAIL("unlinked test.bundle found via _dyld_get_image_name()"); + return 1; + } + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod != NULL ) { + FAIL("NSLinkModule succeeded but should have failed"); + return 1; + } + + // make sure link-failed-ofi is not visible through _dyld_get_image_name + count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strcmp(name, "test.bundle") == 0 ) { + FAIL("failed linked test.bundle found via _dyld_get_image_name()"); + return 1; + } + } +#endif + PASS("bundle-unlinkable"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile b/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile new file mode 100644 index 0000000..d5b047f --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/bundle.c b/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/bundle.c new file mode 100644 index 0000000..56690a7 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +// test to see if bss section is properly expanded + +static int mydata[10]; + +bool checkdata() +{ + return ( mydata[9] == 0 ); +} diff --git a/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/main.c b/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/main.c new file mode 100644 index 0000000..018ee9e --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-unload-keep-mapped/main.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ +// NSCreateObjectFileImageFromMemory is only available on Mac OS X - not iPhone OS +#if __MAC_OS_X_VERSION_MIN_REQUIRED + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_NONE); + if ( mod == NULL ) { + FAIL("NSLinkModule failed"); + return 1; + } + + NSSymbol sym = NSLookupSymbolInModule(mod, "_checkdata"); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInModule failed"); + return 1; + } + + CheckFunc func = NSAddressOfSymbol(sym); + if ( !func() ) { + FAIL("NSAddressOfSymbol failed"); + return 1; + } + + if ( !NSUnLinkModule(mod, NSUNLINKMODULE_OPTION_KEEP_MEMORY_MAPPED) ) { + FAIL("NSUnLinkModule failed"); + return 1; + } + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + return 1; + } + + // call function again, even though bundle is unloaded + func(); +#endif + + PASS("bundle-basic"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-v-dylib/Makefile b/dyld/unit-tests/test-cases/bundle-v-dylib/Makefile new file mode 100644 index 0000000..e706883 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-v-dylib/Makefile @@ -0,0 +1,50 @@ +## +# 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 + +main : main.c bar.dylib foo.bundle foo.dylib foo2.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c bar.dylib + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle -o foo.bundle foo.c + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -o foo.dylib foo.c + +foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -o foo2.dylib foo.c + +bar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -o bar.dylib bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib foo2.dylib bar.dylib + diff --git a/dyld/unit-tests/test-cases/bundle-v-dylib/bar.c b/dyld/unit-tests/test-cases/bundle-v-dylib/bar.c new file mode 100644 index 0000000..65f9d64 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-v-dylib/bar.c @@ -0,0 +1,29 @@ +/* + * 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 + + +void bar() +{ + +} diff --git a/dyld/unit-tests/test-cases/bundle-v-dylib/foo.c b/dyld/unit-tests/test-cases/bundle-v-dylib/foo.c new file mode 100644 index 0000000..91fbe46 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-v-dylib/foo.c @@ -0,0 +1,29 @@ +/* + * 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 + + +void foo() +{ + +} diff --git a/dyld/unit-tests/test-cases/bundle-v-dylib/main.c b/dyld/unit-tests/test-cases/bundle-v-dylib/main.c new file mode 100644 index 0000000..aea5c6f --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-v-dylib/main.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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) +{ + int fd = open(path, O_RDONLY, 0); + if ( fd == -1 ) { + FAIL("bundle-v-dylib: open() failed"); + exit(0); + } + + struct stat stat_buf; + if ( fstat(fd, &stat_buf) == -1) { + FAIL("bundle-v-dylib: fstat() failed"); + exit(0); + } + + void* loadAddress = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0); + if ( loadAddress == ((void*)(-1)) ) { + FAIL("bundle-v-dylib: mmap() failed"); + exit(0); + } + + close(fd); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromMemory(loadAddress, stat_buf.st_size, &ofi) == NSObjectFileImageSuccess ) { + FAIL("bundle-v-dylib: NSCreateObjectFileImageFromMemory() incorrectly allowed %s to be loaded", path); + exit(0); + } +} + +void loadAsBundle(const char* path) +{ + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(path, &ofi) == NSObjectFileImageSuccess ) { + FAIL("bundle-v-dylib: NSCreateObjectFileImageFromFile() incorrectly allowed %s to be loaded", path); + exit(0); + } +} + +void loadAsDylib(const char* path) +{ + if ( NSAddImage(path, NSADDIMAGE_OPTION_RETURN_ON_ERROR) != NULL ) { + FAIL("bundle-v-dylib: NSAddImage() incorrectly allowed %s to be loaded", path); + exit(0); + } +} + +#endif + +int main() +{ +#if __MAC_OS_X_VERSION_MIN_REQUIRED + int dummy; + + // verify that NSAddImage fails to load MH_BUNDLE + loadAsDylib("foo.bundle"); + + // verify that NSCreateObjectFileImageFromFile fails to load MH_DYLIB + loadAsBundle("foo.dylib"); + + // verify that NSCreateObjectFileImageFromFile fails to load MH_DYLIB already linked against main + loadAsBundle("bar.dylib"); + + // verify that bar.dylib was not unloaded when above failed + bar(); + + // try loading a dylib from memory using bundle API's + loadAsBundleFromMemory("foo2.dylib"); + + // verify that dyld data structures are not wanked by scanning all images + _dyld_get_image_header_containing_address(&dummy); +#endif + PASS("bundle-v-dylib"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/bundle-weak/Makefile b/dyld/unit-tests/test-cases/bundle-weak/Makefile new file mode 100644 index 0000000..7cdc170 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-weak/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.cxx + ${CXX} ${CCXXFLAGS} -bundle -o test.bundle bundle.cxx + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/bundle-weak/bundle.cxx b/dyld/unit-tests/test-cases/bundle-weak/bundle.cxx new file mode 100644 index 0000000..fb36871 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-weak/bundle.cxx @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +bool test() +{ + try { + std::vector().at(1); + } + catch(const std::out_of_range&) { + //fprintf(stderr, "caught out_of_range\n"); + return true; + } + catch(...) { + //fprintf(stderr, "caught something\n"); + } + return false; +} diff --git a/dyld/unit-tests/test-cases/bundle-weak/main.c b/dyld/unit-tests/test-cases/bundle-weak/main.c new file mode 100644 index 0000000..814c406 --- /dev/null +++ b/dyld/unit-tests/test-cases/bundle-weak/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +typedef bool (*CheckFunc)(); + +int main() +{ + void* handle = dlopen("test.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"test.bundle\") failed: %s", dlerror()); + return 0; + } + + CheckFunc func = (CheckFunc)dlsym(handle, "_Z4testv"); + if ( func == NULL ) { + FAIL("dlsym(handle, \"__Z4testv\") failed"); + return 0; + } + + if ( func() ) + PASS("bundle-weak"); + else + FAIL("bundle-weak"); + + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/Makefile b/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/Makefile new file mode 100644 index 0000000..4a7020a --- /dev/null +++ b/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/Makefile @@ -0,0 +1,42 @@ +## +# 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} foo.c -dynamiclib -o libfoo3.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo4.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libfoo3.dylib libfoo4.dylib + diff --git a/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/foo.c b/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/foo.c new file mode 100644 index 0000000..3f2cbaf --- /dev/null +++ b/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/foo.c @@ -0,0 +1,4 @@ + +void foo() {} +void bar() {} +void baz() {} diff --git a/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/main.c b/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/main.c new file mode 100644 index 0000000..d5199c9 --- /dev/null +++ b/dyld/unit-tests/test-cases/concurrent-dlopen-initializers/main.c @@ -0,0 +1,94 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void* work(void* libName) +{ + for (int i=0; i < 500; ++i) { + void* handle = dlopen((char*)libName, 0); + if ( handle == NULL ) { + FAIL("dlopen failed: %s", dlerror()); + exit(0); + } + dlclose(handle); + } + + return NULL; +} + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + usleep(10000); + return NULL; +} + + +int main() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, batchMappedHandler); + + // other thread dlopens and closes libfoo.dylib 500 times + pthread_t t1; + if ( pthread_create(&t1, NULL, work, "libfoo1.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t t2; + if ( pthread_create(&t2, NULL, work, "libfoo2.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t t3; + if ( pthread_create(&t3, NULL, work, "libfoo3.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t t4; + if ( pthread_create(&t4, NULL, work, "libfoo4.dylib") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + pthread_join(t1, &result); + pthread_join(t2, &result); + pthread_join(t3, &result); + pthread_join(t4, &result); + + PASS("concurrent-dlopen-initializers"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/coreSymbolication-notify/Makefile b/dyld/unit-tests/test-cases/coreSymbolication-notify/Makefile new file mode 100644 index 0000000..d0b5b87 --- /dev/null +++ b/dyld/unit-tests/test-cases/coreSymbolication-notify/Makefile @@ -0,0 +1,52 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/coreSymbolication-notify/bar.c b/dyld/unit-tests/test-cases/coreSymbolication-notify/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/coreSymbolication-notify/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/dyld/unit-tests/test-cases/coreSymbolication-notify/foo.c b/dyld/unit-tests/test-cases/coreSymbolication-notify/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/coreSymbolication-notify/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/coreSymbolication-notify/main.c b/dyld/unit-tests/test-cases/coreSymbolication-notify/main.c new file mode 100644 index 0000000..56ca5f4 --- /dev/null +++ b/dyld/unit-tests/test-cases/coreSymbolication-notify/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + + 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; +} diff --git a/dyld/unit-tests/test-cases/crt-apple/Makefile b/dyld/unit-tests/test-cases/crt-apple/Makefile new file mode 100644 index 0000000..73b71ac --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-apple/Makefile @@ -0,0 +1,59 @@ +## +# 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@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +# +# verify that apple[0] parameter is correct by comparing to argv[1] +# + +all-check: all check + +check: + ./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 2>/dev/null + +all: main main.stripped main-setuid + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -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 + + +clean: + ${RM} ${RMFLAGS} *~ main main.stripped main-setuid + + diff --git a/dyld/unit-tests/test-cases/crt-apple/main.c b/dyld/unit-tests/test-cases/crt-apple/main.c new file mode 100644 index 0000000..73a1922 --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-apple/main.c @@ -0,0 +1,84 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include <_simple.h> + +/// +/// 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); + } + + const char* execPath = _simple_getenv(apple, "executable_path"); + if ( execPath == NULL ) + FAIL("crt-apple apple[] missing executable_path="); + else if ( strcmp(execPath, argv[1]) == 0 ) + PASS("crt-apple %s", execPath); + else + FAIL("crt-apple %s", execPath); + + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/crt-argv-NULL/Makefile b/dyld/unit-tests/test-cases/crt-argv-NULL/Makefile new file mode 100644 index 0000000..f1865bb --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-argv-NULL/Makefile @@ -0,0 +1,45 @@ +## +# 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 + +# +# verifies that crt glue can handle argv[0] = NULL +# + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "crt-argv-NULL main" "crt-argv-NULL main" ./main + + +all: main + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + + diff --git a/dyld/unit-tests/test-cases/crt-argv-NULL/main.c b/dyld/unit-tests/test-cases/crt-argv-NULL/main.c new file mode 100644 index 0000000..1adc975 --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-argv-NULL/main.c @@ -0,0 +1,14 @@ + + #include + + +int main(int argc, const char* argv[]) +{ + if ( argv[0] != NULL ) { + // re-exec with empty argv[] array + char* const emptyArgv[] = { NULL }; + execv(argv[0], emptyArgv); + } + + return 0; +} diff --git a/dyld/unit-tests/test-cases/crt-custom/Makefile b/dyld/unit-tests/test-cases/crt-custom/Makefile new file mode 100644 index 0000000..7bb8ddc --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-custom/Makefile @@ -0,0 +1,46 @@ +## +# 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 + +# +# verifies that 10.4 binaries with a custom entry point +# have the entry point called before initializers are run +# + +all-check: all check + +check: + ./main + + +all: main + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main mystart.s main.c -e _mystart -Wl,-no_new_main + + +clean: + ${RM} ${RMFLAGS} *~ main + + diff --git a/dyld/unit-tests/test-cases/crt-custom/main.c b/dyld/unit-tests/test-cases/crt-custom/main.c new file mode 100644 index 0000000..29c79cf --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-custom/main.c @@ -0,0 +1,81 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// the value of flag is altered by mystart +int flag = 0; + + +#if __LP64__ + // for 64-bit binaries initializers are always called before entry point + #define ENTRY_BEFORE_INIT 0 +#else + #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 + + +void __attribute__((constructor)) myinit() +{ +#if ENTRY_BEFORE_INIT + if ( flag != 2 ) { + FAIL("crt-custom entry point not called before initializer"); + exit(0); + } +#endif + flag = 1; +} + + +int main() +{ +#if ENTRY_BEFORE_INIT + if ( flag != 1 ) { + FAIL("crt-custom initializer not called"); + exit(0); + } +#else + if ( flag != 2 ) { + FAIL("crt-custom entry not called"); + exit(0); + } +#endif + + PASS("crt-custom"); + + return 0; +} diff --git a/dyld/unit-tests/test-cases/crt-custom/mystart.s b/dyld/unit-tests/test-cases/crt-custom/mystart.s new file mode 100644 index 0000000..c507706 --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-custom/mystart.s @@ -0,0 +1,44 @@ +# Built output for (null) +# Generated at (null) +# Using (null) configuration, (null) architecture for (null) target of (null) project + + + + + .text + .align 2 + .globl _mystart +_mystart: +#if __i386__ + 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) + jmp start +#elif __ppc__ || __ppc64__ + li r0,2 + 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 + + diff --git a/dyld/unit-tests/test-cases/crt-libSystem/Makefile b/dyld/unit-tests/test-cases/crt-libSystem/Makefile new file mode 100644 index 0000000..367730a --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-libSystem/Makefile @@ -0,0 +1,50 @@ +## +# 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 + +# +# verifies that _NS* routines in libSystem properly find global variables in main executable. +# the mechanism for 10.4 and 10.5 is different +# + + +all-check: all check + +check: + ./main + ./main.stripped + +all: main main.stripped + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include $(CRTLIB) -o main main.c + +main.stripped: main + ${STRIP} main -o main.stripped + +clean: + ${RM} ${RMFLAGS} *~ main main.stripped + + diff --git a/dyld/unit-tests/test-cases/crt-libSystem/main.c b/dyld/unit-tests/test-cases/crt-libSystem/main.c new file mode 100644 index 0000000..b7027df --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-libSystem/main.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +struct ProgramVars +{ + const void* mh; + int* NXArgcPtr; + char*** NXArgvPtr; + char*** environPtr; + char** __prognamePtr; +}; +static const struct ProgramVars* sVars; + +// global variables defeined in crt1.o +extern char** NXArgv; +extern int NXArgc; +extern char** environ; +extern char* __progname; + + +int +main(int argc, const char* argv[]) +{ + bool success = true; + + if ( _NSGetArgv() != &NXArgv ) { + FAIL("crt-libSystem _NSGetArgv() != &NXArgv (%p!=%p) for %s", _NSGetArgv(), &NXArgv, argv[0]); + success = false; + } + + if ( _NSGetArgc() != &NXArgc ) { + FAIL("crt-libSystem _NSGetArgc() != &NXArgc (%p!=%p) for %s", _NSGetArgc(), &NXArgc, argv[0]); + success = false; + } + + if ( _NSGetEnviron() != &environ ) { + FAIL("crt-libSystem _NSGetEnviron() != &environv (%p!=%p) for %s", _NSGetEnviron(), &environ, argv[0]); + success = false; + } + + if ( _NSGetProgname() != &__progname ) { + FAIL("crt-libSystem _NSGetProgname() != &__progname (%p!=%p) for %s", _NSGetProgname(), &__progname, argv[0]); + success = false; + } + + if ( _NSGetMachExecuteHeader() != &_mh_execute_header ) { + FAIL("crt-libSystem _NSGetMachExecuteHeader() != &_mh_execute_headerv (%p!=%p) for %s", _NSGetMachExecuteHeader(), &_mh_execute_header, argv[0]); + success = false; + } + + if ( sVars->NXArgvPtr != &NXArgv ) { + FAIL("crt-libSystem sVars->NXArgvPtr != &NXArg (%p!=%p) for %s", sVars->NXArgvPtr, &NXArgv, argv[0]); + success = false; + } + + if ( sVars->NXArgcPtr != &NXArgc ) { + FAIL("crt-libSystem sVars->NXArgcPtr != &NXArgc (%p!=%p) for %s", sVars->NXArgcPtr, &NXArgc, argv[0]); + success = false; + } + + if ( sVars->environPtr != &environ ) { + FAIL("crt-libSystem sVars->environPtr != &environ (%p!=%p) for %s", sVars->environPtr, &environ, argv[0]); + success = false; + } + + if ( sVars->__prognamePtr != &__progname ) { + FAIL("crt-libSystem sVars->__prognamePtr != &__progname (%p!=%p) for %s", sVars->__prognamePtr, &__progname, argv[0]); + success = false; + } + + if ( sVars->mh != &_mh_execute_header ) { + FAIL("crt-libSystem sVars->mh != &_mh_execute_header (%p!=%p) for %s", sVars->mh, &_mh_execute_header, argv[0]); + success = false; + } + + if ( success ) + PASS("crt-libSystem"); + + return EXIT_SUCCESS; +} + + + +void __attribute__((constructor)) +myInit(int argc, const char* argv[], const char* envp[], const char* apple[], const struct ProgramVars* vars) +{ + sVars = vars; +} + + + + + diff --git a/dyld/unit-tests/test-cases/crt-result/Makefile b/dyld/unit-tests/test-cases/crt-result/Makefile new file mode 100644 index 0000000..a0cf8ed --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-result/Makefile @@ -0,0 +1,50 @@ +## +# 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 + +# +# verifies that the return value from main() makes it to shell +# for both crt1.0 and crt1.10.5.o +# + +all-check: all check + +check: + ${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 bad + + +good: good.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o good good.c + +bad: bad.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o bad bad.c + +clean: + ${RM} ${RMFLAGS} *~ good bad + + diff --git a/dyld/unit-tests/test-cases/crt-result/bad.c b/dyld/unit-tests/test-cases/crt-result/bad.c new file mode 100644 index 0000000..40cbb54 --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-result/bad.c @@ -0,0 +1 @@ +int main() { return 1; } diff --git a/dyld/unit-tests/test-cases/crt-result/good.c b/dyld/unit-tests/test-cases/crt-result/good.c new file mode 100644 index 0000000..76e8197 --- /dev/null +++ b/dyld/unit-tests/test-cases/crt-result/good.c @@ -0,0 +1 @@ +int main() { return 0; } diff --git a/dyld/unit-tests/test-cases/cxa_finalize/Makefile b/dyld/unit-tests/test-cases/cxa_finalize/Makefile new file mode 100644 index 0000000..44b4c0c --- /dev/null +++ b/dyld/unit-tests/test-cases/cxa_finalize/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib foo.cxx -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/cxa_finalize/foo.cxx b/dyld/unit-tests/test-cases/cxa_finalize/foo.cxx new file mode 100644 index 0000000..e073170 --- /dev/null +++ b/dyld/unit-tests/test-cases/cxa_finalize/foo.cxx @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +class A +{ +public: + A() { f = 10; } + ~A() { if ( f == 0 ) abort(); f = 0; } + int get() { return f; } +private: + int f; +}; + +A a; + + +int test() +{ + return a.get(); +} diff --git a/dyld/unit-tests/test-cases/cxa_finalize/main.c b/dyld/unit-tests/test-cases/cxa_finalize/main.c new file mode 100644 index 0000000..d1b62d5 --- /dev/null +++ b/dyld/unit-tests/test-cases/cxa_finalize/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + return EXIT_SUCCESS; + } + + dlclose(handle); + + PASS("cxa_finalize"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/deadlock/Makefile b/dyld/unit-tests/test-cases/deadlock/Makefile new file mode 100644 index 0000000..c01b46d --- /dev/null +++ b/dyld/unit-tests/test-cases/deadlock/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c bar.dylib foo.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c foo.dylib + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -o foo.dylib foo.c + +bar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -o bar.dylib bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.dylib bar.dylib + diff --git a/dyld/unit-tests/test-cases/deadlock/bar.c b/dyld/unit-tests/test-cases/deadlock/bar.c new file mode 100644 index 0000000..ea63b5f --- /dev/null +++ b/dyld/unit-tests/test-cases/deadlock/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +void bar() +{ + +} diff --git a/dyld/unit-tests/test-cases/deadlock/foo.c b/dyld/unit-tests/test-cases/deadlock/foo.c new file mode 100644 index 0000000..8b9c637 --- /dev/null +++ b/dyld/unit-tests/test-cases/deadlock/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +void foo() +{ + +} diff --git a/dyld/unit-tests/test-cases/deadlock/main.c b/dyld/unit-tests/test-cases/deadlock/main.c new file mode 100644 index 0000000..21e6446 --- /dev/null +++ b/dyld/unit-tests/test-cases/deadlock/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include + +#include "test.h" + +/// rdar://problem/3811777 + +// barrier thread 1 thread 2 +// add image +// 1 +// acquire sMyLock +// 2 +// in callback acquire sMyLock call lazy pointer +// release sMyLock release sMyLock + +extern void foo(); + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +static volatile int sBarrier = 0; +static pthread_mutex_t sBarrierMutex; +static pthread_cond_t sBarrierFree; + +static void blockUntilBarrier(int n) +{ + pthread_mutex_lock(&sBarrierMutex); + while ( sBarrier < n ) + pthread_cond_wait(&sBarrierFree, &sBarrierMutex); + pthread_mutex_unlock(&sBarrierMutex); +} + +static void advanceToBarrier(int n) +{ + pthread_mutex_lock(&sBarrierMutex); + sBarrier = n; + pthread_cond_broadcast(&sBarrierFree); + pthread_mutex_unlock(&sBarrierMutex); +} + + + + + +static pthread_mutex_t sMyLock; + +static void* thread2(void* arg) +{ + // thread 2 + blockUntilBarrier(1); + pthread_mutex_lock(&sMyLock); + advanceToBarrier(2); + foo(); + pthread_mutex_unlock(&sMyLock); + return NULL; +} + + + +static void myImageHandler(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + // thread 1 + if ( NSLookupSymbolInImage(mh, "_bar", 0) != NULL ) { + advanceToBarrier(1); + blockUntilBarrier(2); + pthread_mutex_lock(&sMyLock); + 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); + + // self-terminate this process if it locks up for two seconds + alarm(2); + + advanceToBarrier(0); + + pthread_t pthread2; + if ( pthread_create(&pthread2, NULL, thread2, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + // thread 1 + _dyld_register_func_for_add_image(&myImageHandler); + NSAddImage("bar.dylib", 0); +#endif + + PASS("deadlock"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dladdr-stripped/Makefile b/dyld/unit-tests/test-cases/dladdr-stripped/Makefile new file mode 100644 index 0000000..2e537bd --- /dev/null +++ b/dyld/unit-tests/test-cases/dladdr-stripped/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${STRIP} main + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/dladdr-stripped/main.c b/dyld/unit-tests/test-cases/dladdr-stripped/main.c new file mode 100644 index 0000000..4f8901a --- /dev/null +++ b/dyld/unit-tests/test-cases/dladdr-stripped/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2009 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// verify dladdr() returns NULL for a symbol name in a fully stripped +/// main executable (and not _mh_execute_header+nnn). +/// + +int main() +{ + Dl_info info; + if ( dladdr(&main, &info) == 0 ) { + FAIL("dladdr(&main, xx) failed"); + exit(0); + } + + if ( info.dli_sname != NULL ){ + FAIL("dladdr() returned: \"%s\" instead of NULL", info.dli_sname); + exit(0); + } + + PASS("dladdr-stripped"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dladdr/Makefile b/dyld/unit-tests/test-cases/dladdr/Makefile new file mode 100644 index 0000000..fb05db1 --- /dev/null +++ b/dyld/unit-tests/test-cases/dladdr/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -g -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.dSYM + diff --git a/dyld/unit-tests/test-cases/dladdr/main.c b/dyld/unit-tests/test-cases/dladdr/main.c new file mode 100644 index 0000000..c75c621 --- /dev/null +++ b/dyld/unit-tests/test-cases/dladdr/main.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int bar() +{ + return 2; +} + +static int foo() +{ + return 3; +} + +__attribute__((visibility("hidden"))) int hide() +{ + return 4; +} + +// checks global symbol +static void verifybar() +{ + Dl_info info; + if ( dladdr(&bar, &info) == 0 ) { + FAIL("dladdr(&bar, xx) failed"); + exit(0); + } + if ( strcmp(info.dli_sname, "bar") != 0 ) { + if ( strcmp(info.dli_sname, "_bar") == 0 ) { + XFAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); + } + else { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"bar\"", info.dli_sname); + exit(0); + } + } + if ( info.dli_saddr != &bar) { + 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 +static void verifyfoo() +{ + Dl_info info; + if ( dladdr(&foo, &info) == 0 ) { + FAIL("dladdr(&foo, xx) failed"); + exit(0); + } + if ( strcmp(info.dli_sname, "foo") != 0 ) { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"foo\"", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &foo) { + 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 +static void verifyhide() +{ + Dl_info info; + if ( dladdr(&hide, &info) == 0 ) { + FAIL("dladdr(&hide, xx) failed"); + exit(0); + } + if ( strcmp(info.dli_sname, "hide") != 0 ) { + FAIL("dladdr()->dli_sname is \"%s\" instead of \"hide\"", info.dli_sname); + exit(0); + } + if ( info.dli_saddr != &hide) { + FAIL("dladdr()->dli_saddr is not &hide"); + exit(0); + } +#if __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 +} + + +int main() +{ + verifybar(); + verifyhide(); + verifyfoo(); + + + PASS("dladdr"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-basic/Makefile b/dyld/unit-tests/test-cases/dlclose-basic/Makefile new file mode 100644 index 0000000..8aeb014 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-basic/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle -o test.bundle foo.c + +main : main.c test.bundle + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + + diff --git a/dyld/unit-tests/test-cases/dlclose-basic/foo.c b/dyld/unit-tests/test-cases/dlclose-basic/foo.c new file mode 100644 index 0000000..39ab8be --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-basic/foo.c @@ -0,0 +1,26 @@ +/* + * 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 1; +} diff --git a/dyld/unit-tests/test-cases/dlclose-basic/main.c b/dyld/unit-tests/test-cases/dlclose-basic/main.c new file mode 100644 index 0000000..ee7c122 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-basic/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // regular open + void* handle = dlopen("test.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"test.bundle\", RTLD_LAZY) failed"); + exit(1); + } + + // regular close + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose() failed"); + exit(1); + } + + // now try to close again (expect to fail) + result = dlclose(handle); + if ( result != -1 ) { + FAIL("dlclose() on released handle should have returned -1, but returned %d", result); + exit(1); + } + + // now try to close a bad handle value (expect to fail) + result = dlclose((void*)0x12345); + if ( result != -1 ) { + FAIL("dlclose() on bogus handle should have returned -1, but returned %d", result); + exit(1); + } + + PASS("dlclose-basic"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-bundle-unload/Makefile b/dyld/unit-tests/test-cases/dlclose-bundle-unload/Makefile new file mode 100644 index 0000000..c3d1b06 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-bundle-unload/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle + diff --git a/dyld/unit-tests/test-cases/dlclose-bundle-unload/foo.c b/dyld/unit-tests/test-cases/dlclose-bundle-unload/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-bundle-unload/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlclose-bundle-unload/main.c b/dyld/unit-tests/test-cases/dlclose-bundle-unload/main.c new file mode 100644 index 0000000..3e0798a --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-bundle-unload/main.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int main() +{ + // open same bundle three times + void* handle1 = dlopen("test.bundle", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-bundle-unload: dlopen(\"test.bundle\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("test.bundle", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-bundle-unload: dlopen(\"test.bundle\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("test.bundle", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-bundle-unload: dlopen(\"test.bundle\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "foo"); + if ( sym == NULL ) { + FAIL("dlclose-bundle-unload: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-bundle-unload: dlclose(handle4) == 0, but should have failed"); + exit(0); + } + + + + PASS("dlclose-bundle-unload"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile new file mode 100644 index 0000000..ac4fb27 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c new file mode 100644 index 0000000..d0fd23c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/bar.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int bar() +{ + return mydata; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c new file mode 100644 index 0000000..a3a1b44 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/baz.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int baz() +{ + return mydata; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c new file mode 100644 index 0000000..899bb44 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/foo.c @@ -0,0 +1,8 @@ +__attribute__((weak)) +int mydata; + + +int foo() +{ + return mydata; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c new file mode 100644 index 0000000..9c39733 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-dynamic-ref/main.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + Dl_info info; + + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load bar + void* handleBar = dlopen("libbar.dylib", RTLD_LAZY); + if ( handleBar == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load baz + void* handleBaz = dlopen("libbaz.dylib", RTLD_LAZY); + if ( handleBaz == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlopen(\"libbaz.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + + void* sym_foo = dlsym(handleFoo, "foo"); + if ( sym_foo == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleFoo, \"foo\") failed"); + exit(0); + } + + void* sym_bar = dlsym(handleBar, "bar"); + if ( sym_bar == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleBar, \"bar\") failed"); + exit(0); + } + + void* sym_baz = dlsym(handleBaz, "baz"); + if ( sym_baz == NULL ) { + FAIL("dlclose-dylib-dynamic-ref: dlsym(handleBaz, \"baz\") failed"); + exit(0); + } + + // since foo loaded first, bar and baz should have a dynamic reference to foo because all + // the weak mydata symbols were coaleseced to the one in foo. + + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libbar and libbaz + // have dynamic references to libfoo. + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + + if ( dlclose(handleBar) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + // sym_bar should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_bar, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_bar) != 0, but should have failed"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because of external libbaz + // has a dynamic references to libfoo. + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + + if ( dlclose(handleBaz) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_baz should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_baz, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_baz) != 0, but should have failed"); + exit(0); + } + + + // sym_foo should finally be inaccessible via dladdr() because all dynamic references to libfoo are gone + if ( dladdr(sym_foo, &info) != 0 ) { + FAIL("dlclose-dylib-dynamic-ref: dladdr(sym_foo) == 0, but should have succeeded"); + exit(0); + } + + + PASS("dlclose-dylib-dynamic-ref"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile new file mode 100644 index 0000000..63e2f78 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbase.dylib + diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c new file mode 100644 index 0000000..8312b89 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/bar.c @@ -0,0 +1,6 @@ + + +int bar() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/base.c b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/base.c new file mode 100644 index 0000000..e7e0d08 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/base.c @@ -0,0 +1 @@ +void base() { } diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c new file mode 100644 index 0000000..8184cd1 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/main.c b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/main.c new file mode 100644 index 0000000..5902d82 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-ref-count/main.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + Dl_info info; + + // load bar + void* handleBar = dlopen("libbar.dylib", RTLD_LAZY); + if ( handleBar == NULL ) { + FAIL("dlclose-dylib-ref-count: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-ref-count: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* sym_base = dlsym(handleBar, "base"); + if ( sym_base == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + void* sym_foo = dlsym(handleFoo, "foo"); + if ( sym_foo == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + void* sym_bar = dlsym(handleBar, "bar"); + if ( sym_bar == NULL ) { + FAIL("dlclose-dylib-ref-count: dlsym(handleBar, \"base\") failed"); + exit(0); + } + + if ( dlclose(handleBar) != 0 ) { + FAIL("dlclose-dylib-ref-count: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_base should still be accessible via dladdr() because of external reference from libfoo.dylib + if ( dladdr(sym_base, &info) == 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_base) == 0, but should have succeeded"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libfoo was dlopen'ed + if ( dladdr(sym_foo, &info) == 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_foo) == 0, but should have succeeded"); + exit(0); + } + + // sym_bar should not be accessible via dladdr() because libbar was dlclose'ed + if ( dladdr(sym_bar, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_bar) != 0, but should have failed"); + exit(0); + } + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-ref-count: dlclose(handleBar) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // sym_base should no longer be accessible via dladdr() because libfoo and libbar both closed + if ( dladdr(sym_base, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(base) != 0, but should have failed"); + exit(0); + } + + // sym_foo should still be accessible via dladdr() because libfoo was dlclose'ed + if ( dladdr(sym_foo, &info) != 0 ) { + FAIL("dlclose-dylib-ref-count: dladdr(sym_foo) != 0, but should have failed"); + exit(0); + } + + PASS("dlclose-dylib-ref-count"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-terminators/Makefile b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/Makefile new file mode 100644 index 0000000..e6d0b4c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# dylibs need to be unloaded in reverse order of creation + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib + ${CXX} ${CXXFLAGS} -dynamiclib bar.cpp libbaz.dylib -o libbar.dylib + ${CXX} ${CXXFLAGS} -dynamiclib foo.cpp libbaz.dylib libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp new file mode 100644 index 0000000..2815167 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/bar.cpp @@ -0,0 +1,17 @@ +extern "C" int bazData; + +class BazUser { +public: + BazUser() { } + ~BazUser() { bazData = 0; } +}; + + +BazUser b; + + +int bar() +{ + return bazData; +} + diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-terminators/baz.c b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/baz.c new file mode 100644 index 0000000..b5a740e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/baz.c @@ -0,0 +1,7 @@ +int bazData = 5; + + +int baz() +{ + return bazData; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp new file mode 100644 index 0000000..ead0e83 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/foo.cpp @@ -0,0 +1,23 @@ + +extern void bar(); + + + +class A { +public: + A() { bar(); } + ~A() { bar(); } +}; + + +// Create global object which will have its destructor run when +// this dylib is unloaded. The destructor will call into libbar, +// so libbar.dylib can't be unloaded before this dylib. +A a; + + + + +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-terminators/main.c b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/main.c new file mode 100644 index 0000000..34d2d0d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-terminators/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +int main() +{ + // load foo + void* handleFoo = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handleFoo == NULL ) { + FAIL("dlclose-dylib-order: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handleFoo) != 0 ) { + FAIL("dlclose-dylib-order: dlclose(handleFoo) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + + PASS("dlclose-dylib-order"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-unload/Makefile b/dyld/unit-tests/test-cases/dlclose-dylib-unload/Makefile new file mode 100644 index 0000000..bba1b65 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-unload/Makefile @@ -0,0 +1,46 @@ +## +# 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 libfoo.dylib + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbar.dylib -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-unload/bar.c b/dyld/unit-tests/test-cases/dlclose-dylib-unload/bar.c new file mode 100644 index 0000000..817b8cd --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-unload/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int bar() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-unload/foo.c b/dyld/unit-tests/test-cases/dlclose-dylib-unload/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-unload/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlclose-dylib-unload/main.c b/dyld/unit-tests/test-cases/dlclose-dylib-unload/main.c new file mode 100644 index 0000000..5e48c00 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-dylib-unload/main.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + + +void verifyfoo() +{ + // open same dylib three times + void* handle1 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "foo"); + if ( sym == NULL ) { + FAIL("dlclose-dylib-unload: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // symbol foo should no longer be accessible via dladdr() + Dl_info info; + if ( dladdr(sym, &info) != 0 ) { + FAIL("dlclose-dylib-unload: dladdr(foo_sym) != 0, but should have failed"); + //exit(0); + } + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-dylib-unload: dlclose(foo_handle4) == 0, but should have failed"); + //exit(0); + } + +} + + + +void verifybar() +{ + // open same dylib three times + void* handle1 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle2 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + void* handle3 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libbar.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + // get symbol + void* sym = dlsym(handle1, "bar"); + if ( sym == NULL ) { + FAIL("dlclose-dylib-unload: dlsym(handle1, \"bar\") failed"); + exit(0); + } + + // close same bundle three times + if ( dlclose(handle3) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle3) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle2) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle2) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + if ( dlclose(handle1) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle1) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + // symbol bar should still longer be accessible via dladdr() because of external reference to libbar.dylib + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("dlclose-dylib-unload: dladdr(bar_sym) == 0, but should have succeeded"); + exit(0); + } + + // extra close should fail + if ( dlclose(handle1) == 0 ) { + FAIL("dlclose-dylib-unload: dlclose(bar_handle4) == 0, but should have failed"); + exit(0); + } +} + + +// verify libbar.dylib can be loaded and unloaded +// verify libbar.dylib can be loaded, but cannot be unloaded (because main executable links against it) +int main() +{ + verifyfoo(); + verifybar(); + + PASS("dlclose-dylib-unload"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-order/Makefile b/dyld/unit-tests/test-cases/dlclose-order/Makefile new file mode 100644 index 0000000..6447f72 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-order/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2013 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# foo depends on bar depends on baz +# foo links with baz then bar +# verify that baz terminators run before bar terminators + + +all-check: all check + +check: + ./main libfoo.dylib + ./main libfoo2.dylib + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CXX} ${CXXFLAGS} -dynamiclib baz.cxx libbase.dylib -o libbaz.dylib + ${CXX} ${CXXFLAGS} -dynamiclib bar.cxx libbaz.dylib libbase.dylib -o libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib libbaz.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbaz.dylib libbar.dylib -o libfoo2.dylib + ${CC} ${CCFLAGS} main.c libbase.dylib -I${TESTROOT}/include -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo2.dylib libbaz.dylib libbar.dylib libbase.dylib + + diff --git a/dyld/unit-tests/test-cases/dlclose-order/bar.cxx b/dyld/unit-tests/test-cases/dlclose-order/bar.cxx new file mode 100644 index 0000000..94fddfe --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-order/bar.cxx @@ -0,0 +1,12 @@ +#include "base.h" + + +class Bar { +public: + Bar() { if ( bazInitied) barInitied = true; } + ~Bar() { if ( barInitied && !bazTeminated ) barTeminated = true; } +}; + + +Bar bar; + diff --git a/dyld/unit-tests/test-cases/dlclose-order/base.c b/dyld/unit-tests/test-cases/dlclose-order/base.c new file mode 100644 index 0000000..d128ed8 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-order/base.c @@ -0,0 +1,8 @@ +#include "base.h" + +bool barInitied = false; +bool barTeminated = false; + +bool bazInitied = false; +bool bazTeminated = false; + diff --git a/dyld/unit-tests/test-cases/dlclose-order/base.h b/dyld/unit-tests/test-cases/dlclose-order/base.h new file mode 100644 index 0000000..75b5960 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-order/base.h @@ -0,0 +1,9 @@ +#include + +extern bool barInitied; +extern bool barTeminated; + +extern bool bazInitied; +extern bool bazTeminated; + + diff --git a/dyld/unit-tests/test-cases/dlclose-order/baz.cxx b/dyld/unit-tests/test-cases/dlclose-order/baz.cxx new file mode 100644 index 0000000..415a9c6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-order/baz.cxx @@ -0,0 +1,12 @@ +#include "base.h" + +class Baz { +public: + Baz() { bazInitied = true; } + ~Baz() { bazTeminated = true; } + +}; + + +Baz baz; + diff --git a/dyld/unit-tests/test-cases/dlclose-order/foo.c b/dyld/unit-tests/test-cases/dlclose-order/foo.c new file mode 100644 index 0000000..39ab8be --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-order/foo.c @@ -0,0 +1,26 @@ +/* + * 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 1; +} diff --git a/dyld/unit-tests/test-cases/dlclose-order/main.c b/dyld/unit-tests/test-cases/dlclose-order/main.c new file mode 100644 index 0000000..c776426 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-order/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "base.h" + + +int main(int argc, const char* argv[]) +{ + // regular open + void* handle = dlopen(argv[1], RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlclose-order: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed"); + return EXIT_SUCCESS; + } + + // regular close + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose-order: dlclose() failed"); + return EXIT_SUCCESS; + } + + if ( !bazTeminated ) { + FAIL("dlclose-order: baz not terminated"); + return EXIT_SUCCESS; + } + + if ( !barTeminated ) { + FAIL("dlclose-order: bar not terminated"); + return EXIT_SUCCESS; + } + + PASS("dlclose-order"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile new file mode 100644 index 0000000..0c9b579 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# Leopard (9a499): dyld crash with recursive calls to dlclose() + + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbar.dylib -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c new file mode 100644 index 0000000..6cd208d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int bar() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c new file mode 100644 index 0000000..e12dc61 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/foo.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + + +// Leopard (9a499): dyld crash with recursive calls to dlclose() + +static void* handle = NULL; + +static void __attribute__((constructor)) myinit() +{ + handle = dlopen("libbar.dylib", RTLD_LAZY); +} + +static void __attribute__((destructor)) myterm() +{ + // myterm() is called within dlclose. + // now call dlclose() on another (chainded dylib) to test dlclose is re-entrant + if ( handle != NULL ) + dlclose(handle); +} + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/main.c b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/main.c new file mode 100644 index 0000000..90ea779 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-terminator-dlclose/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlclose-dylib-unload: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + + if ( dlclose(handle) != 0 ) { + FAIL("dlclose-dylib-unload: dlclose(handle) != 0, dlerrr()=%s", dlerror()); + exit(0); + } + + PASS("dlclose-terminator-dlclose"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-unload-c++/Makefile b/dyld/unit-tests/test-cases/dlclose-unload-c++/Makefile new file mode 100644 index 0000000..35530b6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-unload-c++/Makefile @@ -0,0 +1,46 @@ +## +# 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 libfoo.dylib libbar.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/dlclose-unload-c++/bar.c b/dyld/unit-tests/test-cases/dlclose-unload-c++/bar.c new file mode 100644 index 0000000..c69ec99 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-unload-c++/bar.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +static void local() +{ +} + +extern void* common() __attribute__((weak)); + +void* common() +{ + return &local; +} + +void* bar() +{ + return common(); +} diff --git a/dyld/unit-tests/test-cases/dlclose-unload-c++/foo.c b/dyld/unit-tests/test-cases/dlclose-unload-c++/foo.c new file mode 100644 index 0000000..b6bd1e4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-unload-c++/foo.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +static void local() +{ +} + +extern void* common() __attribute__((weak)); + +void* common() +{ + return &local; +} + +void* foo() +{ + return common(); +} diff --git a/dyld/unit-tests/test-cases/dlclose-unload-c++/main.c b/dyld/unit-tests/test-cases/dlclose-unload-c++/main.c new file mode 100644 index 0000000..8300a41 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-unload-c++/main.c @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +/// +/// This tests that if a C++ symbol (any weak symbol) is bound to an image +/// that is dynamically unloaed, the image is not unloaded until all its clients are +/// + +typedef void* (*proc)(void); + +bool inImage(void* x) +{ + Dl_info info; + return ( dladdr(x, &info) != 0 ); +} + + +int main() +{ + void* handle1 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle1 == NULL ) { + FAIL("dlclose-unload-c++: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + proc fooProc = (proc)dlsym(handle1, "foo"); + if ( fooProc == NULL ) { + FAIL("dlclose-unload-c++: dlsym(handle1, \"foo\") failed"); + exit(0); + } + + void* handle2 = dlopen("libbar.dylib", RTLD_LAZY); + if ( handle2 == NULL ) { + FAIL("dlclose-unload-c++: dlopen(\"libfoo.dylib\", RTLD_LAZY) failed with dlerror()=%s", dlerror()); + exit(0); + } + + proc barProc = (proc)dlsym(handle2, "bar"); + if ( barProc == NULL ) { + FAIL("dlclose-unload-c++: dlsym(handle2, \"bar\") failed"); + exit(0); + } + + // verify that uniquing is happening + void* fooResult = (*fooProc)(); + void* barResult = (*barProc)(); + if ( fooResult != barResult ) { + FAIL("dlclose-unload-c++: foo() and bar() returned different values"); + exit(0); + } + + // close libfoo, even though libbar is using libfoo + dlclose(handle1); + + // error if libfoo was unloaded + if ( !inImage(fooProc) ) { + FAIL("dlclose-unload-c++: libfoo should not have been unloaded"); + exit(0); + } + + // close libbar which should release libfoo + dlclose(handle2); + + // error if libfoo was not unloaded + if ( inImage(fooProc) ) { + FAIL("dlclose-unload-c++: libfoo should have been unloaded"); + exit(0); + } + + PASS("dlclose-unload-c++"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlclose-unmap/Makefile b/dyld/unit-tests/test-cases/dlclose-unmap/Makefile new file mode 100644 index 0000000..5f24149 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-unmap/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/dyld/unit-tests/test-cases/dlclose-unmap/foo.c b/dyld/unit-tests/test-cases/dlclose-unmap/foo.c new file mode 100644 index 0000000..8826cba --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-unmap/foo.c @@ -0,0 +1,2 @@ + +void foo() {} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/dlclose-unmap/main.c b/dyld/unit-tests/test-cases/dlclose-unmap/main.c new file mode 100644 index 0000000..a2c3cbf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlclose-unmap/main.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that dlclose() actually unmmaps the image +/// + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\" RTLD_LAZY) failed but it should have worked: %s", path, msg); + exit(0); + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", msg); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + + // now try to create a page where foo() was + vm_address_t addr = ((uintptr_t)sym) & (-4096); + kern_return_t r = vm_allocate(mach_task_self(), &addr, 4096, VM_FLAGS_FIXED); + if ( r != KERN_SUCCESS ) { + FAIL("dlclose-unmap: could not allocate page where SO was previously mapped", result); + exit(0); + } +} + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlclose-unmap"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlerror-clear/Makefile b/dyld/unit-tests/test-cases/dlerror-clear/Makefile new file mode 100644 index 0000000..4871023 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlerror-clear/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/dlerror-clear/main.c b/dyld/unit-tests/test-cases/dlerror-clear/main.c new file mode 100644 index 0000000..f70cc86 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlerror-clear/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that the dlerror message is cleared when dlerror is called +/// + +int main() +{ + // try to non-existent library + void* handle1 = dlopen("frobulite", RTLD_LAZY); + if ( handle1 != NULL ) { + FAIL("dlerror-clear: dlopen(\"frobulite\", RTLD_LAZY) succeeded but should have failed"); + exit(0); + } + + // verify there is an error message + const char* msg = dlerror(); + if ( msg == NULL ) { + FAIL("dlerror-clear: dlerror() returned NULL but should have returned an error message"); + exit(0); + } + + // verify error message was cleared + const char* msg2 = dlerror(); + if ( msg2 != NULL ) { + FAIL("dlerror-clear: dlerror() returned message but should have returned NULL"); + exit(0); + } + + PASS("dlerror-clear"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlerror/Makefile b/dyld/unit-tests/test-cases/dlerror/Makefile new file mode 100644 index 0000000..4871023 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlerror/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/dlerror/main.c b/dyld/unit-tests/test-cases/dlerror/main.c new file mode 100644 index 0000000..71ed8ab --- /dev/null +++ b/dyld/unit-tests/test-cases/dlerror/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that the dlerror message is kept per thread +/// + +static void* work(void* arg) +{ + const char* str = (char*)arg; + for(int i=0; i < 1000; ++i) { + //fprintf(stderr, "dlopen(%s)\n", str); + void* handle = dlopen(str, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen(%s) unexpectedly succeeded", str); + exit(0); + } + char* msg = dlerror(); + //fprintf(stderr, "dlopen(%s) => %s\n", str, msg); + if ( (msg == NULL) || (strstr(msg, str) == NULL) ) { + FAIL("dlerror() did not contain library name that could not be loaded", str); + exit(0); + } + + + } + return 0; +} + + + +int main() +{ + dlsym(RTLD_DEFAULT, "foobar"); + //fprintf(stderr, "%s\n", dlerror()); + + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, "/frazzle/bar") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, "/frazzle/foo") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, "/frazzle/dazzle") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); + + PASS("dlerror-thread-test"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..30d9778 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,31 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# Test that DYLD_FALLBACK_LIBRARY_PATH does not apply to dlopen() of a full path +# DYLD_FALLBACK_LIBRARY_PATH man page misleading +# + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH="${PWD}/hide" && ./main + +all: main hide/libfoo.dylib + +main : main.c hide/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/hide/libfoo.dylib" + + + +clean: + ${RM} -rf *~ main hide + diff --git a/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c b/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..33c0685 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/foo.c @@ -0,0 +1,6 @@ + + +int foo() +{ + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 0000000..d2ca85e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for getenv() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen("/junk/path/libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + FAIL("dlopen-DYLD_FALLBACK_LIBRARY_PATH unexpected found dylib"); + else + PASS("dlopen-DYLD_FALLBACK_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..9ea3667 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,51 @@ +## +# 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 + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main "${PWD}/libfoo.dylib" + export DYLD_LIBRARY_PATH="${PWD}/alt" && ./main "${PWD}/libfoo.dylib" + +all: main alt/libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +alt/libfoo.dylib : foo.c + mkdir -p alt + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt/libfoo.dylib" -DALT + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/libfoo.dylib" + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib alt/libfoo.dylib alt + diff --git a/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/foo.c b/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..0502e90 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ +#ifdef ALT + return 1; +#else + return 0; +#endif +} diff --git a/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..8067d7c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-DYLD_LIBRARY_PATH/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for getenv() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +typedef int (*fooproc)(); + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen(argv[1], RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", argv[1]); + exit(0); + } + + fooproc sym = (fooproc)dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int expectedResult = 0; + if ( getenv("DYLD_LIBRARY_PATH") != NULL ) + expectedResult = 1; + + int actualResult = (*sym)(); + + if ( actualResult != expectedResult ) + FAIL("dlopen-DYLD_LIBRARY_PATH using wrong dylib"); + else + PASS("dlopen-DYLD_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..dddef71 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/Makefile @@ -0,0 +1,73 @@ +## +# 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 + +PWD = $(shell pwd) + +# +# tests combinations of dlopen() and LD_LIBRARY_PATH +# +# if dlopen() path contains a slash, LD_LIBRARY_PATH is ignored +# +# 1) leaf name found in current directory (On linux this fails because . is usually not in default LD_LIBRARY_PATH path) +# 2) cwd relative path +# 3) full path +# 4) leaf name and LD_LIBRARY_PATH overrides cwd (On 10.3, this fails because cwd was always searched before LD_LIBRARY_PATH??) +# 5) leaf name and LD_LIBRARY_PATH set to alternate directory +# 6) fullpath and LD_LIBRARY_PATH set to alt +# + +all-check: all check + +check: + cd alt1 && ../main "libfoo.dylib" 1 "leafname found in cwd" + ./main "./alt1/libfoo.dylib" 1 "relative path" + ./main "${PWD}/alt2/libfoo.dylib" 2 "fullpath" + export LD_LIBRARY_PATH="${PWD}/alt1" && ./main "libfoo.dylib" 1 "leafname and LD_LIBRARY_PATH overrides cwd" + export LD_LIBRARY_PATH="${PWD}/alt1" && cd alt3 && ../main "libfoo.dylib" 1 "leafname and alt LD_LIBRARY_PATH" + export LD_LIBRARY_PATH="${PWD}/alt1" && ./main "${PWD}/alt2/libfoo.dylib" 2 "fullpath and LD_LIBRARY_PATH" + +all: main alt1/libfoo.dylib alt2/libfoo.dylib alt3 + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +alt1/libfoo.dylib : foo.c + mkdir -p alt1 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt1/libfoo.dylib" -DVALUE=1 + +alt2/libfoo.dylib : foo.c + mkdir -p alt2 + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/alt2/libfoo.dylib" -DVALUE=2 + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o "${PWD}/libfoo.dylib" -DVALUE=0 + +alt3 : + mkdir -p alt3 + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib alt1 alt2 alt3 + diff --git a/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/foo.c b/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..de65df5 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return VALUE; +} diff --git a/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..70f18bb --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-LD_LIBRARY_PATH/main.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for getenv() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +typedef int (*fooproc)(); + +// +// argv[1] is path to dlopen() +// argv[2] is exepect result from foo() +// argv[3] is message +// +int main(int argc, const char* argv[]) +{ + void* handle = dlopen(argv[1], RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-LD_LIBRARY_PATH %s, dlopen(\"%s\") failed", argv[3], argv[1]); + exit(0); + } + + fooproc sym = (fooproc)dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlopen-LD_LIBRARY_PATH %s, dlsym(handle, \"foo\") failed", argv[3]); + exit(0); + } + + int expectedResult = atoi(argv[2]); + + int actualResult = (*sym)(); + + if ( actualResult != expectedResult ) + FAIL("dlopen-LD_LIBRARY_PATH %s", argv[3]); + else + PASS("dlopen-LD_LIBRARY_PATH %s", argv[3]); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile b/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile new file mode 100644 index 0000000..6d7266a --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + + + diff --git a/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c b/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c new file mode 100644 index 0000000..fea884b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c b/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c new file mode 100644 index 0000000..b86c5e5 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-NULL-RTLD_FIRST/main.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// When dlopen(NULL, RTLD_FIRST) is called, +/// any dlsym() looks against that handle should only look in +/// the main executable, and not subsequent images. +/// + + +struct info +{ + const char* path; + void* handle; +}; +typedef struct info info; + +static info dlopen_or_fail(const char* path, int options) +{ + info result; + result.path = path; + result.handle = dlopen(path, options); + if ( result.handle == NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlopen(\"%s\") failed: %s", path, dlerror()); + exit(0); + } + //fprintf(stderr, "dlopen(%s, 0x%0X) => %p\n", path, options, result.handle); + return result; +} + +static void dlsym_should_fail(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym != NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlsym(handle-%s, \"%s\") should have failed", hp.path, symbol); + exit(0); + } +} + +static void dlsym_should_succeed(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym == NULL ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlsym(handle-%s, \"%s\") failed", hp.path, symbol); + exit(0); + } +} + + +int main() +{ + int result; + info mainFirst = dlopen_or_fail(NULL, RTLD_FIRST); + info mainDefault = dlopen_or_fail(NULL, 0); + + dlsym_should_succeed(mainFirst, "main_foo"); + dlsym_should_fail(mainFirst, "foo"); + + dlsym_should_succeed(mainDefault, "main_foo"); + dlsym_should_succeed(mainDefault, "foo"); + + result = dlclose(mainFirst.handle); + if ( result != 0 ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlclose(mainFirst.handle) failed: %s", dlerror()); + exit(0); + } + result = dlclose(mainDefault.handle); + if ( result != 0 ) { + FAIL("dlsym-NULL-RTLD_FIRST: dlclose(mainDefault.handle) failed: %s", dlerror()); + exit(0); + } + + PASS("dlsym-NULL-RTLD_FIRST"); + return EXIT_SUCCESS; +} + + +void main_foo() {} + + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile new file mode 100644 index 0000000..c1e199b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = `pwd` + +all-check: all check + +check: + ./main + +all: main + +main : main.c foo.bundle bar.bundle libfoo.dylib libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c libbase.dylib + ${CC} ${CCFLAGS} -bundle foo.c libbase.dylib -o foo.bundle + +bar.bundle : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o bar.bundle + +libfoo.dylib : foo.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + +libbar.dylib : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c libbase.dylib -o libbar.dylib -sub_library libbase + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib -install_name ${PWD}/libbase.dylib + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle bar.bundle libfoo.dylib libbar.dylib libbase.dylib + + + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c new file mode 100644 index 0000000..7ef59fb --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int bar() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c new file mode 100644 index 0000000..87bb46c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/base.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int base() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c new file mode 100644 index 0000000..fea884b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c new file mode 100644 index 0000000..95d3ce4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_FIRST/main.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// When dlopen() is called with the RTLD_FIRST option, +/// any dlsym() looks against that handle should only look in +/// that handle, and not subsequent images. +/// + + +struct info +{ + const char* path; + void* handle; +}; +typedef struct info info; + +static info dlopen_or_fail(const char* path, int options) +{ + info result; + result.path = path; + result.handle = dlopen(path, options); + if ( result.handle == NULL ) { + FAIL("dlopen-RTLD_FIRST: dlopen(\"%s\") failed: %s", path, dlerror()); + exit(0); + } + //fprintf(stderr, "dlopen(%s, 0x%0X) => %p\n", path, options, result.handle); + return result; +} + +static void dlsym_should_fail(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym != NULL ) { + FAIL("dlopen-RTLD_FIRST: dlsym(handle-%s, \"%s\") should have failed", hp.path, symbol); + exit(0); + } +} + +static void dlsym_should_succeed(info hp, const char* symbol) +{ + void* sym = dlsym(hp.handle, symbol); + if ( sym == NULL ) { + FAIL("dlopen-RTLD_FIRST: dlsym(handle-%s, \"%s\") failed", hp.path, symbol); + exit(0); + } +} + + +int main() +{ + info libFooFirst = dlopen_or_fail("libfoo.dylib", RTLD_FIRST); + info libFoo = dlopen_or_fail("libfoo.dylib", 0); + + info libBarFirst = dlopen_or_fail("libbar.dylib", RTLD_FIRST); + info libBar = dlopen_or_fail("libbar.dylib", 0); + + dlsym_should_succeed(libFooFirst, "foo"); + dlsym_should_fail(libFooFirst, "base"); + dlsym_should_fail(libFooFirst, "bar"); + + dlsym_should_succeed(libFoo, "foo"); + dlsym_should_succeed(libFoo, "base"); + + dlsym_should_succeed(libBarFirst, "bar"); + dlsym_should_succeed(libBarFirst, "base"); // libbar re-exports libbase + dlsym_should_fail(libBarFirst, "foo"); + + dlsym_should_succeed(libBar, "bar"); + dlsym_should_succeed(libBar, "base"); + + + + info bundleFooFirst = dlopen_or_fail("foo.bundle", RTLD_FIRST); + info bundleFoo = dlopen_or_fail("foo.bundle", 0); + + info bundleBarFirst = dlopen_or_fail("bar.bundle", RTLD_FIRST); + info bundleBar = dlopen_or_fail("bar.bundle", 0); + + dlsym_should_succeed(bundleFooFirst, "foo"); + dlsym_should_fail(bundleFooFirst, "base"); + dlsym_should_fail(bundleFooFirst, "bar"); + + dlsym_should_succeed(bundleFoo, "foo"); + dlsym_should_succeed(bundleFoo, "base"); + + dlsym_should_succeed(bundleBarFirst, "bar"); + dlsym_should_fail(bundleBarFirst, "base"); + dlsym_should_fail(bundleBarFirst, "foo"); + + dlsym_should_succeed(bundleBar, "bar"); + dlsym_should_succeed(bundleBar, "base"); + + + PASS("dlsym-RTLD_FIRST bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile new file mode 100644 index 0000000..e6a5e5a --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/Makefile @@ -0,0 +1,49 @@ +## +# 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 foo.bundle bar.bundle + ./main foo.dylib bar.bundle + +all: main foo.bundle foo.dylib bar.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +bar.bundle : bar.c + ${CC} ${CCFLAGS} -flat_namespace -bundle bar.c -o bar.bundle -undefined suppress + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib bar.bundle + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/bar.c b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/bar.c new file mode 100644 index 0000000..e8e0806 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/bar.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int foo; + +int bar() +{ + return foo; +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/foo.c new file mode 100644 index 0000000..8689597 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/foo.c @@ -0,0 +1,25 @@ +/* + * 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 = 0; diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/main.c new file mode 100644 index 0000000..a25854b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_GLOBAL/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + + +int main(int argc, const char* argv[]) +{ + // open first object which defines foo + void* handle = dlopen(argv[1], RTLD_GLOBAL); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", argv[1]); + exit(1); + } + + // open second object which uses foo + void* handle2 = dlopen(argv[2], RTLD_NOW); + if ( handle2 == NULL ) { + FAIL("dlopen(\"%s\") failed", argv[2]); + exit(1); + } + + PASS("dlopen-RTLD_GLOBAL"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile new file mode 100644 index 0000000..823d5de --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/Makefile @@ -0,0 +1,46 @@ +## +# 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 + +main : main.c foo.dylib bar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.dylib + + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +bar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o bar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main foo.dylib bar.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/bar.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/bar.c new file mode 100644 index 0000000..73b29f2 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int bar() +{ + return 2; +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/foo.c new file mode 100644 index 0000000..d040e48 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 1; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/main.c new file mode 100644 index 0000000..b2862c5 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-ignore/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + // main alreadly links with foo.dylib + // now dynamically link with bar.dylib + void* handle = dlopen("./bar.dylib", RTLD_GLOBAL); + if ( handle == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\", RTLD_GLOBAL) failed: %s", argv[1], msg); + return EXIT_SUCCESS; + } + + // verify we can find _foo + void* fooSym = dlsym(RTLD_DEFAULT, "foo"); + if ( fooSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"foo\") failed: %s", msg); + return EXIT_SUCCESS; + } + + // verify we can find _bar + void* barSym = dlsym(RTLD_DEFAULT, "bar"); + if ( barSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"bar\") failed: %s", msg); + return EXIT_SUCCESS; + } + + // open foo privately (since it was already opened global, RTLD_LOCAL should have no effect + void* handleFoo = dlopen("./foo.dylib", RTLD_LOCAL); + if ( handleFoo == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", "./foo.dylib", msg); + return EXIT_SUCCESS; + } + + // open foo privately (since it was already opened global, RTLD_LOCAL should have no effect + void* handleBar = dlopen("./bar.dylib", RTLD_LOCAL); + if ( handleBar == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", "./bar.dylib", msg); + return EXIT_SUCCESS; + } + + // verify we can still find _foo + fooSym = dlsym(RTLD_DEFAULT, "foo"); + if ( fooSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"foo\") failed: %s", msg); + return EXIT_SUCCESS; + } + + // verify we can still find _bar + barSym = dlsym(RTLD_DEFAULT, "bar"); + if ( barSym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(RTLD_DEFAULT, \"bar\") failed: %s", msg); + return EXIT_SUCCESS; + } + + PASS("dlopen-RTLD_LOCAL-ignore"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile new file mode 100644 index 0000000..48bf4c4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/Makefile @@ -0,0 +1,31 @@ + + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifes that RTLD_LOCAL suppresses weak symbol coalescing +# + + + +all-check: all check + +check: + ./main + +all: main libfoo.dylib libbar.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c new file mode 100644 index 0000000..b4ae377 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/bar.c @@ -0,0 +1,5 @@ +int __attribute__((weak)) A[] = { 1, 2, 3, 4 }; + + +int* getA() { return A; } + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c new file mode 100644 index 0000000..c8ea49b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/foo.c @@ -0,0 +1,5 @@ + + +int __attribute__((weak)) A[] = { 5, 6, 7, 8 }; + +int* getA() { return A; } diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c new file mode 100644 index 0000000..0340138 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL-weak/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// this strong A should not override weak +// As in loaded code because of RTLD_LOCAL +int A[] = { 10, 11, 12, 13 }; + +typedef int* (*getproc)(void); + +int main(int argc, const char* argv[]) +{ + // open first object + void* fooHandle = dlopen("libfoo.dylib", RTLD_LOCAL); + if ( fooHandle == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlopen(\"libfoo.dylib\", RTLD_LOCAL) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // open second object + void* barHandle = dlopen("libbar.dylib", RTLD_LOCAL); + if ( barHandle == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlopen(\"libbar.dylib\", RTLD_LOCAL) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get functions + getproc fooproc = (getproc)dlsym(fooHandle, "getA"); + if ( fooproc == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlsym(getA) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + getproc barproc = (getproc)dlsym(barHandle, "getA"); + if ( barproc == NULL ) { + FAIL("dlopen-RTLD_LOCAL-weak: dlsym(getA) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get values + int* fooA = (*fooproc)(); + int* barA = (*barproc)(); + + if ( fooA == A ) + FAIL("dlopen-RTLD_LOCAL-weak: fooA == A"); + else if ( barA == A ) + FAIL("dlopen-RTLD_LOCAL-weak: barA == A"); + else if ( fooA == barA ) + FAIL("dlopen-RTLD_LOCAL-weak: fooA == barA"); + else + PASS("dlopen-RTLD_LOCAL-weak"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile new file mode 100644 index 0000000..e6a5e5a --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/Makefile @@ -0,0 +1,49 @@ +## +# 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 foo.bundle bar.bundle + ./main foo.dylib bar.bundle + +all: main foo.bundle foo.dylib bar.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +bar.bundle : bar.c + ${CC} ${CCFLAGS} -flat_namespace -bundle bar.c -o bar.bundle -undefined suppress + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib bar.bundle + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/bar.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/bar.c new file mode 100644 index 0000000..e8e0806 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/bar.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int foo; + +int bar() +{ + return foo; +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/foo.c new file mode 100644 index 0000000..8689597 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/foo.c @@ -0,0 +1,25 @@ +/* + * 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 = 0; diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/main.c new file mode 100644 index 0000000..b8a30bc --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_LOCAL/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + // open first object which defines foo + void* handle = dlopen(argv[1], RTLD_LOCAL); + if ( handle == NULL ) { + const char* msg = dlerror(); + // Panther dlopen() fails on RTLD_LOCAL of a dylib + if ( strstr(msg, "RTLD_LOCAL") != NULL ) + XFAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", argv[1], msg); + else + FAIL("dlopen(\"%s\", RTLD_LOCAL) failed: %s", argv[1], msg); + return EXIT_SUCCESS; + } + + // open second object which uses foo + void* handle2 = dlopen(argv[2], RTLD_NOW); + if ( handle2 != NULL ) { + FAIL("dlopen(\"%s\") succeeded but foo should have been not found", argv[2]); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "foo") == NULL ) { + FAIL("dlopen(\"%s\") correctly failed, but foo was not in error mesage: %s", argv[2], msg); + return EXIT_SUCCESS; + } + + + PASS("dlopen-RTLD_LOCAL"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile new file mode 100644 index 0000000..800a40c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/foo.c new file mode 100644 index 0000000..5cc9791 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/foo.c @@ -0,0 +1,25 @@ +/* + * 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 = 10; \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/main.c new file mode 100644 index 0000000..296479e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NODELETE/main.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NODELETE prevents an image from being unloaded +/// + +static int trySO(const char* path) +{ // main links against libfoo.dylib so it should already be loaded + void* handle = dlopen(path, RTLD_NODELETE); + if ( handle == NULL ) { + const char* msg = dlerror(); + FAIL("dlopen(\"%s\" RTLD_NODELETE) failed but it should have worked: %s", path, msg); + exit(0); + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", msg); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle[%s]) returned %d", path, result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + + // now try to access foo. If .so was unmapped, this will bus error + return *((int*)sym); +} + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlopen-RTLD_NODELETE"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile new file mode 100644 index 0000000..b6a476e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### +### main links with hide/libfoo.dylib +### main is run with DYLD_FALLBACK_LIBRARY_PATH pointing to hide +### main calls dlopen("/foo/bar/libfoo.dylib", RTLD_NOLOAD) +### dlopen should *not* find the already loaded libfoo.dylib because +### it is only supposed to search existing loaded images for one +### with a matching path. +### + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH=hide && ./main /foo/bar/libfoo.dylib + +all: main + +main : main.c hide/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c hide/libfoo.dylib -o main + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -o hide/libfoo.dylib + + + + +clean: + ${RM} ${RMFLAGS} *~ main hide + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c new file mode 100644 index 0000000..f629da7 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-fallback/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD does not find an image via a fallback path +/// + + +int main(int argc, const char* argv[]) +{ + const char* path = argv[1]; + void* handle = dlopen(path, RTLD_NOLOAD); + if ( handle != NULL ) + FAIL("dlopen-RTLD_NOLOAD-fallback, dlopen(% RTLD_NOLOAD) should have failed"); + else + PASS("dlopen-RTLD_NOLOAD-fallback"); + + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile new file mode 100644 index 0000000..75479e4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + + +libfoo.dylib : foo.c libbar.dylib libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib libbaz.dylib -o libfoo.dylib + +libbar.dylib : bar.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -I${TESTROOT}/include libbase.dylib -o libbar.dylib + +libbaz.dylib : baz.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib baz.c libbase.dylib -o libbaz.dylib + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib libbase.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c new file mode 100644 index 0000000..62fdfec --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/bar.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern bool bazInitialized(); + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + if ( bazInitialized() ) { + FAIL("dlopen-RTLD_NOLOAD-in-initializer baz initialized too early"); + exit(0); + } + + // calling dlopen with RTLD_NOLOAD. + // this should not cause initialzers to run. + // in particular, baz should be be initialized + dlopen("libbaz.dylib", RTLD_NOLOAD); + + if ( bazInitialized() ) { + FAIL("dlopen-RTLD_NOLOAD-in-initializer baz initialized by dlopen"); + exit(0); + } +} + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c new file mode 100644 index 0000000..3bcd164 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/base.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // exit(), EXIT_SUCCESS +#include + +static bool inited = false; + +void setBazInitialized() { inited = true; } + +bool bazInitialized() { return inited; } + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c new file mode 100644 index 0000000..6a80314 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/baz.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +extern void setBazInitialized(); + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + setBazInitialized(); +} + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c new file mode 100644 index 0000000..1e61ab3 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + + +bool foo() +{ + return true; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c new file mode 100644 index 0000000..9b67e37 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-in-initializer/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef bool (*fooProc)(void); + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"libfoo.dylib\") failed with: %s", dlerror()); + exit(0); + } + + dlclose(handle); + + PASS("dlopen-RTLD_NOLOAD-in-initializer"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile new file mode 100644 index 0000000..eb6db50 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/Makefile @@ -0,0 +1,62 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +### +### Test that RTLD_NOLOAD finds existing image +### even when symlinks are used to obscure it +### + +all-check: all check + +check: + ./main libfoosym.dylib + ./main2 libbar.dylib + + +all: main main2 libfoosym.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoosym.dylib : libfoo.dylib + ln -sf libfoo.dylib libfoosym.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o ${PWD}/libfoo.dylib + +main2 : main.c libbarsym.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbarsym.dylib -o main2 + +libbarsym.dylib : libbar.dylib + ln -sf libbar.dylib libbarsym.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -install_name ${PWD}/libbarsym.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main2 libfoo.dylib libfoosym.dylib libbar.dylib libbarsym.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c new file mode 100644 index 0000000..7fe6403 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/bar.c @@ -0,0 +1 @@ +int bar() { return 0; } diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c new file mode 100644 index 0000000..c58fc07 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/foo.c @@ -0,0 +1,2 @@ +int foo() { return 0; } + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c new file mode 100644 index 0000000..4ce752c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD-symlink/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD finds existing images +/// + + +int main(int argc, const char* argv[]) +{ + // main links against libfoo.dylib, so it is already loaded + // libbar.dylib is a symlink to libfoo.dylib, so it should succeed + void* handle = dlopen(argv[1], RTLD_NOLOAD); + if ( handle != NULL ) + PASS("dlopen-RTLD_NOLOAD-symlink"); + else + FAIL("dlopen-RTLD_NOLOAD-symlink"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile new file mode 100644 index 0000000..f634e7b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/main.c new file mode 100644 index 0000000..7383a8c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOLOAD/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that RTLD_NOLOAD binds finds existing images +/// + + +int main() +{ + // main links against libfoo.dylib so it should already be loaded + void* handle = dlopen("libfoo.dylib", RTLD_NOLOAD); + if ( handle == NULL ) { + const char* msg = dlerror(); + // Panther dlcompat does not check existing loaded images (only those opened with dlopen) + if ( strstr(msg, "RTLD_NOLOAD") != NULL ) + XFAIL("dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s", msg); + else + FAIL("dlopen(libfoo.dylib, RTLD_NOLOAD) failed but it should have worked: %s", msg); + return 0; + } + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + const char* msg = dlerror(); + FAIL("dlsym(handle, \"foo\") failed but it should have worked: %s", msg); + return 0; + } + + // libfobbulate.dylib does not exist, so load should return NULL + void* handle2 = dlopen("libfobbulate.dylib", RTLD_NOLOAD); + if ( handle2 != NULL ) { + FAIL("dlopen(libfobbulate.dylib, RTLD_NOLOAD) succeeded but it should have failed"); + return 0; + } + + + + PASS("dlopen-RTLD_NOLOAD"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile new file mode 100644 index 0000000..3b639ea --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/Makefile @@ -0,0 +1,49 @@ +## +# 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 foo.dylib foo_foo2.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c foo_foo2.dylib + ${CC} ${CCFLAGS} -bundle bundle.c foo_foo2.dylib -o test.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +foo_foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -DFOO2 -o foo_foo2.dylib -install_name foo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle foo.dylib foo_foo2.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/bundle.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/bundle.c new file mode 100644 index 0000000..5772ff4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/bundle.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +extern int foo(); +extern int foo2(); + +void doit() +{ + foo(); + foo2(); +} diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/foo.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/foo.c new file mode 100644 index 0000000..e937770 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/foo.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} + +#if FOO2 +int foo2() +{ + return 10; +} +#endif diff --git a/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/main.c b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/main.c new file mode 100644 index 0000000..2ebb4ad --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-RTLD_NOW/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that TLD_NOW binds all lazy symbols. +/// We do this by making sure a lazy symbol does not exist +/// and dlopen() errors out with a message string +/// that contains the missing symbol name. +/// + + +int main() +{ + void* handle = dlopen("test.bundle", RTLD_NOW); + if ( handle == NULL ) { + const char* msg = dlerror(); + if ( strstr(msg, "foo2") != NULL ) { + PASS("dlopen-RTLD_NOW"); + exit(0); + } + FAIL("dlopen(test.bundle, RTLD_NOW) failed but error message did not contain foo2: %s", msg); + exit(0); + } + FAIL("dlopen(test.bundle, RTLD_NOW) succeed but should have failed because foo2 does not exist"); + exit(0); +} diff --git a/dyld/unit-tests/test-cases/dlopen-basic/Makefile b/dyld/unit-tests/test-cases/dlopen-basic/Makefile new file mode 100644 index 0000000..800a40c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-basic/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-basic/foo.c b/dyld/unit-tests/test-cases/dlopen-basic/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-basic/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-basic/main.c b/dyld/unit-tests/test-cases/dlopen-basic/main.c new file mode 100644 index 0000000..f877537 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-basic/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlopen-basic bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile b/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile new file mode 100644 index 0000000..aabd547 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify uncodesigned dylibs gracefully fail to load +# + + +all-check: all check + +check: + ./main + ./main-enforce + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -DENFORCE -o main-enforce + codesign -s - main-enforce + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ main main-enforce libfoo.dylib diff --git a/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c b/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/main.c b/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/main.c new file mode 100644 index 0000000..37dba84 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-codesign-dynamic/main.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test.h" + + +int main() +{ +#if ENFORCE + uint32_t flags = CS_ENFORCEMENT | CS_KILL; + if ( csops(0, CS_OPS_SET_STATUS, &flags, sizeof(flags)) != 0 ) { + FAIL("dlopen-codesign-dynamic: csops() failed"); + return EXIT_SUCCESS; + } + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib should have failed"); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "signature") == NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib failed, but message was wrong: %s", msg); + return EXIT_SUCCESS; + } + +#else + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-codesign-dynamic: load of libfoo.dylib failed"); + return EXIT_SUCCESS; + } + +#endif + + PASS("dlopen-codesign-dynamic"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-codesign/Makefile b/dyld/unit-tests/test-cases/dlopen-codesign/Makefile new file mode 100644 index 0000000..9060b25 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-codesign/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify uncodesigned dylibs gracefully fail to load +# + + +all-check: all check + +check: + ./main + +all: + ${CC} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + codesign -s - -o enforcement,hard main + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib diff --git a/dyld/unit-tests/test-cases/dlopen-codesign/foo.c b/dyld/unit-tests/test-cases/dlopen-codesign/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-codesign/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/dlopen-codesign/main.c b/dyld/unit-tests/test-cases/dlopen-codesign/main.c new file mode 100644 index 0000000..53fce5f --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-codesign/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-codesign: load of libfoo.dylib should have failed"); + return EXIT_SUCCESS; + } + const char* msg = dlerror(); + if ( strstr(msg, "signature") == NULL ) { + FAIL("dlopen-codesign: load of libfoo.dylib failed, but message was wrong: %s", msg); + return EXIT_SUCCESS; + } + + PASS("dlopen-codesign"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-dyld-locking/Makefile b/dyld/unit-tests/test-cases/dlopen-dyld-locking/Makefile new file mode 100644 index 0000000..dcf5be4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-dyld-locking/Makefile @@ -0,0 +1,49 @@ +## +# 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: + ./main + +all: main libbar.dylib + +main : main.c libfoo.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbase.dylib + +libfoo.dylib : foo.c libbase.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c libbase.dylib -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib -I${TESTROOT}/include + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbase.dylib + + + diff --git a/dyld/unit-tests/test-cases/dlopen-dyld-locking/bar.c b/dyld/unit-tests/test-cases/dlopen-dyld-locking/bar.c new file mode 100644 index 0000000..0531aa2 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-dyld-locking/bar.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +void __attribute__((constructor)) myInit() +{ + // printf("in bar\n"); +} + + + diff --git a/dyld/unit-tests/test-cases/dlopen-dyld-locking/base.c b/dyld/unit-tests/test-cases/dlopen-dyld-locking/base.c new file mode 100644 index 0000000..6947d31 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-dyld-locking/base.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static pthread_mutex_t sBarrierMutex; +static pthread_cond_t sBarrierFree; + +static volatile int sValue = 0; + +static void __attribute__((constructor)) myinit() +{ + pthread_mutex_init(&sBarrierMutex, NULL); + pthread_cond_init(&sBarrierFree, NULL); +} + +void waitForState(int value) +{ + //fprintf(stderr, "waitForState(%d), currently %d\n", value, sValue); + pthread_mutex_lock(&sBarrierMutex); + while ( sValue < value ) { + struct timeval tvNow; + struct timespec tsTimeout; + gettimeofday(&tvNow, NULL); + TIMEVAL_TO_TIMESPEC(&tvNow, &tsTimeout); + tsTimeout.tv_sec += 2; // fail if block for 2 seconds + if ( pthread_cond_timedwait(&sBarrierFree, &sBarrierMutex, &tsTimeout) == ETIMEDOUT ) { + FAIL("dlopen-dyld-locking: lock timed out"); + exit(0); + } + } + pthread_mutex_unlock(&sBarrierMutex); +} + + +void setState(int value) +{ + pthread_mutex_lock(&sBarrierMutex); + //fprintf(stderr, "setState(%d)\n", value); + sValue = value; + pthread_cond_broadcast(&sBarrierFree); + pthread_mutex_unlock(&sBarrierMutex); +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-dyld-locking/base.h b/dyld/unit-tests/test-cases/dlopen-dyld-locking/base.h new file mode 100644 index 0000000..f1aef79 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-dyld-locking/base.h @@ -0,0 +1,3 @@ + +extern void waitForState(int); +extern void setState(int); diff --git a/dyld/unit-tests/test-cases/dlopen-dyld-locking/foo.c b/dyld/unit-tests/test-cases/dlopen-dyld-locking/foo.c new file mode 100644 index 0000000..5753fa5 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-dyld-locking/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include "base.h" + +void __attribute__((constructor)) myInit() +{ + setState(1); + waitForState(2); +} + + + diff --git a/dyld/unit-tests/test-cases/dlopen-dyld-locking/main.c b/dyld/unit-tests/test-cases/dlopen-dyld-locking/main.c new file mode 100644 index 0000000..3c68a9d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-dyld-locking/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "base.h" + + +/// +/// This test verifies that dlopen() releases the dyld lock +/// while initializers are run. +/// +/// + + +static void* work(void* arg) +{ + waitForState(1); + dlopen("libbar.dylib", RTLD_LAZY); // dladdr() will hang until dyld lock is released + setState(2); // the initializer in libfoo.dylib is block waiting for state 2 + return NULL; +} + + +int main() +{ + pthread_t otherThread; + if ( pthread_create(&otherThread, NULL, work, NULL) != 0 ) { + FAIL("dlopen-dyld-locking: pthread_create failed"); + exit(0); + } + + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-dyld-locking: dlopen(\"libfoo.dylib\") failed: %s", dlerror()); + exit(0); + } + + PASS("dlopen-dyld-locking"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-error/Makefile b/dyld/unit-tests/test-cases/dlopen-error/Makefile new file mode 100644 index 0000000..6515da4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-error/Makefile @@ -0,0 +1,44 @@ +## +# 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 + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +all-check: all check + +check: + 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 + +clean: + ${RM} ${RMFLAGS} *~ main libnoread.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-error/foo.c b/dyld/unit-tests/test-cases/dlopen-error/foo.c new file mode 100644 index 0000000..0fd6c1e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-error/foo.c @@ -0,0 +1,2 @@ + +void foo() {} diff --git a/dyld/unit-tests/test-cases/dlopen-error/main.c b/dyld/unit-tests/test-cases/dlopen-error/main.c new file mode 100644 index 0000000..481db0c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-error/main.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strstr() +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // test error message of a dylib that does not exist + void* handle = dlopen("libdoesnotexist.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-error: dlopen(libdoesnotexist.dylib, RTLD_LAZY) should have failed"); + return 0; + } + if ( strstr(dlerror(), "image not found") == NULL ) { + FAIL("dlopen-error: expected 'image not found' in dlerror() string"); + return 0; + } + + // test error message of a dylib that is not readabble + handle = dlopen("libnoread.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-error: dlopen(libnoread.dylib, RTLD_LAZY) should have failed"); + return 0; + } + if ( strstr(dlerror(), "failed with errno=13") == NULL ) { + FAIL("dlopen-error: expected 'failed with errno=13' in dlerror() string"); + return 0; + } + + + PASS("dlopen-error"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-executable/Makefile b/dyld/unit-tests/test-cases/dlopen-executable/Makefile new file mode 100644 index 0000000..2fc2da1 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-executable/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.exe foo.pie + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.exe : foo.c + ${CC} ${CCFLAGS} foo.c -o foo.exe -Wl,-no_pie + +foo.pie : foo.c + ${CC} ${CCFLAGS} foo.c -o foo.pie -Wl,-pie + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.exe foo.pie + diff --git a/dyld/unit-tests/test-cases/dlopen-executable/foo.c b/dyld/unit-tests/test-cases/dlopen-executable/foo.c new file mode 100644 index 0000000..d639011 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-executable/foo.c @@ -0,0 +1,10 @@ +int foo() +{ + return 10; +} + +int main() +{ + return 0; +} + diff --git a/dyld/unit-tests/test-cases/dlopen-executable/main.c b/dyld/unit-tests/test-cases/dlopen-executable/main.c new file mode 100644 index 0000000..a9a6f21 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-executable/main.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*fooproc)(void); + +int main() +{ + // dlopen of regular executable should fail + void* handle = dlopen("./foo.exe", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-executable: dlopen(\"./foo.exe\") did not fail"); + return EXIT_SUCCESS; + } + + // dlopen of pie should work + handle = dlopen("./foo.pie", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen-executable: dlopen(\"./foo.pie\") failed with: %s", dlerror()); + return EXIT_SUCCESS; + } + + fooproc pfoo = (fooproc)dlsym(handle, "foo"); + if ( pfoo == NULL ) { + FAIL("dlopen-executable: dlsym(handle, \"foo\") failed"); + return EXIT_SUCCESS; + } + + if ( (*pfoo)() != 10 ) { + FAIL("dlopen-executable: foo() != 10"); + return EXIT_SUCCESS; + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlopen-executable: dlclose(handle) returned %d", result); + return EXIT_SUCCESS; + } + + PASS("dlopen-executable"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile b/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile new file mode 100644 index 0000000..fec0176 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.bundle + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c b/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c new file mode 100644 index 0000000..fea884b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/main.c b/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/main.c new file mode 100644 index 0000000..e208e14 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-from-anonymous-code/main.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // memcpy +#include // exit(), EXIT_SUCCESS +#include +#include // sys_icache_invalidate +#include // for mprotext +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +void* calldlopen(const char* path, int mode, void* (*dlopen_proc)(const char* path, int mode)) +{ + return (*dlopen_proc)(path, mode); +} + +#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() +{ + // 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)) = ADDR_FROM_BLOCK(codeBlock); + + void* handle = (*caller)("foo.bundle", RTLD_LAZY, &dlopen); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "foo.bundle", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + + PASS("dlopen-from-anonymous-code"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-in-initializer/Makefile b/dyld/unit-tests/test-cases/dlopen-in-initializer/Makefile new file mode 100644 index 0000000..8623188 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-in-initializer/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-in-initializer" + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-in-initializer/foo.c b/dyld/unit-tests/test-cases/dlopen-in-initializer/foo.c new file mode 100644 index 0000000..3bef65e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-in-initializer/foo.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // call dlopen on a non-existent file to verify error handled cleanly + dlopen("/no_such/path", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-in-initializer/main.c b/dyld/unit-tests/test-cases/dlopen-in-initializer/main.c new file mode 100644 index 0000000..bdc6dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-in-initializer/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlopen-in-initializer"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile new file mode 100644 index 0000000..b24fa5f --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/Makefile @@ -0,0 +1,62 @@ +all-check: all check + +check:## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### ADOBE: Premiere Pro crashes on quit +### +### libfoo depends on libfoo1 and libfoo2. main dlopens(libfoo). +### libfoo1 has an initializer that calls dlopen(libbar). +### libbar depends on libfoo2 +### + +all-check: all check + +check: + ./main + +all: main + +main : main.cxx libfoo.dylib + ${CXX} ${CCXXFLAGS} -I${TESTROOT}/include -o main main.cxx + + +libfoo.dylib : foo.c libfoo1.dylib libfoo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libfoo1.dylib libfoo2.dylib + +libfoo1.dylib : foo1.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o libfoo1.dylib + +libfoo2.dylib : foo2.c + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o libfoo2.dylib + +libbar.dylib : bar.c libfoo2.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo2.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib libfoo1.dylib libfoo2.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c new file mode 100644 index 0000000..edbdbc4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c new file mode 100644 index 0000000..c24996b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo1.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // call dlopen to verify that initializer lock can be held recursively + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo1() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c new file mode 100644 index 0000000..5dd0a2a --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/foo2.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + +} + + +int foo2() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx new file mode 100644 index 0000000..cb916f2 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-notify/main.cxx @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + +static std::set sCurrentImages; + +static void notify(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + //fprintf(stderr, "mh=%p\n", mh); + if ( sCurrentImages.count(mh) != 0 ) { + FAIL("notified twice about %p", mh); + exit(0); + } + sCurrentImages.insert(mh); +} + + + +int main() +{ + _dyld_register_func_for_add_image(¬ify); + + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen-notify"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile new file mode 100644 index 0000000..8144bce --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-init-dlopen-up" + +all: main + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c new file mode 100644 index 0000000..15b1327 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/bar.c @@ -0,0 +1,5 @@ + + +extern int foo(); + +int bar() { return foo(); } diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c new file mode 100644 index 0000000..a816ea8 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/foo.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // this is an upward dependency since libbar depends on libfoo + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/main.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/main.c new file mode 100644 index 0000000..d9a2c70 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen-up/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + +} + + + +int main() +{ + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen-up"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen/Makefile b/dyld/unit-tests/test-cases/dlopen-init-dlopen/Makefile new file mode 100644 index 0000000..67d3a63 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-init-dlopen" + +all: main + +main : main.c libbar.dylib libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen/bar.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen/foo.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen/foo.c new file mode 100644 index 0000000..78ab60d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen/foo.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // call dlopen to verify that initializer lock can be held recursively + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-dlopen/main.c b/dyld/unit-tests/test-cases/dlopen-init-dlopen/main.c new file mode 100644 index 0000000..9f1c5fb --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-dlopen/main.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + if ( result == 1 ) { + // panther dyld returns 1 if you try to dlclose() a dylib + XFAIL("dlclose(handle) returned %d", result); + } + else { + FAIL("dlclose(handle) returned %d", result); + exit(0); + } + } + +} + + + +int main() +{ + trySO("libfoo.dylib"); + + PASS("dlopen-init-dlopen"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-up/Makefile b/dyld/unit-tests/test-cases/dlopen-init-up/Makefile new file mode 100644 index 0000000..104e51b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-up/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main || echo "FAIL dlopen-init-dlopen-up" + +all: main + +main : main.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbar.dylib + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libbar.dylib : bar.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-init-up/bar.c b/dyld/unit-tests/test-cases/dlopen-init-up/bar.c new file mode 100644 index 0000000..15b1327 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-up/bar.c @@ -0,0 +1,5 @@ + + +extern int foo(); + +int bar() { return foo(); } diff --git a/dyld/unit-tests/test-cases/dlopen-init-up/foo.c b/dyld/unit-tests/test-cases/dlopen-init-up/foo.c new file mode 100644 index 0000000..77dd28f --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-up/foo.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static void myInit() __attribute__((constructor)); + +static void myInit() +{ + // this is an upward dependency since libbar depends on libfoo + // rdar://problem/5213017 + dlopen("libbar.dylib", RTLD_LAZY); +} + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-init-up/main.c b/dyld/unit-tests/test-cases/dlopen-init-up/main.c new file mode 100644 index 0000000..abdd5f1 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-init-up/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int main() +{ + PASS("dlopen-init-up"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-initializer/Makefile b/dyld/unit-tests/test-cases/dlopen-initializer/Makefile new file mode 100644 index 0000000..a4bc8a5 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-initializer/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test1.dylib test2.dylib + +main : main.c test1.dylib test2.dylib test3.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +test1.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -DINIT_NAME=myinit -o test1.dylib + +test2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -DINIT_NAME=_init -o test2.dylib + +test3.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o test3.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test1.dylib test2.dylib test3.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-initializer/bar.c b/dyld/unit-tests/test-cases/dlopen-initializer/bar.c new file mode 100644 index 0000000..e055874 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-initializer/bar.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +static int initCount = 0; + + +void _init() +{ + initCount++; +} + +int getInitCount() +{ + return initCount; +} + diff --git a/dyld/unit-tests/test-cases/dlopen-initializer/foo.c b/dyld/unit-tests/test-cases/dlopen-initializer/foo.c new file mode 100644 index 0000000..9ffde51 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-initializer/foo.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +static int initCount = 0; + +void INIT_NAME() __attribute__((constructor)); + +void INIT_NAME() +{ + initCount++; +} + +int getInitCount() +{ + return initCount; +} + diff --git a/dyld/unit-tests/test-cases/dlopen-initializer/main.c b/dyld/unit-tests/test-cases/dlopen-initializer/main.c new file mode 100644 index 0000000..5dc4f2e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-initializer/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*getInitCountProc)(void); + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", path); + exit(0); + } + + getInitCountProc sym = (getInitCountProc)dlsym(handle, "getInitCount"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"getInitCount\") failed"); + exit(0); + } + + int count = (*sym)(); + if ( count != 1 ) { + FAIL("initializer in %s called %d times", path, count); + exit(0); + } + +} + + + +int main() +{ + trySO("test1.dylib"); + trySO("test2.dylib"); + //trySO("test3.dylib"); // Mac OS X 10.4 does not automatically run _init functions + PASS("dlopen-initializer"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-leak-threaded/Makefile b/dyld/unit-tests/test-cases/dlopen-leak-threaded/Makefile new file mode 100644 index 0000000..83d46ff --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-leak-threaded/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + DYLD_LIBRARY_PATH=hide && ./main + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + + +all: main + + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} *~ main diff --git a/dyld/unit-tests/test-cases/dlopen-leak-threaded/main.c b/dyld/unit-tests/test-cases/dlopen-leak-threaded/main.c new file mode 100644 index 0000000..c1b3a37 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-leak-threaded/main.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include + + +#include "test.h" + + +/// +/// Test that the dyld copy of the unwinder keeps each thread's +/// exception object seperate. +/// + +static void* work(void* arg) +{ + // will fail and cause exception to be thrown inside dyld + dlopen((char*)arg, RTLD_LAZY); + return NULL; +} + + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, "/frazzle/bar") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, "/frazzle/foo") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, "/frazzle/dazzle") != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int status = system(cmd); + if ( status == EXIT_SUCCESS ) + PASS("dlopen-leak-threaded"); + else + FAIL("dlopen-leak-threaded"); + + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-leak/Makefile b/dyld/unit-tests/test-cases/dlopen-leak/Makefile new file mode 100644 index 0000000..a9f14c8 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-leak/Makefile @@ -0,0 +1,68 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + DYLD_LIBRARY_PATH=hide && ./main + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + + +all: main + + +hide/libbar.dylib : bar.c + mkdir -p hide + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib + +libfoo.dylib : foo.c hide/libbar.dylib + ${CC} foo.c hide/libbar.dylib -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide diff --git a/dyld/unit-tests/test-cases/dlopen-leak/bar.c b/dyld/unit-tests/test-cases/dlopen-leak/bar.c new file mode 100644 index 0000000..a48d74f --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-leak/bar.c @@ -0,0 +1,6 @@ +#include + +void bar() +{ + strcpy(NULL, NULL); +} diff --git a/dyld/unit-tests/test-cases/dlopen-leak/foo.c b/dyld/unit-tests/test-cases/dlopen-leak/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/dlopen-leak/main.c b/dyld/unit-tests/test-cases/dlopen-leak/main.c new file mode 100644 index 0000000..3ff66e5 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-leak/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2007-2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + dlopen("libnotthere.dylib", RTLD_LAZY); + } + + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen-leak"); + else + FAIL("dlopen-leak"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-local-and-global/Makefile b/dyld/unit-tests/test-cases/dlopen-local-and-global/Makefile new file mode 100644 index 0000000..dae1997 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-local-and-global/Makefile @@ -0,0 +1,48 @@ +## +# 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-local-first + ./main-global-first + +all: main-local-first main-global-first foo.bundle foo.dylib + +main-local-first : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -DLOCAL_FIRST -o main-local-first + +main-global-first : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main-global-first + +foo.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o foo.bundle + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib main-global-first main-local-first + diff --git a/dyld/unit-tests/test-cases/dlopen-local-and-global/bar.c b/dyld/unit-tests/test-cases/dlopen-local-and-global/bar.c new file mode 100644 index 0000000..e8e0806 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-local-and-global/bar.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int foo; + +int bar() +{ + return foo; +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-local-and-global/foo.c b/dyld/unit-tests/test-cases/dlopen-local-and-global/foo.c new file mode 100644 index 0000000..8689597 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-local-and-global/foo.c @@ -0,0 +1,25 @@ +/* + * 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 = 0; diff --git a/dyld/unit-tests/test-cases/dlopen-local-and-global/main.c b/dyld/unit-tests/test-cases/dlopen-local-and-global/main.c new file mode 100644 index 0000000..5c3b97e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-local-and-global/main.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + + +static void* openWithModeGetSymbol(const char* path, int mode, const char* symbol) +{ + void* handle = dlopen(path, mode); + if ( handle == NULL ) { + const char* msg = dlerror(); + if ( ((mode & RTLD_LOCAL) != 0) && (strstr(msg, "RTLD_LOCAL") != NULL) ) + XFAIL("dlopen(\"%s\") failed: %s", path, msg); + else + FAIL("dlopen(\"%s\") failed: %s", path, msg); + exit(0); + } + + void* sym = dlsym(handle, symbol); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"%s\") failed", symbol); + exit(0); + } + return sym; +} + +static void trySO(const char* path) +{ +#if LOCAL_FIRST + void* symLocal = openWithModeGetSymbol(path, RTLD_LOCAL, "foo"); + void* symGlobal = openWithModeGetSymbol(path, RTLD_GLOBAL, "foo"); +#else + void* symGlobal = openWithModeGetSymbol(path, RTLD_GLOBAL, "foo"); + void* symLocal = openWithModeGetSymbol(path, RTLD_LOCAL, "foo"); +#endif + if ( symLocal != symGlobal ) { + FAIL("global load after local load failed"); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + trySO("foo.bundle"); + trySO("foo.dylib"); + + PASS("dlopen-local-and-global"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-multi/Makefile b/dyld/unit-tests/test-cases/dlopen-multi/Makefile new file mode 100644 index 0000000..800a40c --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-multi/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-multi/foo.c b/dyld/unit-tests/test-cases/dlopen-multi/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-multi/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlopen-multi/main.c b/dyld/unit-tests/test-cases/dlopen-multi/main.c new file mode 100644 index 0000000..180e91e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-multi/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int main() +{ + void* handle1 = dlopen("test.bundle", RTLD_LAZY); + void* handle2 = dlopen("test.bundle", RTLD_LAZY); + void* handle3 = dlopen("test.dylib", RTLD_LAZY); + void* handle4 = dlopen("test.dylib", RTLD_LAZY); + if ((NULL == handle1)||(NULL == handle2)) { + FAIL("dlopen(\"test.bundle\") failed"); + } + if ((NULL == handle3)||(NULL == handle4)) { + FAIL("dlopen(\"test.dylib\") failed"); + } + if (handle1 != handle2) { + FAIL("dlopen handle1 and handle2 are not equal %p != %p",handle1,handle2); + } + if (handle3 != handle4) { + FAIL("dlopen handle3 and handle4 are not equal %p != %p",handle3,handle4); + } + if (dlclose(handle4)) { + XFAIL("Could not close handle4"); + } + if (dlclose(handle2)) { + FAIL("Could not close handle2"); + } + void* sym = dlsym(handle1, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle1, \"foo\") failed"); + exit(0); + } + sym = dlsym(handle3, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle3, \"foo\") failed"); + exit(0); + } + if (dlclose(handle1)) { + XFAIL("Could not close handle1"); + } + if (dlclose(handle3)) { + XFAIL("Could not close handle3"); + } + + PASS("dlopen-multi"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-non-canonical-path/Makefile b/dyld/unit-tests/test-cases/dlopen-non-canonical-path/Makefile new file mode 100644 index 0000000..e933867 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-non-canonical-path/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} main diff --git a/dyld/unit-tests/test-cases/dlopen-non-canonical-path/main.c b/dyld/unit-tests/test-cases/dlopen-non-canonical-path/main.c new file mode 100644 index 0000000..023c370 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-non-canonical-path/main.c @@ -0,0 +1,60 @@ +/* + * 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 // EXIT_SUCCESS +#include +#include +#include + +#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); + } +} + +// +// 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; +} + + diff --git a/dyld/unit-tests/test-cases/dlopen-notify-bind/Makefile b/dyld/unit-tests/test-cases/dlopen-notify-bind/Makefile new file mode 100644 index 0000000..484ef57 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-notify-bind/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### CFSTRs cause crashes in Leopard +### +### main registers for notification and then dlopens(libfoo). +### In the notification callback, main calls NSLookupSymbolInImage(BIND) +### which double bound libfoo +### + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wno-deprecated-declarations -o main main.c + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen-notify-bind/foo.c b/dyld/unit-tests/test-cases/dlopen-notify-bind/foo.c new file mode 100644 index 0000000..8dab6be --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-notify-bind/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + +void* externalRlocToMalloc = &malloc; + +void* foo() +{ + return externalRlocToMalloc; +} diff --git a/dyld/unit-tests/test-cases/dlopen-notify-bind/main.c b/dyld/unit-tests/test-cases/dlopen-notify-bind/main.c new file mode 100644 index 0000000..630ba97 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-notify-bind/main.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +typedef void* (*fooProc)(); + + +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 +} + + +int main() +{ + _dyld_register_func_for_add_image(¬ify); + + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", "libfoo.dylib", dlerror()); + exit(0); + } + + fooProc fooPtr = (fooProc)dlsym(handle, "foo"); + if ( fooPtr == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + void* foosMalloc = (*fooPtr)(); + //fprintf(stderr, "foo says &malloc=%p\n", foosMalloc); + //fprintf(stderr, "&malloc=%p\n", &malloc); + + dlclose(handle); + + if ( foosMalloc == &malloc ) + PASS("dlopen-notify-bind"); + else + FAIL("dlopen-notify-bind libfoo.dylib double bound"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-sandbox/Makefile b/dyld/unit-tests/test-cases/dlopen-sandbox/Makefile new file mode 100644 index 0000000..f52f1b2 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-sandbox/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2015 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: + sandbox-exec -f sbs/deny-open.sb ./main "file system sandbox blocked open" + sandbox-exec -f sbs/deny-stat.sb ./main "file system sandbox blocked stat" + sandbox-exec -f sbs/deny-mmap.sb ./main "file system sandbox blocked mmap" + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + mkdir -p sbs + sed -e 's/FILTER/file-read-data/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-open.sb + sed -e 's/FILTER/file-read-metadata/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-stat.sb + sed -e 's/FILTER/file-map-executable/' -e "s|DIR|$$PWD|" < base.sb > sbs/deny-mmap.sb + +clean: + ${RM} -rf *~ main sbs libfoo.dylib + + diff --git a/dyld/unit-tests/test-cases/dlopen-sandbox/base.sb b/dyld/unit-tests/test-cases/dlopen-sandbox/base.sb new file mode 100644 index 0000000..c97f7e8 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-sandbox/base.sb @@ -0,0 +1,9 @@ + +(version 1) +(allow default) +(debug deny) + +(deny FILTER + (literal "DIR/libfoo.dylib")) + + diff --git a/dyld/unit-tests/test-cases/dlopen-sandbox/foo.c b/dyld/unit-tests/test-cases/dlopen-sandbox/foo.c new file mode 100644 index 0000000..8f1b3d8 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-sandbox/foo.c @@ -0,0 +1,7 @@ + + +int foo() +{ + return 42; +} + diff --git a/dyld/unit-tests/test-cases/dlopen-sandbox/main.c b/dyld/unit-tests/test-cases/dlopen-sandbox/main.c new file mode 100644 index 0000000..f3ade8f --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-sandbox/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2015 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + void* handle = dlopen("./libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("dlopen-sandbox dylib should not have loaded"); + return EXIT_SUCCESS; + } + const char* errorMsg = dlerror(); + const char* shouldContain = argv[1]; + if ( strstr(errorMsg, shouldContain) == NULL ) + FAIL("dlopen-sandbox dylib correctly failed to loaded, but with wrong error message: %s", errorMsg); + else + PASS("dlopen-sandbox: %s", shouldContain); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-search-leak/Makefile b/dyld/unit-tests/test-cases/dlopen-search-leak/Makefile new file mode 100644 index 0000000..e62fe0d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-search-leak/Makefile @@ -0,0 +1,71 @@ +## +# 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 + +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 diff --git a/dyld/unit-tests/test-cases/dlopen-search-leak/foo.c b/dyld/unit-tests/test-cases/dlopen-search-leak/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-search-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/dlopen-search-leak/main.c b/dyld/unit-tests/test-cases/dlopen-search-leak/main.c new file mode 100644 index 0000000..33c608b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-search-leak/main.c @@ -0,0 +1,51 @@ +/* + * 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 +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen-search-leak"); + else + FAIL("dlopen-search-leak"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen-zero/Makefile b/dyld/unit-tests/test-cases/dlopen-zero/Makefile new file mode 100644 index 0000000..b8a5145 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-zero/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/dlopen-zero/main.c b/dyld/unit-tests/test-cases/dlopen-zero/main.c new file mode 100644 index 0000000..81977a4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen-zero/main.c @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 42; +} + + +int main() +{ + // passing NULL as path to dlopen() has the special meaning of + // "get handle to main executable" + void* handle = dlopen(NULL, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(NULL, RTLD_LAZY) failed"); + exit(1); + } + + // make sure we find "foo" in this main executable + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(1); + } + + if ( sym != &foo ) { + FAIL("dlsym(handle, \"foo\") returned wrong address"); + exit(1); + } + + PASS("dlopen-zero"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-basic/Makefile b/dyld/unit-tests/test-cases/dlopen_preflight-basic/Makefile new file mode 100644 index 0000000..285779f --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-basic/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + ./main batch + +all: main foo.bundle bar.bundle bogus.bundle libbogus.dylib + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +bogus.bundle : + echo "bogus" > bogus.bundle + +libbogus.dylib : + echo "bogus" > libbogus.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle bogus.bundle libbogus.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-basic/bar.c b/dyld/unit-tests/test-cases/dlopen_preflight-basic/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-basic/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-basic/foo.c b/dyld/unit-tests/test-cases/dlopen_preflight-basic/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-basic/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-basic/main.c b/dyld/unit-tests/test-cases/dlopen_preflight-basic/main.c new file mode 100644 index 0000000..4fdec0e --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-basic/main.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //for (uint32_t i=0; i < infoCount; ++i) { + // fprintf(stderr, "batch mapped: %d/%d %s\n", i, infoCount, info[i].imageFilePath); + //} + for (uint32_t i=0; i < infoCount; ++i) { + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "cannot load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + if ( dlopen_preflight("foo.bundle") ) { + FAIL("dlopen_preflight-basic foo.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "foo.bundle, dlerror: %s\n", dlerror()); + + if ( dlopen_preflight("bar.bundle") ) { + FAIL("dlopen_preflight-basic bar.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "bar.bundle, dlerror: %s\n", dlerror()); + + if ( dlopen_preflight("bogus.bundle") ) { + FAIL("dlopen_preflight-basic bogus.bundle should not be loadable"); + exit(0); + } + //fprintf(stderr, "bogus.bundle, dlerror: %s\n", dlerror()); + + if ( ! dlopen_preflight("/usr/lib/libSystem.B.dylib") ) { + FAIL("dlopen_preflight-basic libSystem should be loadable, but got: %s", dlerror()); + exit(0); + } + + + PASS("dlopen_preflight-basic"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-cycle/Makefile b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/Makefile new file mode 100644 index 0000000..2123b40 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2011 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 + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbaz.dylib baz.c -Wl,-upward_library,libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c libbaz.dylib + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbaz.dylib libbar.dylib libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-cycle/bar.c b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-cycle/baz.c b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/baz.c new file mode 100644 index 0000000..256a0e3 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/baz.c @@ -0,0 +1 @@ +void baz() {} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-cycle/foo.c b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-cycle/main.c b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/main.c new file mode 100644 index 0000000..af5caec --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-cycle/main.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + if ( ! dlopen_preflight("libfoo.dylib") ) { + FAIL("dlopen_preflight-cycle libfoo.dylib should not be loadable"); + return EXIT_SUCCESS; + } + + void* handle = dlopen("libbar.dylib", RTLD_NOLOAD); + if ( handle != NULL ) { + FAIL("dlopen_preflight-cycle libbar.dylib was left loaded by dlopen_preflight()"); + return EXIT_SUCCESS; + } + + handle = dlopen("libbaz.dylib", RTLD_NOLOAD); + if ( handle != NULL ) { + FAIL("dlopen_preflight-cycle libbaz.dylib was left loaded by dlopen_preflight()"); + return EXIT_SUCCESS; + } + + handle = dlopen("libfoo.dylib", RTLD_NOLOAD); + if ( handle != NULL ) { + FAIL("dlopen_preflight-cycle libfoo.dylib was left loaded by dlopen_preflight()"); + return EXIT_SUCCESS; + } + + PASS("dlopen_preflight-cycle"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile b/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile new file mode 100644 index 0000000..f5e572f --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/Makefile @@ -0,0 +1,63 @@ +## +# Copyright (c) 2007-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen_preflight() if a non-batch image state handler +# denies the load +# Leaked fSegmentsArray and image segments during failed dlopen_preflight +# + + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + +all: main + +libfoo.dylib : foo.c + ${CC} foo.c -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c b/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c b/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c new file mode 100644 index 0000000..48b5b72 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-leak-image-deny-single/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2007-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" + +static bool doCheck = false; + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( doCheck ) + return "can't load"; + return NULL; +} + + +int main() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, batchMappedHandler); + doCheck = true; + + for (int i=0; i < 10; ++i) { + dlopen_preflight("libfoo.dylib"); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen_preflight-leak-image-deny-single"); + else + FAIL("dlopen_preflight-leak-image-deny-single"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-leak/Makefile b/dyld/unit-tests/test-cases/dlopen_preflight-leak/Makefile new file mode 100644 index 0000000..1752c26 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-leak/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with dlopen/close on success and failure +# + +# leaks does not work on rosetta processes +CHECK = check-real +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + CHECK = check-xfail + endif +endif + +all-check: all check + +check: ${CHECK} + +check-real: + DYLD_LIBRARY_PATH=hide && ./main + ./main + +check-xfail: + echo "XFAIL dlopen-leak"; + +all: main + +hide/libbar.dylib : bar.c + mkdir -p hide + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib + +libfoo.dylib : foo.c hide/libbar.dylib + ${CC} foo.c hide/libbar.dylib -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-leak/bar.c b/dyld/unit-tests/test-cases/dlopen_preflight-leak/bar.c new file mode 100644 index 0000000..b72a1a5 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-leak/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-leak/foo.c b/dyld/unit-tests/test-cases/dlopen_preflight-leak/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-leak/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/dlopen_preflight-leak/main.c b/dyld/unit-tests/test-cases/dlopen_preflight-leak/main.c new file mode 100644 index 0000000..0941597 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlopen_preflight-leak/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + dlopen_preflight("libfoo.dylib"); + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("dlopen_preflight-leak"); + else + FAIL("dlopen_preflight-leak"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile b/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile new file mode 100644 index 0000000..d1f6bf0 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/foo.c b/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/foo.c new file mode 100644 index 0000000..675f4df --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/foo.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +char* strdup(const char* str) +{ + return "from foo"; +} + +typedef char* (*strdupProc)(const char* str); + + +void myinit() __attribute__((constructor)); +void myinit() +{ + strdupProc sym = (strdupProc)dlsym(RTLD_DEFAULT, "strdup"); + if ( sym == NULL ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") failed"); + exit(0); + } + + const char* result = (*sym)("hello"); + if ( strcmp(result, "from main") != 0 ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") returned wrong strdup: %s", result); + exit(0); + } + +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/main.c b/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/main.c new file mode 100644 index 0000000..ed3d884 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_DEFAULT/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +typedef char* (*strdupProc)(const char* str); + + +char* strdup(const char* str) +{ + return "from main"; +} + + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", path); + exit(0); + } + + strdupProc sym = (strdupProc)dlsym(RTLD_DEFAULT, "strdup"); + if ( sym == NULL ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") failed"); + exit(0); + } + + const char* result = (*sym)("hello"); + if ( strcmp(result, "from main") != 0 ) { + FAIL("dlsym(RTLD_DEFAULT, \"strdup\") returned wrong strdup: %s", result); + exit(0); + } + +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + PASS("dlsym-RTLD_DEFAULT bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile b/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile new file mode 100644 index 0000000..9e2b6da --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/Makefile @@ -0,0 +1,22 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c b/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c new file mode 100644 index 0000000..a82c67d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/foo.c @@ -0,0 +1,33 @@ +/* + * 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; +} + +int bar() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c b/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c new file mode 100644 index 0000000..65aacee --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_MAIN_ONLY/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2008 Apple, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 0; +} + + +int main() +{ + // make sure we get the foo() in main and not the one in libfoo.dylib + if ( dlsym(RTLD_MAIN_ONLY, "foo") != &foo ) { + FAIL("dlsym(RTLD_MAIN_ONLY, \"foo\") returned wrong value"); + } + + // make sure don't find bar() in libfoo.dylib + if ( dlsym(RTLD_MAIN_ONLY, "bar") != NULL ) { + FAIL("dlsym(RTLD_MAIN_ONLY, \"bar\") returned non-NULL"); + } + + + PASS("dlsym-RTLD_MAIN_ONLY"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile new file mode 100644 index 0000000..8be6211 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/Makefile @@ -0,0 +1,61 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PROG = ./main + +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + PROG = /usr/bin/true + endif +endif + + + +all-check: all check + +check: + ${TESTROOT}/bin/exit-zero-pass.pl "dlsym-RTLD_NEXT-missing with circular libraries" "dlsym-RTLD_NEXT-missing with circular libraries" ${PROG} + +all: main + +main : main.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo1.dylib + + +foo1.dylib : foo1.c foo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1.dylib foo2.dylib + +foo2.dylib : foo2.c foo3.dylib + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o foo2.dylib foo3.dylib + +foo3.dylib : foo3.c + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1-temp.dylib -install_name foo1.dylib + ${CC} ${CCFLAGS} -dynamiclib foo3.c -o foo3.dylib foo1-temp.dylib + rm foo1-temp.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle foo1.dylib foo2.dylib foo3.dylib + diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c new file mode 100644 index 0000000..eda5424 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo1.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo1() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c new file mode 100644 index 0000000..a17cf3d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo2.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo2() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c new file mode 100644 index 0000000..923e363 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/foo3.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo3() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c new file mode 100644 index 0000000..914cca6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT-missing/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This process has no "bar" function, but it does have libraries with a +/// circular dependency. In 10.4.0 this cause dlsym(RTLD_NEXT) to +/// go into an infinite loop. +/// + + + +int main() +{ + dlsym(RTLD_NEXT, "bar"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile new file mode 100644 index 0000000..4a148d8 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/Makefile @@ -0,0 +1,51 @@ +## +# 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 + +main : main.c test.bundle test.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : test.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle test.c foo1.dylib -o test.bundle + +test.dylib : test.c foo2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c foo2.dylib -o test.dylib + +foo1.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo1.dylib + +foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo2.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib foo1.dylib foo2.dylib + diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/foo.c b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/main.c b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/main.c new file mode 100644 index 0000000..5c1064b --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This process has five foo functions. They are in: +/// main, test.bundle, test.dylib, foo1.dylib, foo2.dylib +/// +/// Both test.bundle and test.dylib call dlsym(RTLD_NEXT, "foo"); +/// They should find the ones in foo1.dylib and foo2.dylib respectively. +/// We test this be looking up those symbols explictly. +/// + + +int foo() +{ + return 0; +} + +typedef void* (*TestProc)(void); + +static void trySO(const char* pathToLoad, const char* indirectLibrary) +{ + void* indirectHandle = dlopen(indirectLibrary, RTLD_LAZY); + if ( indirectHandle == NULL ) { + FAIL("dlopen(\"%s\") failed", indirectLibrary); + exit(0); + } + + void* indirectFoo = (TestProc)dlsym(indirectHandle, "foo"); + if ( indirectFoo == NULL ) { + FAIL("dlsym(handle, \"test\") failed"); + exit(0); + } + + void* handle = dlopen(pathToLoad, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", pathToLoad); + exit(0); + } + + TestProc sym = (TestProc)dlsym(handle, "test"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"test\") failed"); + exit(0); + } + + void* targetFoo = (*sym)(); + + //printf("targetFoo = %p, indirectFoo = %p\n", targetFoo, indirectFoo); + + if ( targetFoo != indirectFoo ) { + FAIL("dlsym-RTLD_NEXT wrong foo found"); + exit(0); + } + +} + + + +int main() +{ + trySO("test.bundle", "foo1.dylib"); + trySO("test.dylib", "foo2.dylib"); + + PASS("dlsym-RTLD_NEXT bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/test.c b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/test.c new file mode 100644 index 0000000..0d4b463 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_NEXT/test.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +void* test() +{ + return dlsym(RTLD_NEXT, "foo"); +} + +int foo() +{ + return 2; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile new file mode 100644 index 0000000..4a148d8 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/Makefile @@ -0,0 +1,51 @@ +## +# 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 + +main : main.c test.bundle test.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +test.bundle : test.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle test.c foo1.dylib -o test.bundle + +test.dylib : test.c foo2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c foo2.dylib -o test.dylib + +foo1.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo1.dylib + +foo2.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo2.dylib + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib foo1.dylib foo2.dylib + diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/foo.c b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/main.c b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/main.c new file mode 100644 index 0000000..8c137c6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/main.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This process has five foo functions. They are in: +/// main, test.bundle, test.dylib, foo1.dylib, foo2.dylib +/// +/// Both test.bundle and test.dylib call dlsym(RTLD_SELF, "foo"); +/// They should find the ones in their own linkage unit and +/// call FAIL() otherwise. +/// We also check that this works in the main executable. +/// + + +int foo() +{ + return 0; +} + +typedef void (*TestProc)(void); + +static void trySO(const char* pathToLoad) +{ + void* handle = dlopen(pathToLoad, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed", pathToLoad); + exit(0); + } + + TestProc sym = (TestProc)dlsym(handle, "test"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"test\") failed"); + exit(0); + } + + (*sym)(); +} + + + +int main() +{ + trySO("test.bundle"); + trySO("test.dylib"); + + if ( dlsym(RTLD_SELF, "foo") != &foo ) { + FAIL("dlsym(RTLD_SELF, \"foo\") returned wrong value"); + } + + PASS("dlsym-RTLD_SELF bundle and dylib"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/test.c b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/test.c new file mode 100644 index 0000000..b4ae941 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-RTLD_SELF/test.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 2; +} + +void test() +{ +#ifdef RTLD_SELF + if ( dlsym(RTLD_SELF, "foo") != &foo ) + FAIL("dlsym(RTLD_SELF, \"foo\") returned wrong value"); +#endif +} diff --git a/dyld/unit-tests/test-cases/dlsym-error/Makefile b/dyld/unit-tests/test-cases/dlsym-error/Makefile new file mode 100644 index 0000000..b8a5145 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-error/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/dlsym-error/main.c b/dyld/unit-tests/test-cases/dlsym-error/main.c new file mode 100644 index 0000000..cb4a9d6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-error/main.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int foo() +{ + return 42; +} + + +int main() +{ + void* handle = (void*)0x12345; // bogus value + + // expect dlsym() to return NULL + void* sym = dlsym(handle, "foo"); + if ( sym != NULL ) { + FAIL("dlsym(handle, \"foo\") should not have succeeded"); + exit(1); + } + // expect dlerro() to mention "handle" + if ( strstr(dlerror(), "handle") == NULL ) { + FAIL("dlerror() after dlsym(handle, \"foo\") does not mention \"handle\""); + exit(1); + } + + PASS("dlsym-error"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dlsym-indirect/Makefile b/dyld/unit-tests/test-cases/dlsym-indirect/Makefile new file mode 100644 index 0000000..f806baa --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-indirect/Makefile @@ -0,0 +1,55 @@ +## +# 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 foo.dylib + ./main foo.bundle + +all: main + +main : main.c foo.bundle foo.dylib foo3.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +foo.bundle : foo.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -bundle foo.c foo1.dylib -o foo.bundle + +foo.dylib : foo.c foo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c foo1.dylib -o foo.dylib + +foo1.dylib : foo1.c foo2.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c -o foo1.dylib foo2.dylib + +foo2.dylib : foo2.c + ${CC} ${CCFLAGS} -dynamiclib foo2.c -o foo2.dylib + +foo3.dylib : foo3.c + ${CC} ${CCFLAGS} -dynamiclib foo3.c -o foo3.dylib + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle foo.dylib foo1.dylib foo2.dylib foo3.dylib + diff --git a/dyld/unit-tests/test-cases/dlsym-indirect/foo.c b/dyld/unit-tests/test-cases/dlsym-indirect/foo.c new file mode 100644 index 0000000..81f7dcf --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-indirect/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-indirect/foo1.c b/dyld/unit-tests/test-cases/dlsym-indirect/foo1.c new file mode 100644 index 0000000..eda5424 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-indirect/foo1.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo1() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-indirect/foo2.c b/dyld/unit-tests/test-cases/dlsym-indirect/foo2.c new file mode 100644 index 0000000..a17cf3d --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-indirect/foo2.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo2() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-indirect/foo3.c b/dyld/unit-tests/test-cases/dlsym-indirect/foo3.c new file mode 100644 index 0000000..923e363 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-indirect/foo3.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +int foo3() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/dlsym-indirect/main.c b/dyld/unit-tests/test-cases/dlsym-indirect/main.c new file mode 100644 index 0000000..e619d82 --- /dev/null +++ b/dyld/unit-tests/test-cases/dlsym-indirect/main.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// This tests that dlsym() will search indirect libraries +/// rdar://problem/4047391 +/// + + + +int main(int argc, const char* argv[]) +{ + const char* path = argv[1]; + const char* otherPath = "foo3.dylib"; + + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlsym-indirect dlopen(\"%s\") failed", path); + exit(0); + } + + void* handle3 = dlopen(otherPath, RTLD_LAZY); + if ( handle3 == NULL ) { + FAIL("dlsym-indirect dlopen(\"%s\") failed", otherPath); + exit(0); + } + + void* foo = dlsym(handle, "foo"); + if ( foo == NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo\") failed"); + exit(0); + } + + void* foo1 = dlsym(handle, "foo1"); + if ( foo1 == NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo1\") failed"); + //exit(0); + } + + void* foo2 = dlsym(handle, "foo2"); + if ( foo2 == NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo2\") failed"); + //exit(0); + } + + void* foo3 = dlsym(handle, "foo3"); + if ( foo3 != NULL ) { + FAIL("dlsym-indirect dlsym(handle, \"foo3\") should have failed"); + //exit(0); + } + + PASS("dlsym-indirect %s", path); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/dtrace-static-probes/Makefile b/dyld/unit-tests/test-cases/dtrace-static-probes/Makefile new file mode 100644 index 0000000..f800ae4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dtrace-static-probes/Makefile @@ -0,0 +1,58 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that dtrace probes fire +# + +ifeq "$(OS_NAME)" "iPhoneOS" + SUDO = +else + SUDO = sudo +endif + +all-check: all check + +check: + 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 \ + echo "FAIL dtrace-static-probes"; \ + fi; \ + + + +all: main + + +main: main.c + dtrace -h -s foo.d + ${CC} ${CCFLAGS} main.c -o main -dead_strip + + +clean: + rm -rf main foo.h + diff --git a/dyld/unit-tests/test-cases/dtrace-static-probes/foo.d b/dyld/unit-tests/test-cases/dtrace-static-probes/foo.d new file mode 100644 index 0000000..930965b --- /dev/null +++ b/dyld/unit-tests/test-cases/dtrace-static-probes/foo.d @@ -0,0 +1,7 @@ + +provider Foo { + probe count(int); +}; + + +#pragma D attributes Evolving/Evolving/Common provider Foo args diff --git a/dyld/unit-tests/test-cases/dtrace-static-probes/main.c b/dyld/unit-tests/test-cases/dtrace-static-probes/main.c new file mode 100644 index 0000000..851bb33 --- /dev/null +++ b/dyld/unit-tests/test-cases/dtrace-static-probes/main.c @@ -0,0 +1,13 @@ + +#include + +#include "foo.h" + + +int main() { + for (int i=0; i < 5; ++i) { + FOO_COUNT(i); + } + + return 0; +} diff --git a/dyld/unit-tests/test-cases/dyld-func-lookup/Makefile b/dyld/unit-tests/test-cases/dyld-func-lookup/Makefile new file mode 100644 index 0000000..c2a8e2e --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld-func-lookup/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main test.bundle test.dylib + +main : main.c foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.c + + +test.bundle : foo.c + ${CC} ${CCFLAGS} -bundle foo.c -o test.bundle + +test.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o test.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle test.dylib + diff --git a/dyld/unit-tests/test-cases/dyld-func-lookup/foo.c b/dyld/unit-tests/test-cases/dyld-func-lookup/foo.c new file mode 100644 index 0000000..bc232a2 --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld-func-lookup/foo.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + + +// _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); + + + +bool check_dyld_func_lookup() +{ + void* address; + + // dlopen does not exist, something is wrong + if ( ! _dyld_func_lookup("__dyld_dlopen", &address) ) + return false; + + // if a garbage string returns true, something is wrong + if ( _dyld_func_lookup("blablah", &address) ) + return false; + + // looks good + return true; +} + +#endif diff --git a/dyld/unit-tests/test-cases/dyld-func-lookup/main.c b/dyld/unit-tests/test-cases/dyld-func-lookup/main.c new file mode 100644 index 0000000..35bbd9f --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld-func-lookup/main.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#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); + +static void trySO(const char* path) +{ + void* handle = dlopen(path, RTLD_LAZY); + if ( handle == NULL ) { + FAIL("dlopen(\"%s\") failed with: %s", path, dlerror()); + exit(0); + } + + proc sym = (proc)dlsym(handle, "check_dyld_func_lookup"); + if ( sym == NULL ) { + FAIL("dlsym(handle, \"foo\") failed"); + exit(0); + } + + if ( sym == &check_dyld_func_lookup ) + FAIL("found same check_dyld_func_lookup as in main executable"); + + if ( ! (*sym)() ) + FAIL("check_dyld_func_lookup failed for %s", path); + + dlclose(handle); +} +#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; +} diff --git a/dyld/unit-tests/test-cases/dyld-launched-prebound/Makefile b/dyld/unit-tests/test-cases/dyld-launched-prebound/Makefile new file mode 100644 index 0000000..61f202b --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld-launched-prebound/Makefile @@ -0,0 +1,36 @@ +## +# 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: + ${TESTROOT}/bin/exit-zero-pass.pl "_dyld_launched_prebound() was implemented" "_dyld_launched_prebound() was not implemented" ./main + +all: + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/dyld-launched-prebound/main.c b/dyld/unit-tests/test-cases/dyld-launched-prebound/main.c new file mode 100644 index 0000000..aa6ab80 --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld-launched-prebound/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "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; +} diff --git a/dyld/unit-tests/test-cases/dyld_is_memory_immutable/Makefile b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/Makefile new file mode 100644 index 0000000..80fae8e --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/Makefile @@ -0,0 +1,21 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verify addends work +# + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include libfoo.dylib -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/dyld_is_memory_immutable/bar.c b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/bar.c new file mode 100644 index 0000000..7bf381e --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/bar.c @@ -0,0 +1,7 @@ + + + +const char* bar() +{ + return "bar"; +} diff --git a/dyld/unit-tests/test-cases/dyld_is_memory_immutable/foo.c b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/foo.c new file mode 100644 index 0000000..ab30bb4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/foo.c @@ -0,0 +1,6 @@ + + +const char* foo() +{ + return "foo"; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/dyld_is_memory_immutable/main.c b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/main.c new file mode 100644 index 0000000..aa3a9d4 --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld_is_memory_immutable/main.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern uint32_t _cpu_capabilities; + +extern const char* foo(); + +typedef const char* (*BarProc)(void); + +const char* myStr = "myStr"; + +int myInt; + +int main() +{ + if ( !_dyld_is_memory_immutable(myStr, 6) ) { + FAIL("_dyld_is_memory_immutable() returned false for string in main executable"); + return EXIT_SUCCESS; + } + + if ( _dyld_is_memory_immutable(strdup("hello"), 6) ) { + FAIL("_dyld_is_memory_immutable() returned true for result from strdup()"); + return EXIT_SUCCESS; + } + + if ( _dyld_is_memory_immutable(&myInt, 4) ) { + FAIL("_dyld_is_memory_immutable() returned true for global variabe in main executable"); + return EXIT_SUCCESS; + } + + if ( !_dyld_is_memory_immutable(foo(), 4) ) { + FAIL("_dyld_is_memory_immutable() returned false for string in statically linked dylib"); + return EXIT_SUCCESS; + } + + if ( !_dyld_is_memory_immutable(&strcpy, 4) ) { + FAIL("_dyld_is_memory_immutable() returned false for function in dyld shared cache"); + return EXIT_SUCCESS; + } + + if ( _dyld_is_memory_immutable(&_cpu_capabilities, 4) ) { + FAIL("_dyld_is_memory_immutable() returned true for global variabe in shared cache"); + return EXIT_SUCCESS; + } + + void* handle = dlopen("libbar.dylib", 0); + if ( handle == NULL ) { + FAIL("dlopen(libbar.dylib) failed"); + return EXIT_SUCCESS; + } + + BarProc proc = dlsym(handle, "bar"); + if ( proc == NULL ) { + FAIL("dlsym(bar) failed"); + return EXIT_SUCCESS; + } + const char* barStr = (*proc)(); + if ( _dyld_is_memory_immutable(barStr, 4) ) { + FAIL("_dyld_is_memory_immutable() returned true for string in unloadable dylib"); + return EXIT_SUCCESS; + } + + + PASS("_dyld_is_memory_immutable"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/dyld_shared_cache_iterate_text/Makefile b/dyld/unit-tests/test-cases/dyld_shared_cache_iterate_text/Makefile new file mode 100644 index 0000000..8f07d7f --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld_shared_cache_iterate_text/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2015 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 -I../../../launch-cache/ -o main main.c + +clean: + ${RM} ${RMFLAGS} main diff --git a/dyld/unit-tests/test-cases/dyld_shared_cache_iterate_text/main.c b/dyld/unit-tests/test-cases/dyld_shared_cache_iterate_text/main.c new file mode 100644 index 0000000..32ac531 --- /dev/null +++ b/dyld/unit-tests/test-cases/dyld_shared_cache_iterate_text/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // EXIT_SUCCESS +#include +#include +#include +#include +#include +#include +#include + +#include "test.h" +#include "dyld_cache_format.h" + + + +int main() +{ + uuid_t curUuid; + _dyld_get_shared_cache_uuid(curUuid); + + int __block imageCount = 0; + int result = dyld_shared_cache_iterate_text(curUuid, ^(const dyld_shared_cache_dylib_text_info* info) { + ++imageCount; + //printf(" cur: 0x%09llX -> 0x%09llX off=0x%0llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid + info->textSegmentSize, info->textSegmentOffset, info->path); + }); + if ( result != 0 ) { + FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() failed"); + exit(0); + } + + if ( imageCount < 500 ) { + FAIL("dyld_shared_cache_iterate_text: dyld_shared_cache_iterate_text() iterated less than 500 dylibs (%d)", imageCount); + exit(0); + } +#if 0 + //uuid_t fixedUUID = {0x3E, 0xDE, 0x37, 0x05, 0x81, 0x68, 0x33, 0xEF, 0xBF, 0x97, 0xC0, 0xF6, 0xD2, 0x4D, 0x93, 0xEC}; + uuid_t fixedUUID = {0xD4, 0x3B, 0x31, 0x2B, 0xA5, 0xA7, 0x3C, 0x55, 0x90, 0xA0, 0x9A, 0x37, 0x60, 0x7D, 0x70, 0xAF}; + result = dyld_shared_cache_iterate_text(fixedUUID, ^(const dyld_shared_cache_dylib_text_info* info) { + printf(" my: 0x%09llX -> 0x%09llX %s\n", info->loadAddressUnslid, info->loadAddressUnslid + info->textSegmentSize, info->path); + }); +#endif + + PASS("dyld_shared_cache_iterate_text"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/dynamic_cast-basic/Makefile b/dyld/unit-tests/test-cases/dynamic_cast-basic/Makefile new file mode 100644 index 0000000..79e8be6 --- /dev/null +++ b/dyld/unit-tests/test-cases/dynamic_cast-basic/Makefile @@ -0,0 +1,24 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.cxx librealmain.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx librealmain.dylib + +librealmain.dylib: realmain.cxx libfoo.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.cxx libfoo.dylib + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.cxx + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib librealmain.dylib + diff --git a/dyld/unit-tests/test-cases/dynamic_cast-basic/foo.cxx b/dyld/unit-tests/test-cases/dynamic_cast-basic/foo.cxx new file mode 100644 index 0000000..5c824bb --- /dev/null +++ b/dyld/unit-tests/test-cases/dynamic_cast-basic/foo.cxx @@ -0,0 +1,15 @@ + +#include + +#include "foo.h" + +test* maketestsub() +{ + return new testsub(); +} + +bool istestsub(test* t) +{ + return (dynamic_cast(t) != NULL); +} + diff --git a/dyld/unit-tests/test-cases/dynamic_cast-basic/foo.h b/dyld/unit-tests/test-cases/dynamic_cast-basic/foo.h new file mode 100644 index 0000000..940e356 --- /dev/null +++ b/dyld/unit-tests/test-cases/dynamic_cast-basic/foo.h @@ -0,0 +1,32 @@ + + +class test +{ +public: + virtual void aa() {} + + int f; +}; + +class testsub : public test +{ +public: + testsub() : g(0) {} + virtual void aa() {} + + int g; +}; + + +class testsubother : public test +{ +public: + testsubother() : h(0) {} + virtual void aa() {} + + int h; +}; + +extern test* maketestsub(); + +extern bool istestsub(test* t); \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/dynamic_cast-basic/main.cxx b/dyld/unit-tests/test-cases/dynamic_cast-basic/main.cxx new file mode 100644 index 0000000..a25c448 --- /dev/null +++ b/dyld/unit-tests/test-cases/dynamic_cast-basic/main.cxx @@ -0,0 +1,10 @@ + +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/dyld/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx b/dyld/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx new file mode 100644 index 0000000..e951768 --- /dev/null +++ b/dyld/unit-tests/test-cases/dynamic_cast-basic/realmain.cxx @@ -0,0 +1,24 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "foo.h" + + +void realmain() +{ + test* t1 = maketestsub(); + test* t2 = new testsub(); + test* t3 = new testsubother(); + testsub* t1a = dynamic_cast(t1); + testsubother* t3a = dynamic_cast(t3); + if ( (t1a == NULL) || (t3a == NULL) || !istestsub(t2) ) + FAIL("dynamic_cast-basic"); + else + PASS("dynamic_cast-basic"); +} + + diff --git a/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..ab33f8c --- /dev/null +++ b/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/hide && ./main hide/libz.dylib + ./main /usr/lib/libz + export DYLD_FALLBACK_LIBRARY_PATH="" && ${TESTROOT}/bin/exit-non-zero-pass.pl "env-DYLD_FALLBACK_LIBRARY_PATH" "env-DYLD_FALLBACK_LIBRARY_PATH with empty env" ./main2 + + +all: + mkdir -p hide + ${CC} compress.c -dynamiclib -o hide/libz.dylib -install_name /other/libz.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/libz.dylib + ${CC} -I${TESTROOT}/include main.c -DSHOULD_FAIL=1 -o main2 hide/libz.dylib + + +clean: + ${RM} -rf *~ main main2 hide diff --git a/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c b/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c new file mode 100644 index 0000000..b45d613 --- /dev/null +++ b/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/compress.c @@ -0,0 +1,4 @@ +int compress() +{ + return 0; +} diff --git a/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 0000000..3534032 --- /dev/null +++ b/dyld/unit-tests/test-cases/env-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +#include "test.h" + +extern void compress(); + +int main(int argc, const char* argv[]) +{ +#if !SHOULD_FAIL + Dl_info info; + if ( dladdr(&compress, &info) ) { + //fprintf(stderr, "_compress found in %s\n", info.dli_fname); + if ( strstr(info.dli_fname, argv[1]) != 0 ) { + PASS("env-DYLD_FALLBACK_LIBRARY_PATH"); + exit(0); + } + } + FAIL("env-DYLD_FALLBACK_LIBRARY_PATH"); +#endif + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/executable-image-index/Makefile b/dyld/unit-tests/test-cases/executable-image-index/Makefile new file mode 100644 index 0000000..b85494d --- /dev/null +++ b/dyld/unit-tests/test-cases/executable-image-index/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + #export DYLD_INSERT_LIBRARIES=libfoo.dylib && ./main + ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/executable-image-index/foo.c b/dyld/unit-tests/test-cases/executable-image-index/foo.c new file mode 100644 index 0000000..83c0b65 --- /dev/null +++ b/dyld/unit-tests/test-cases/executable-image-index/foo.c @@ -0,0 +1,2 @@ +void foo() { } + diff --git a/dyld/unit-tests/test-cases/executable-image-index/main.c b/dyld/unit-tests/test-cases/executable-image-index/main.c new file mode 100644 index 0000000..315ea8e --- /dev/null +++ b/dyld/unit-tests/test-cases/executable-image-index/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// many apps rely on _dyld_get_image_header(0) being the main executable +// DYLD_INSERT_LIBRARIES can change the order + +extern const struct mach_header __dso_handle; + +int main() +{ + if ( _dyld_get_image_header(0) == &__dso_handle ) + PASS("executable-image-index"); + else + FAIL("executable-image-index: index(0) is %s", _dyld_get_image_name(0)); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile new file mode 100644 index 0000000..1992633 --- /dev/null +++ b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +PWD = $(shell pwd) + + +all-check: all check + + +all: main + +main : main.c libfoo.dylib libbar.dylib + ${CC} -I${TESTROOT}/include main.c libfoo.dylib libbar.dylib -o main + +libfoo.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -DFOO=1 -dynamiclib -install_name ${PWD}/libfoo.dylib -o libfoo.dylib + +libbar.dylib : bar.c other/libfoo.dylib + ${CC} -I${TESTROOT}/include bar.c -dynamiclib other/libfoo.dylib -install_name ${PWD}/libbar.dylib -o libbar.dylib + +other/libfoo.dylib : foo.c + mkdir -p ${PWD}/other + ${CC} -I${TESTROOT}/include foo.c -DFOO=2 -dynamiclib -install_name ${PWD}/other/libfoo.dylib -o other/libfoo.dylib + + + +check: + export DYLD_FALLBACK_LIBRARY_PATH=${PWD} && ./main + + +clean: + rm -rf main libfoo.dylib libbar.dylib other \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/bar.c b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/bar.c new file mode 100644 index 0000000..b4415f8 --- /dev/null +++ b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int foo(); + +int bar() +{ + return foo(); +} diff --git a/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/foo.c b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/foo.c new file mode 100644 index 0000000..9644c23 --- /dev/null +++ b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +int foo() +{ + return FOO; +} diff --git a/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/main.c b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/main.c new file mode 100644 index 0000000..351769a --- /dev/null +++ b/dyld/unit-tests/test-cases/fallback-non-unique-leaf-names/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + +/// +/// This process has two libfoo.dylibs each of which has a foo() that returns a different value. +/// If dyld only loads one libfoo.dylib, then this test fails +/// main links directly against one libfoo.dylib and indirectly through libbar.dylib with the other. +/// + +extern int foo(); +extern int bar(); + +int main(int argc, const char* argv[]) +{ + if ( foo() == bar() ) { + FAIL("fallback-non-unique-leaf-names"); + return EXIT_SUCCESS; + } + + PASS("fallback-non-unique-leaf-names"); + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/fallback-with-suid/Makefile b/dyld/unit-tests/test-cases/fallback-with-suid/Makefile new file mode 100644 index 0000000..dccb78c --- /dev/null +++ b/dyld/unit-tests/test-cases/fallback-with-suid/Makefile @@ -0,0 +1,53 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +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: + ${RUN_AS_USER} ${PASS_IFF_FAILURE} "fallback-with-suid" "fallback-with-suid" $(PWD)/main-suid + +all: main-suid + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +main-suid: main + cp main main-suid + sudo chown root main-suid + sudo chmod 4755 main-suid + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name /bogus/libz.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main-suid libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/fallback-with-suid/foo.c b/dyld/unit-tests/test-cases/fallback-with-suid/foo.c new file mode 100644 index 0000000..4ecb931 --- /dev/null +++ b/dyld/unit-tests/test-cases/fallback-with-suid/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +void compress() {} + diff --git a/dyld/unit-tests/test-cases/fallback-with-suid/main.c b/dyld/unit-tests/test-cases/fallback-with-suid/main.c new file mode 100644 index 0000000..bb79393 --- /dev/null +++ b/dyld/unit-tests/test-cases/fallback-with-suid/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never use $HOME part of fallback-path +// + +extern void compress(); + +int main(int argc, const char *argv[]) +{ + if ( &compress != NULL ) + FAIL("fallback-with-suid root"); + + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/flat-data/Makefile b/dyld/unit-tests/test-cases/flat-data/Makefile new file mode 100644 index 0000000..392ffaa --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-data/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +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 getbar.c + ${CC} ${CCFLAGS} -dynamiclib getbar.c bar.c -o libbar.dylib -flat_namespace + + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib + diff --git a/dyld/unit-tests/test-cases/flat-data/bar.c b/dyld/unit-tests/test-cases/flat-data/bar.c new file mode 100644 index 0000000..a53bfdd --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-data/bar.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bar = 2; diff --git a/dyld/unit-tests/test-cases/flat-data/getbar.c b/dyld/unit-tests/test-cases/flat-data/getbar.c new file mode 100644 index 0000000..020d7ad --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-data/getbar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int bar; + +int* getbar() +{ + return &bar; +} + diff --git a/dyld/unit-tests/test-cases/flat-data/main.c b/dyld/unit-tests/test-cases/flat-data/main.c new file mode 100644 index 0000000..fc85a6e --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-data/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// getbar() is implemented in libbar.dylib which has its own bar +// libbar.dylib is built flat-namespace so it should use the bar from main +// libbar.dylib is built multi_module so that the static linker won't complain +// about two bar's. + +extern int* getbar(); +int bar = 1; + +int main() +{ + if ( getbar() != &bar ) + FAIL("flat-data found wrong bar"); + else + PASS("flat-data"); + + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/flat-insert/Makefile b/dyld/unit-tests/test-cases/flat-insert/Makefile new file mode 100644 index 0000000..9ce9b26 --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-insert/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +### verify inserted libraries override with flat namespace + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libfoo.dylib" && ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -flat_namespace -Wl,-interposable + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/flat-insert/foo.c b/dyld/unit-tests/test-cases/flat-insert/foo.c new file mode 100644 index 0000000..2d68edf --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-insert/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +int foo() +{ + return 42; +} + diff --git a/dyld/unit-tests/test-cases/flat-insert/main.c b/dyld/unit-tests/test-cases/flat-insert/main.c new file mode 100644 index 0000000..14cb600 --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-insert/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + if ( foo() == 42 ) + PASS("flat-insert"); + else + FAIL("flat-insert"); + return EXIT_SUCCESS; +} + +// inserted library has another copy of foo() that should +// override this one and return 42 +int foo() +{ + return 0; +} diff --git a/dyld/unit-tests/test-cases/flat-prebound/Makefile b/dyld/unit-tests/test-cases/flat-prebound/Makefile new file mode 100644 index 0000000..6fe230b --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-prebound/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +libfoo.dylib : foo.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib libbar.dylib -flat_namespace -prebind -seg1addr 20000 + + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -prebind -seg1addr 30000 + + + +clean: + ${RM} ${RMFLAGS} *~ main libbar.dylib libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/flat-prebound/bar.c b/dyld/unit-tests/test-cases/flat-prebound/bar.c new file mode 100644 index 0000000..e12bfce --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-prebound/bar.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +int bar() +{ + return 1; +} + +int barVar = 1; +int barVar2 = 1; diff --git a/dyld/unit-tests/test-cases/flat-prebound/foo.c b/dyld/unit-tests/test-cases/flat-prebound/foo.c new file mode 100644 index 0000000..f47a91a --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-prebound/foo.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int bar(); +extern int barVar; +extern int barVar2; + +int* myBarVar = &barVar; + + +bool foo() +{ + // test non-lazy pointer + if ( barVar2 != 0 ) + return false; + + // test external relocation + if ( *myBarVar != 0 ) + return false; + + // test lazy pointer + if ( bar() != 0 ) + return false; + + return true; +} diff --git a/dyld/unit-tests/test-cases/flat-prebound/main.c b/dyld/unit-tests/test-cases/flat-prebound/main.c new file mode 100644 index 0000000..54862f4 --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-prebound/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// foo() internally calls bar() +// libfoo.dylib is build flat and prebound to libbar.dylib +// but the bar in this main executable should override the prebound bar +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("flat-prebound"); + else + FAIL("flat-prebound found wrong bar"); + + return EXIT_SUCCESS; +} + + +int bar() +{ + return 0; +} +int barVar = 0; +int barVar2 = 0; + diff --git a/dyld/unit-tests/test-cases/flat-private-extern/Makefile b/dyld/unit-tests/test-cases/flat-private-extern/Makefile new file mode 100644 index 0000000..fe7dbc1 --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-private-extern/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoobar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoobar.dylib + + +libfoobar.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoobar.dylib -flat_namespace + + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoobar.dylib + diff --git a/dyld/unit-tests/test-cases/flat-private-extern/bar.c b/dyld/unit-tests/test-cases/flat-private-extern/bar.c new file mode 100644 index 0000000..0b13569 --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-private-extern/bar.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +int bar() __attribute__((visibility("hidden"))); + +int bar() +{ + return 1; +} diff --git a/dyld/unit-tests/test-cases/flat-private-extern/foo.c b/dyld/unit-tests/test-cases/flat-private-extern/foo.c new file mode 100644 index 0000000..ed6fc9b --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-private-extern/foo.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int bar(); + +int foo() +{ + return bar(); +} diff --git a/dyld/unit-tests/test-cases/flat-private-extern/main.c b/dyld/unit-tests/test-cases/flat-private-extern/main.c new file mode 100644 index 0000000..6505066 --- /dev/null +++ b/dyld/unit-tests/test-cases/flat-private-extern/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// foo() internally calls bar() +// libfoobar.dylib is build flat with bar() that is private_extern +// The bar() in the main executable should not override the one in libfoobar + +extern int foo(); + +int main() +{ + if ( foo() == 0 ) + FAIL("flat-private-extern found wrong bar"); + else + PASS("flat-private-extern"); + + return EXIT_SUCCESS; +} + +int bar() +{ + return 0; +} diff --git a/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..569d814 --- /dev/null +++ b/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main 10 + export DYLD_LIBRARY_PATH=${PWD}/hide && ./main 10 + +all: + mkdir -p Foo.framework + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=10 -o "${PWD}/Foo.framework/Foo" + mkdir -p hide + ${CC} ${CCFLAGS} -dynamiclib foo.c -DRESULT=0 -o "${PWD}/hide/Foo" + ${CC} ${CCFLAGS} main.c -framework Foo -F. -I../../include -o main + + +clean: + ${RM} -rf Foo.framework hide main \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c b/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..01c576d --- /dev/null +++ b/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,5 @@ + +int foo() +{ + return RESULT; +} diff --git a/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..7e6502e --- /dev/null +++ b/dyld/unit-tests/test-cases/framework-DYLD_LIBRARY_PATH/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include // for atoi() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + int expectedResult = atoi(argv[1]); + int actualResult = foo(); + //fprintf(stderr, "foo() returned %d, expected %d\n", actualResult, expectedResult); + if ( actualResult != expectedResult ) + FAIL("framework-DYLD_LIBRARY_PATH using wrong dylib. foo() returned %d, expected %d", actualResult, expectedResult); + else + PASS("framework-DYLD_LIBRARY_PATH"); + + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/framework-fallback/Makefile b/dyld/unit-tests/test-cases/framework-fallback/Makefile new file mode 100644 index 0000000..f29fb62 --- /dev/null +++ b/dyld/unit-tests/test-cases/framework-fallback/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations main.c -o main -I${TESTROOT}/include + +clean: + ${RM} ${RMFLAGS} main + + diff --git a/dyld/unit-tests/test-cases/framework-fallback/main.c b/dyld/unit-tests/test-cases/framework-fallback/main.c new file mode 100644 index 0000000..fdaa893 --- /dev/null +++ b/dyld/unit-tests/test-cases/framework-fallback/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + +/// +/// rdar://problem/3736945 +/// +/// Test that a std framework can be dynamically loaded via the fallback paths +/// +/// + + + +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 ) + FAIL("Could not load AppKit"); + else +#endif + PASS("AppKit loaded"); + + return 0; +} + diff --git a/dyld/unit-tests/test-cases/ignore-bad-files/Makefile b/dyld/unit-tests/test-cases/ignore-bad-files/Makefile new file mode 100644 index 0000000..b181007 --- /dev/null +++ b/dyld/unit-tests/test-cases/ignore-bad-files/Makefile @@ -0,0 +1,73 @@ +## +# 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 + +# +# This test the ability of dyld to continue searching if a file was found but not usable. +# Uses DYLD_LIBRARY_PATH to search through many bogus files +# +# + + +all-check: all check + +check: + export DYLD_LIBRARY_PATH=locations/datafile:locations/exec:locations/dir && ${TESTROOT}/bin/exit-non-zero-pass.pl "ignore-bad-files intended failure" "ignore-bad-files intended failure" ./main + export DYLD_LIBRARY_PATH=locations/datafile:locations/exec:locations/dir:locations/real && ${TESTROOT}/bin/exit-zero-pass.pl "ignore-bad-files intended success" "ignore-bad-files intended success" ./main + +all: main locations/real/libfoo.dylib locations/exec/libfoo.dylib locations/datafile/libfoo.dylib locations/dir/libfoo.dylib + +main: main.c locations/real/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c locations/real/libfoo.dylib + + +# real dylib to use +locations/real/libfoo.dylib : foo.c + mkdir -p locations/real + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o locations/real/libfoo.dylib foo.c -install_name libfoo.dylib + +# not a dylib - but a mach-o main executable +locations/exec/libfoo.dylib : main.c + mkdir -p locations/exec + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o locations/exec/libfoo.dylib main.c foo.c + +# some random data file +locations/datafile/libfoo.dylib : main.c + mkdir -p locations/datafile + cat main.c > locations/datafile/libfoo.dylib + +# not a file - but a directory +locations/dir/libfoo.dylib : + mkdir -p locations/dir/libfoo.dylib + +# file with wrong architecture +#locations/wrongarch/libfoo.dylib : +# mkdir -p locations/wrongarch +# ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o locations/wrongarch/libfoo.dylib foo.c -install_name libfoo.dylib -arch ppc64 + + + +clean: + ${RM} ${RMFLAGS} *~ main locations + diff --git a/dyld/unit-tests/test-cases/ignore-bad-files/foo.c b/dyld/unit-tests/test-cases/ignore-bad-files/foo.c new file mode 100644 index 0000000..4315d22 --- /dev/null +++ b/dyld/unit-tests/test-cases/ignore-bad-files/foo.c @@ -0,0 +1,23 @@ +/* + * 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@ + */ +void foo() {} diff --git a/dyld/unit-tests/test-cases/ignore-bad-files/main.c b/dyld/unit-tests/test-cases/ignore-bad-files/main.c new file mode 100644 index 0000000..72d7abe --- /dev/null +++ b/dyld/unit-tests/test-cases/ignore-bad-files/main.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void foo(); + +int +main(int argc, const char* argv[]) +{ + foo(); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-count/Makefile b/dyld/unit-tests/test-cases/image-count/Makefile new file mode 100644 index 0000000..115f7fe --- /dev/null +++ b/dyld/unit-tests/test-cases/image-count/Makefile @@ -0,0 +1,17 @@ + +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 diff --git a/dyld/unit-tests/test-cases/image-count/foo.c b/dyld/unit-tests/test-cases/image-count/foo.c new file mode 100644 index 0000000..c1f5255 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-count/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-count/main.c b/dyld/unit-tests/test-cases/image-count/main.c new file mode 100644 index 0000000..06740b9 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-count/main.c @@ -0,0 +1,83 @@ +/* + * 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 // EXIT_SUCCESS +#include +#include +#include +#include + +#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; +} + + diff --git a/dyld/unit-tests/test-cases/image-remove-crash/Makefile b/dyld/unit-tests/test-cases/image-remove-crash/Makefile new file mode 100644 index 0000000..050a99c --- /dev/null +++ b/dyld/unit-tests/test-cases/image-remove-crash/Makefile @@ -0,0 +1,17 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o foo.bundle foo.c -bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/dyld/unit-tests/test-cases/image-remove-crash/foo.c b/dyld/unit-tests/test-cases/image-remove-crash/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-remove-crash/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-remove-crash/main.c b/dyld/unit-tests/test-cases/image-remove-crash/main.c new file mode 100644 index 0000000..612671f --- /dev/null +++ b/dyld/unit-tests/test-cases/image-remove-crash/main.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static bool sNotified = false; + +static void removeNotification2(const struct mach_header *mh, intptr_t vmaddr_slide) +{ +} + +static void removeNotification(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + // crash in dyld possible if remove callbacks added within a remove callback + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + _dyld_register_func_for_remove_image(removeNotification2); + + sNotified = true; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images go away + _dyld_register_func_for_remove_image(removeNotification); + + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-remove-crash: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-remove-crash: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } + + if ( sNotified ) + PASS("image-remove-crash"); + else + FAIL("image-remove-crash"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-remove-notification/Makefile b/dyld/unit-tests/test-cases/image-remove-notification/Makefile new file mode 100644 index 0000000..0e1b901 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-remove-notification/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.bundle + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o foo.bundle foo.c -bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/dyld/unit-tests/test-cases/image-remove-notification/foo.c b/dyld/unit-tests/test-cases/image-remove-notification/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-remove-notification/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-remove-notification/main.c b/dyld/unit-tests/test-cases/image-remove-notification/main.c new file mode 100644 index 0000000..d7e0b4a --- /dev/null +++ b/dyld/unit-tests/test-cases/image-remove-notification/main.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static void* sExpectedBundleBaseAddress = 0; +static bool sNotified = false; + +static void removeNotification(const struct mach_header *mh, intptr_t vmaddr_slide) +{ + if ( mh == sExpectedBundleBaseAddress ) + sNotified = true; +} + + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images go away + _dyld_register_func_for_remove_image(removeNotification); + + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-remove-notification: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "foo"); + if ( sym == NULL ) { + FAIL("image-remove-notification: dlsym(handle, \"foo\") failed: %s", dlerror()); + exit(0); + } + + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("image-remove-notification: dladdr() failed"); + exit(0); + } + + // record the base address we expect to get notification about + sExpectedBundleBaseAddress = info.dli_fbase; + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-remove-notification: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } + + if ( sNotified ) + PASS("image-remove-notification"); + else + FAIL("image-remove-notification"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-slide/Makefile b/dyld/unit-tests/test-cases/image-slide/Makefile new file mode 100644 index 0000000..9c791ea --- /dev/null +++ b/dyld/unit-tests/test-cases/image-slide/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib diff --git a/dyld/unit-tests/test-cases/image-slide/foo.c b/dyld/unit-tests/test-cases/image-slide/foo.c new file mode 100644 index 0000000..c1f5255 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-slide/foo.c @@ -0,0 +1,2 @@ +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-slide/main.c b/dyld/unit-tests/test-cases/image-slide/main.c new file mode 100644 index 0000000..f4fb318 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-slide/main.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // EXIT_SUCCESS +#include +#include +#include + +#include "test.h" + +extern void foo(); + +intptr_t findSlide(const struct mach_header* mh) +{ + int count = _dyld_image_count(); + for(int i=0; i < count; ++i) { + if ( mh == _dyld_get_image_header(i) ) { + return _dyld_get_image_vmaddr_slide(i); + } + } + return -1; +} + + +int +main() +{ + // find mach_header for libfoo.dylib + const struct mach_header* mh; + Dl_info info; + if ( dladdr(&foo, &info) ) { + mh = info.dli_fbase; + } + else { + FAIL("dladdr() could not find foo()"); + exit(0); + } + + + intptr_t slide1 = _dyld_get_image_slide(mh); + intptr_t slide2 = findSlide(mh); + if ( slide1 == slide2 ) + PASS("image-slide"); + else + FAIL("image-slide: 0x%lX != 0x%lX", slide1, slide2); + + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/image-state-change/Makefile b/dyld/unit-tests/test-cases/image-state-change/Makefile new file mode 100644 index 0000000..d4e91c8 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-change/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main foo.bundle + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib + diff --git a/dyld/unit-tests/test-cases/image-state-change/bar.c b/dyld/unit-tests/test-cases/image-state-change/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-change/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/dyld/unit-tests/test-cases/image-state-change/foo.c b/dyld/unit-tests/test-cases/image-state-change/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-change/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-state-change/main.c b/dyld/unit-tests/test-cases/image-state-change/main.c new file mode 100644 index 0000000..3b636aa --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-change/main.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +static int singleMappedCount = 0; +static int batchMappedCount = 0; +static int singleUnMappedCount = 0; +static int batchBoundCount = 0; +static int singleDepsInitedCount = 0; +static int singleBoundCount = 0; + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) + printf("batchMappedHandler(): %u/%u -> %s\n", i, infoCount, info[i].imageFilePath); + if ( state != dyld_image_state_dependents_mapped ) { + FAIL("image-state-change: batchMappedHandler passed state %d", state); + exit(0); + } + batchMappedCount += infoCount; + return NULL; +} + +static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) + printf("batchBoundHandler(): %u/%u -> %s\n", i, infoCount, info[i].imageFilePath); + if ( state != dyld_image_state_bound ) { + FAIL("image-state-change: batchBoundHandler passed state %d", state); + exit(0); + } + batchBoundCount += infoCount; + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleMappedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_mapped ) { + FAIL("image-state-change: singleMappedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleMappedHandler given %d images", infoCount); + exit(0); + } + ++singleMappedCount; + return NULL; +} + + +static const char* singleBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleBoundHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_bound ) { + FAIL("image-state-change: singleBoundHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleBoundHandler given %d images", infoCount); + exit(0); + } + ++singleBoundCount; + return NULL; +} + + + +static const char* singleDepsInitedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleDepsInitedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_dependents_initialized ) { + FAIL("image-state-change: singleDepsInitedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleDepsInitedHandler given %d images", infoCount); + exit(0); + } + ++singleDepsInitedCount; + return NULL; +} + + + +static const char* singleUnmappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + printf("singleUnmappedHandler(%s)\n", info[0].imageFilePath); + if ( state != dyld_image_state_terminated ) { + FAIL("image-state-change: singleUnmappedHandler passed state %d", state); + exit(0); + } + if ( infoCount != 1 ) { + FAIL("image-state-change: singleUnmappedHandler given %d images", infoCount); + exit(0); + } + ++singleUnMappedCount; + return NULL; +} + +static void loadAndUnLoad() +{ + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-change: dlopen(foo.bundle) failed: %s", dlerror()); + exit(0); + } + + int result = dlclose(handle); + if ( result != 0 ) { + FAIL("image-state-change: dlclose(handle) returned %d, %s", result, dlerror()); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + dyld_register_image_state_change_handler(dyld_image_state_bound, false, singleBoundHandler); + dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, false, singleDepsInitedHandler); + dyld_register_image_state_change_handler(dyld_image_state_terminated, false, singleUnmappedHandler); + + // with batch mode we get notified of existing images, but not with single mode, so re-sync counts + batchMappedCount=0; + + loadAndUnLoad(); + + loadAndUnLoad(); + + if ( (singleMappedCount == batchMappedCount) && (singleMappedCount == singleUnMappedCount) ) + PASS("image-state-change"); + else + FAIL("image-state-change: batch count=%d, single count=%d", batchMappedCount, singleMappedCount); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-state-deny-OFI/Makefile b/dyld/unit-tests/test-cases/image-state-deny-OFI/Makefile new file mode 100644 index 0000000..a89cbf9 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-OFI/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + ./main batch + +all: main foo.bundle bar.bundle foo2.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +foo2.bundle: foo.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo2.bundle foo.c + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle foo2.bundle + diff --git a/dyld/unit-tests/test-cases/image-state-deny-OFI/bar.c b/dyld/unit-tests/test-cases/image-state-deny-OFI/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-OFI/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/dyld/unit-tests/test-cases/image-state-deny-OFI/foo.c b/dyld/unit-tests/test-cases/image-state-deny-OFI/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-OFI/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-state-deny-OFI/main.c b/dyld/unit-tests/test-cases/image-state-deny-OFI/main.c new file mode 100644 index 0000000..37eb8d7 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-OFI/main.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +static bool doneRegistering = false; + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) { + //fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "can't load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + + +static const char* singleInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "singleInitializedHandler(%s)\n", info[0].imageFilePath); + if ( doneRegistering ) { + FAIL("image-state-deny something loaded"); + exit(0); + } + return NULL; +} + +//static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +//{ +// //for (uint32_t i=0; i < infoCount; ++i) +// // fprintf(stderr, "bound: %u %s\n", i, info[i].imageFilePath); +// return NULL; +//} + +static void load(const char* name) +{ + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile(name, &ofi) == NSObjectFileImageSuccess ) { + NSModule mod = NSLinkModule(ofi, name, NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod != NULL ) { + FAIL("neither NSCreateObjectFileImageFromFile nor NSLinkModule failed but should have"); + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed"); + } + exit(0); + } + else { + NSDestroyObjectFileImage(ofi); + } + } +} +#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; + + // tell dyld we want to know when images successfully loaded + //dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("foo2.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed for foo2.bundle"); + exit(0); + } + + load("foo.bundle"); + + load("bar.bundle"); + + if ( !NSDestroyObjectFileImage(ofi) ) { + FAIL("NSDestroyObjectFileImage failed for foo2.bundle"); + exit(0); + } + +// dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY); +#endif + + PASS("image-state-deny"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/Makefile b/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/Makefile new file mode 100644 index 0000000..56dd654 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2011 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 bar.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main bar.bundle + diff --git a/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/bar.c b/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/bar.c new file mode 100644 index 0000000..e425999 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/main.c b/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/main.c new file mode 100644 index 0000000..ed4b56c --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-all_image_infos/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + +static void load(const char* name) +{ + void* handle = dlopen(name, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-all_image_infos: dlopen(%s) should have failed", name); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + const struct dyld_all_image_infos* infos = _dyld_get_all_image_infos(); + const uint32_t initialCount = infos->infoArrayCount; + + load("bar.bundle"); + + if ( infos->infoArrayCount != initialCount ){ + FAIL("image-state-deny-all_image_infos: infoArrayCount should not have changed"); + exit(0); + } + + //for (int i=0; i < infos->infoArrayCount; ++i) + // printf("mh=%p, path=%s\n", infos->infoArray[i].imageLoadAddress, infos->infoArray[i].imageFilePath); + + PASS("image-state-deny-all_image_infos"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-state-deny-cache-leak/Makefile b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/Makefile new file mode 100644 index 0000000..48aff7d --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib -o libfoo1.dylib foo.c -lz + ${CC} ${CCFLAGS} -dynamiclib -o libfoo2.dylib foo.c -weak-lz + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main *.dylib + diff --git a/dyld/unit-tests/test-cases/image-state-deny-cache-leak/bar.c b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/bar.c new file mode 100644 index 0000000..30ade8f --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/bar.c @@ -0,0 +1,2 @@ + +void bar() { } diff --git a/dyld/unit-tests/test-cases/image-state-deny-cache-leak/foo.c b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-state-deny-cache-leak/main.c b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/main.c new file mode 100644 index 0000000..edcb894 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-cache-leak/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "libz" in its name from loading. +// +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "libz") != NULL ) + return "can't load libz"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + void* handle = dlopen("libfoo1.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-cache-leak: dlopen(libfoo.dylib) should have failed"); + return EXIT_SUCCESS; + } + + handle = dlopen("libfoo2.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-deny-cache-leak: dlopen(libfoo.dylib) should have succeeded"); + return EXIT_SUCCESS; + } + dlclose(handle); + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null\n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("image-state-deny-cache-leak"); + else + FAIL("image-state-deny-cache-leak"); + + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-state-deny-dlclose/Makefile b/dyld/unit-tests/test-cases/image-state-deny-dlclose/Makefile new file mode 100644 index 0000000..a67d741 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-dlclose/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c + +libbase.dylib : base.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbase.dylib base.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbase.dylib + diff --git a/dyld/unit-tests/test-cases/image-state-deny-dlclose/base.c b/dyld/unit-tests/test-cases/image-state-deny-dlclose/base.c new file mode 100644 index 0000000..842e2e2 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-dlclose/base.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //for (uint32_t i=0; i < infoCount; ++i) { + // fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + //} + return NULL; +} + +static void myregister() __attribute__((constructor)); + +static void myregister() +{ + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); +} diff --git a/dyld/unit-tests/test-cases/image-state-deny-dlclose/foo.c b/dyld/unit-tests/test-cases/image-state-deny-dlclose/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-dlclose/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-state-deny-dlclose/main.c b/dyld/unit-tests/test-cases/image-state-deny-dlclose/main.c new file mode 100644 index 0000000..e4756bb --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-dlclose/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + // open and close base + void* h1 = dlopen("libbase.dylib", RTLD_LAZY); + if ( h1 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libbase.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h1); + + // if base was unloaded, then this dlopen will jump to neverland + void* h2 = dlopen("libfoo.dylib", RTLD_LAZY); + if ( h2 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libfoo.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + + void* h3 = dlopen("libbase.dylib", RTLD_LAZY); + if ( h3 == NULL ) { + FAIL("image-state-deny-dlclose: can't dlopen libbase.dylib: %s", dlerror()); + return EXIT_SUCCESS; + } + dlclose(h3); + + dlclose(h2); + + + PASS("image-state-deny-dlclose"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-state-deny-leak/Makefile b/dyld/unit-tests/test-cases/image-state-deny-leak/Makefile new file mode 100644 index 0000000..364f994 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-leak/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib -o libbar1.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -o libbar2.dylib bar.c + ${CC} ${CCFLAGS} -dynamiclib -o libfoo.dylib foo.c libbar1.dylib -weak_library libbar2.dylib + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main *.dylib + diff --git a/dyld/unit-tests/test-cases/image-state-deny-leak/bar.c b/dyld/unit-tests/test-cases/image-state-deny-leak/bar.c new file mode 100644 index 0000000..30ade8f --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-leak/bar.c @@ -0,0 +1,2 @@ + +void bar() { } diff --git a/dyld/unit-tests/test-cases/image-state-deny-leak/foo.c b/dyld/unit-tests/test-cases/image-state-deny-leak/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-leak/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-state-deny-leak/main.c b/dyld/unit-tests/test-cases/image-state-deny-leak/main.c new file mode 100644 index 0000000..42e369e --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny-leak/main.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +const char* sBlockName = NULL; + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, sBlockName) != NULL ) + return "can't load blocked name"; + return NULL; +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + sBlockName = "libbar1"; + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny-leak: dlopen(libfoo.dylib) should have failed"); + return EXIT_SUCCESS; + } + + sBlockName = "libbar2"; + handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("image-state-deny-leak: dlopen(libfoo.dylib) should have succeeded"); + return EXIT_SUCCESS; + } + + // execute leaks command on myself + char cmd[512]; + sprintf(cmd, "sudo leaks %u > /dev/null \n", getpid()); + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("image-state-deny-leak"); + else + FAIL("image-state-deny-leak"); + + + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-state-deny/Makefile b/dyld/unit-tests/test-cases/image-state-deny/Makefile new file mode 100644 index 0000000..04973bc --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + ./main batch + +all: main foo.bundle bar.bundle + +main: main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +foo.bundle: foo.c libbar.dylib + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o foo.bundle foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + +bar.bundle: bar.c + ${CC} ${CCFLAGS} -bundle -I${TESTROOT}/include -o bar.bundle bar.c + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle libbar.dylib bar.bundle + diff --git a/dyld/unit-tests/test-cases/image-state-deny/bar.c b/dyld/unit-tests/test-cases/image-state-deny/bar.c new file mode 100644 index 0000000..63c34e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny/bar.c @@ -0,0 +1,2 @@ + +int bar = 10; diff --git a/dyld/unit-tests/test-cases/image-state-deny/foo.c b/dyld/unit-tests/test-cases/image-state-deny/foo.c new file mode 100644 index 0000000..6924ac6 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny/foo.c @@ -0,0 +1,3 @@ + +void foo() {} + diff --git a/dyld/unit-tests/test-cases/image-state-deny/main.c b/dyld/unit-tests/test-cases/image-state-deny/main.c new file mode 100644 index 0000000..df8f1d9 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-deny/main.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool doneRegistering = false; + + +// +// By returning a string, we prevent that image from loading. +// We just prevent any image with "bar" in its name from loading. +// + +static const char* batchMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + for (uint32_t i=0; i < infoCount; ++i) { + //fprintf(stderr, "batch mapped: %d %s\n", i, info[i].imageFilePath); + if ( strstr(info[i].imageFilePath, "bar") != NULL ) + return "can't load bar"; + } + return NULL; +} + +static const char* singleMappedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "single mapped: %s\n", info[0].imageFilePath); + if ( strstr(info[0].imageFilePath, "bar") != NULL ) + return "can't load bar"; + return NULL; +} + + + +static const char* singleInitializedHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + //fprintf(stderr, "singleInitializedHandler(%s)\n", info[0].imageFilePath); + if ( doneRegistering ) { + FAIL("image-state-deny something loaded"); + exit(0); + } + return NULL; +} + +//static const char* batchBoundHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +//{ +// //for (uint32_t i=0; i < infoCount; ++i) +// // fprintf(stderr, "bound: %u %s\n", i, info[i].imageFilePath); +// return NULL; +//} + +static void load(const char* name) +{ + void* handle = dlopen(name, RTLD_LAZY); + if ( handle != NULL ) { + FAIL("image-state-deny: dlopen(%s) should have failed", name); + exit(0); + } +} + + +int main(int argc, const char* argv[]) +{ + // tell dyld we want to know when images successfully loaded + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, singleInitializedHandler); + doneRegistering = true; + + // tell dyld we want to know when images successfully loaded + //dyld_register_image_state_change_handler(dyld_image_state_bound, true, batchBoundHandler); + + // tell dyld we want to know when images are mapped + if ( argc > 1 ) + dyld_register_image_state_change_handler(dyld_image_state_dependents_mapped, true, batchMappedHandler); + else + dyld_register_image_state_change_handler(dyld_image_state_mapped, false, singleMappedHandler); + + load("foo.bundle"); + + load("bar.bundle"); + + //dlopen("/usr/lib/libz.1.2.3.dylib", RTLD_LAZY); + + PASS("image-state-deny"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image-state-dependents-initialized/Makefile b/dyld/unit-tests/test-cases/image-state-dependents-initialized/Makefile new file mode 100644 index 0000000..86f01d8 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-dependents-initialized/Makefile @@ -0,0 +1,45 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c libbar.dylib + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} -dynamiclib -I${TESTROOT}/include -o libbar.dylib bar.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/image-state-dependents-initialized/bar.c b/dyld/unit-tests/test-cases/image-state-dependents-initialized/bar.c new file mode 100644 index 0000000..3b6c323 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-dependents-initialized/bar.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +static enum { start, barDeps, bar, fooDeps, foo, mainDeps, main } step = start; + + +static const char* depInitHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( infoCount != 1 ) { + FAIL("image-state-dependents-initialized: depInitHandler infoCount != 1\n"); + exit(0); + } + + //fprintf(stderr, "depInitHandler(%s), state=%d\n", info[0].imageFilePath, step); + + if ( (step == start) && (strstr(info[0].imageFilePath, "libbar.dylib") != NULL) ) + step = barDeps; + else if ( (step == bar) && (strstr(info[0].imageFilePath, "libfoo.dylib") != NULL) ) + step = fooDeps; + else if ( (step == foo) && (strstr(info[0].imageFilePath, "/main") != NULL) ) + step = mainDeps; + else { + fprintf(stderr, "out of order depInitHandler\n"); + exit(0); + } + + + return NULL; +} + +static const char* initHandler(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info info[]) +{ + if ( infoCount != 1 ) { + FAIL("image-state-dependents-initialized: initHandler infoCount != 1\n"); + exit(0); + } + + //fprintf(stderr, "initHandler(%s), state=%d\n", info[0].imageFilePath, step); + bool isBar = (strstr(info[0].imageFilePath, "libbar.dylib") != NULL); + bool isFoo = (strstr(info[0].imageFilePath, "libfoo.dylib") != NULL); + bool isMain= (strstr(info[0].imageFilePath, "/main") != NULL); + + if ( isBar ) { + if ( step == barDeps ) { + step = bar; + } + else { + fprintf(stderr, "out of order initHandler bar\n"); + exit(0); + } + } + else if ( isFoo ) { + if ( step == fooDeps ) { + step = foo; + } + else { + fprintf(stderr, "out of order initHandler foo\n"); + exit(0); + } + } + else if ( isMain ) { + if ( step == mainDeps ) { + step = main; + } + else { + fprintf(stderr, "out of order initHandler main\n"); + exit(0); + } + } + + + return NULL; +} + + + + +void __attribute__((constructor)) setup_bar() +{ + // tell dyld we want to know when images are mapped + dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, false, depInitHandler); + dyld_register_image_state_change_handler(dyld_image_state_initialized, false, initHandler); +} + + + diff --git a/dyld/unit-tests/test-cases/image-state-dependents-initialized/foo.c b/dyld/unit-tests/test-cases/image-state-dependents-initialized/foo.c new file mode 100644 index 0000000..9584c6e --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-dependents-initialized/foo.c @@ -0,0 +1,11 @@ + + +int foo_value = 1; + +void __attribute__((constructor)) setup_foo() +{ + foo_value = 2; +} + + + diff --git a/dyld/unit-tests/test-cases/image-state-dependents-initialized/main.c b/dyld/unit-tests/test-cases/image-state-dependents-initialized/main.c new file mode 100644 index 0000000..d4933f8 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-state-dependents-initialized/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main(int argc, const char* argv[]) +{ + PASS("image-state-dependents-initialized"); + + return EXIT_SUCCESS; +} + + +int main_value = 1; + +void __attribute__((constructor)) setup_main() +{ + main_value = 2; +} + diff --git a/dyld/unit-tests/test-cases/image-suffix/Makefile b/dyld/unit-tests/test-cases/image-suffix/Makefile new file mode 100644 index 0000000..87e7bc8 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-suffix/Makefile @@ -0,0 +1,157 @@ +## +# 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 + +PWD = $(shell pwd) + + +all-check: all check + +check: check1 check2 check3 check4 check5 check6 check7 check8 + +all: main1 main2 main3 main4 main5 main6 main7 main8 + +main : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main libfoo.dylib + +libfoo.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o $(PWD)/libfoo.dylib + +libfoo_debug.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o libfoo_debug.dylib -install_name $(PWD)/libfoo.dylib + +hide/libfoo.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libfoo.dylib -install_name $(PWD)/libfoo.dylib + +hide/libfoo_debug.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libfoo_debug.dylib -install_name $(PWD)/libfoo.dylib + +# bar_debug has bar_debug as install name +libbar.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o libbar.dylib + +libbar_debug.dylib : foo.c + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -DDEBUG -o libbar_debug.dylib + +hide/libbar.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libbar.dylib -install_name $(PWD)/libbar.dylib + +hide/libbar_debug.dylib : foo.c + mkdir -p $(PWD)/hide + ${CC} -I${TESTROOT}/include foo.c -dynamiclib -o hide/libbar_debug.dylib -install_name $(PWD)/libbar_debug.dylib + +clean: + rm -rf libfoo.dylib libfoo_debug.dylib hide libbar.dylib libbar_debug.dylib main1 main2 main3 main4 main5 main6 main7 main8 + + +# +# check1: main links with libfoo.dylib sets DYLD_IMAGE_SUFFIX=_debug and dynamically loads libfoo.dylib +# (fails on 10.3) +# +main1 : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main1 libfoo.dylib + +check1: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 libfoo.dylib + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main1 $(PWD)/libfoo.dylib + + +# +# check2: main links with libfoo_debug.dylib and dynamically loads libfoo.dylib +# +main2 : main.c libfoo_debug.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main2 libfoo_debug.dylib + +check2: + echo "pwd-1 is ${PWD}" + echo "pwd-2 is $(PWD)" + pwd + ./main2 $(PWD)/libfoo.dylib + + +# +# check3: main links with libfoo.dylib sets DYLD_LIBRARY_PATH=hide and dynamically loads libfoo.dylib +# +main3 : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main3 libfoo.dylib + +check3: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && ./main3 $(PWD)/libfoo.dylib + + +# +# check4: main links with libfoo.dylib sets DYLD_LIBRARY_PATH=hide, DYLD_IMAGE_SUFFIX=_debug and dynamically loads libfoo.dylib +# (fails on 10.3) +# +main4 : main.c libfoo.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main4 libfoo.dylib + +check4: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main4 $(PWD)/libfoo.dylib + + +# +# check5: main links with libbar.dylib sets DYLD_IMAGE_SUFFIX=_debug and dynamically loads libbar.dylib +# (fails on 10.3) +# +main5 : main.c libbar.dylib libbar_debug.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main5 libbar.dylib + +check5: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main5 $(PWD)/libbar.dylib + + +# +# check6: main links with libbar_debug.dylib and dynamically loads libbar.dylib +# (fails on 10.3) +# +main6 : main.c libbar_debug.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main6 libbar_debug.dylib + +check6: + DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main6 $(PWD)/libbar.dylib + + +# +# check7: main links with libbar.dylib sets DYLD_LIBRARY_PATH=hide and dynamically loads libbar.dylib +# +main7 : main.c libbar.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main7 libbar.dylib + +check7: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && ./main7 $(PWD)/libbar.dylib + + +# +# check8: main links with libbar.dylib sets DYLD_LIBRARY_PATH=hide, DYLD_IMAGE_SUFFIX=_debug and dynamically loads libbar.dylib +# (fails on 10.3) +# +main8 : main.c libbar.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main8 libbar.dylib + +check8: + DYLD_LIBRARY_PATH=$(PWD)/hide && export DYLD_LIBRARY_PATH && DYLD_IMAGE_SUFFIX=_debug && export DYLD_IMAGE_SUFFIX && ./main8 $(PWD)/libbar.dylib diff --git a/dyld/unit-tests/test-cases/image-suffix/foo.c b/dyld/unit-tests/test-cases/image-suffix/foo.c new file mode 100644 index 0000000..a267f20 --- /dev/null +++ b/dyld/unit-tests/test-cases/image-suffix/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +void foo() +{ + printf("foo"); +} diff --git a/dyld/unit-tests/test-cases/image-suffix/main.c b/dyld/unit-tests/test-cases/image-suffix/main.c new file mode 100644 index 0000000..204744e --- /dev/null +++ b/dyld/unit-tests/test-cases/image-suffix/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +int main(int argc, const char* argv[]) +{ + if ( argc < 2 ) { + FAIL("too few arguments to main()"); + return EXIT_SUCCESS; + } + + uint32_t imageCount = _dyld_image_count(); + dlopen(argv[1], 0); + if ( imageCount != _dyld_image_count() ) { + FAIL("image count changed"); + return EXIT_SUCCESS; + } + + PASS("images loaded properly"); + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/image_header_containing_address/Makefile b/dyld/unit-tests/test-cases/image_header_containing_address/Makefile new file mode 100644 index 0000000..a9c79c1 --- /dev/null +++ b/dyld/unit-tests/test-cases/image_header_containing_address/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/image_header_containing_address/foo.c b/dyld/unit-tests/test-cases/image_header_containing_address/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/dyld/unit-tests/test-cases/image_header_containing_address/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/dyld/unit-tests/test-cases/image_header_containing_address/main.c b/dyld/unit-tests/test-cases/image_header_containing_address/main.c new file mode 100644 index 0000000..56312cd --- /dev/null +++ b/dyld/unit-tests/test-cases/image_header_containing_address/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); + + +// checks dladdr() and dyld_image_header_containing_address() return same image +static void verify(void* addr) +{ + Dl_info info; + if ( dladdr(addr, &info) == 0 ) { + FAIL("dladdr(%p, xx) failed", addr); + exit(0); + } + const struct mach_header* mh = dyld_image_header_containing_address(addr); + if ( mh != info.dli_fbase ) { + FAIL("dladdr's dli_fbase != dyld_image_header_containing_address()"); + exit(0); + } +} + + +int main() +{ + verify(&main); + verify(&foo); + + int x; + if ( dyld_image_path_containing_address(&x) != NULL ) { + FAIL("dyld_image_header_containing_address() of stack variable not NULL"); + exit(0); + } + + PASS("dyld_image_header_containing_address"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/image_path_containing_address/Makefile b/dyld/unit-tests/test-cases/image_path_containing_address/Makefile new file mode 100644 index 0000000..a9c79c1 --- /dev/null +++ b/dyld/unit-tests/test-cases/image_path_containing_address/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/image_path_containing_address/foo.c b/dyld/unit-tests/test-cases/image_path_containing_address/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/dyld/unit-tests/test-cases/image_path_containing_address/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/dyld/unit-tests/test-cases/image_path_containing_address/main.c b/dyld/unit-tests/test-cases/image_path_containing_address/main.c new file mode 100644 index 0000000..2025b1d --- /dev/null +++ b/dyld/unit-tests/test-cases/image_path_containing_address/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005-2008 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void foo(); + + +// checks dladdr() and dyld_image_path_containing_address() return same path +static void verify(void* addr) +{ + Dl_info info; + if ( dladdr(addr, &info) == 0 ) { + FAIL("dladdr(%p, xx) failed", addr); + exit(0); + } + const char* path = dyld_image_path_containing_address(addr); + if ( path != info.dli_fname ) { + FAIL("dladdr's dli_fname != dyld_image_path_containing_address()"); + exit(0); + } +} + + +int main() +{ + verify(&main); + verify(&foo); + + int x; + if ( dyld_image_path_containing_address(&x) != NULL ) { + FAIL("dyld_image_path_containing_address() of stack variable not NULL"); + exit(0); + } + + PASS("image_path_containing_address"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/init-libSystem-first/Makefile b/dyld/unit-tests/test-cases/init-libSystem-first/Makefile new file mode 100644 index 0000000..67424c7 --- /dev/null +++ b/dyld/unit-tests/test-cases/init-libSystem-first/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo.c -nostdlib -ldylib1.o -flat_namespace -undefined suppress -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/init-libSystem-first/foo.c b/dyld/unit-tests/test-cases/init-libSystem-first/foo.c new file mode 100644 index 0000000..1affc43 --- /dev/null +++ b/dyld/unit-tests/test-cases/init-libSystem-first/foo.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + + +extern char*** _NSGetArgv(void); + +static bool libSystemAlreadyInited = false; + + + +// +static __attribute__((constructor)) void myInit() +{ + libSystemAlreadyInited = ( _NSGetArgv() != NULL ); +} + + + +bool foo() +{ + return libSystemAlreadyInited; +} diff --git a/dyld/unit-tests/test-cases/init-libSystem-first/main.c b/dyld/unit-tests/test-cases/init-libSystem-first/main.c new file mode 100644 index 0000000..28a4db3 --- /dev/null +++ b/dyld/unit-tests/test-cases/init-libSystem-first/main.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("init-libSystem-first"); + else + FAIL("init-libSystem-first"); + + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/init-order/Makefile b/dyld/unit-tests/test-cases/init-order/Makefile new file mode 100644 index 0000000..6525dc6 --- /dev/null +++ b/dyld/unit-tests/test-cases/init-order/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libtest.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libtest.dylib + +libtest.dylib: foo1.c foo2.c base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libtest.dylib foo1.c foo2.c base.c -init _myDashInit + + +clean: + ${RM} ${RMFLAGS} *~ main libtest.dylib + diff --git a/dyld/unit-tests/test-cases/init-order/base.c b/dyld/unit-tests/test-cases/init-order/base.c new file mode 100644 index 0000000..2cdb40a --- /dev/null +++ b/dyld/unit-tests/test-cases/init-order/base.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static int state = 1; +bool badOrder = false; + +void setState(int nextState) +{ + if ( nextState == state ) + ++state; + else + badOrder = true; + +} + + +void baseCheck() +{ + if ( badOrder ) { + switch ( state ) { + case 1: + FAIL("init-order -init not run first"); + break; + case 2: + FAIL("init-order myInit1 not run second"); + break; + case 3: + FAIL("init-order myInit2 not run third"); + break; + case 4: + FAIL("init-order myInit3 not run fourth"); + break; + } + } + else + PASS("init-order"); +} + diff --git a/dyld/unit-tests/test-cases/init-order/base.h b/dyld/unit-tests/test-cases/init-order/base.h new file mode 100644 index 0000000..1dbf697 --- /dev/null +++ b/dyld/unit-tests/test-cases/init-order/base.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +extern void setState(int); +extern void baseCheck(); + diff --git a/dyld/unit-tests/test-cases/init-order/foo1.c b/dyld/unit-tests/test-cases/init-order/foo1.c new file mode 100644 index 0000000..b8808ae --- /dev/null +++ b/dyld/unit-tests/test-cases/init-order/foo1.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + + + +// should run second because first initiallzer in first .o file +static __attribute__((constructor)) void myInit1() +{ + //fprintf(stderr, "myInit1()\n"); + setState(2); +} + + +// should run thrid because second initiallzer in first .o file +static __attribute__((constructor)) void myInit2() +{ + //fprintf(stderr, "myInit2()\n"); + setState(3); +} + + +// should run first becuase -init runs first +void myDashInit() +{ + //fprintf(stderr, "myDashInit()\n"); + setState(1); +} + diff --git a/dyld/unit-tests/test-cases/init-order/foo2.c b/dyld/unit-tests/test-cases/init-order/foo2.c new file mode 100644 index 0000000..646358b --- /dev/null +++ b/dyld/unit-tests/test-cases/init-order/foo2.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void myInit3() +{ + //fprintf(stderr, "myInit3()\n"); + setState(4); +} + diff --git a/dyld/unit-tests/test-cases/init-order/foo3.c b/dyld/unit-tests/test-cases/init-order/foo3.c new file mode 100644 index 0000000..c92a444 --- /dev/null +++ b/dyld/unit-tests/test-cases/init-order/foo3.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + +int __attribute__((weak)) coal1 = 3; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo3", &coal1); + baseVerifyCoal2("in foo3", &coal2); +} + diff --git a/dyld/unit-tests/test-cases/init-order/main.c b/dyld/unit-tests/test-cases/init-order/main.c new file mode 100644 index 0000000..b210384 --- /dev/null +++ b/dyld/unit-tests/test-cases/init-order/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/initializer-args/Makefile b/dyld/unit-tests/test-cases/initializer-args/Makefile new file mode 100644 index 0000000..3f4e4d0 --- /dev/null +++ b/dyld/unit-tests/test-cases/initializer-args/Makefile @@ -0,0 +1,36 @@ +## +# 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 arg1 arg2 + +all: + ${CC} ${CCFLAGS} -w -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/initializer-args/main.c b/dyld/unit-tests/test-cases/initializer-args/main.c new file mode 100644 index 0000000..79e43e0 --- /dev/null +++ b/dyld/unit-tests/test-cases/initializer-args/main.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static int my_argc = 0; +static char **my_argv = NULL; +static char **my_envp = NULL; +static char **my_appl = NULL; + +void +__attribute__((constructor)) +my_init(int argc, char **argv, char **envp, char **appl) +{ + my_argc = argc; + my_argv = argv; + my_envp = envp; + my_appl = appl; +} + +int +main(int argc, char **argv, char **envp, char**appl) +{ + if(argc == my_argc + && argv == my_argv + && envp == my_envp + && appl == my_appl) + { + PASS("initializer/constructor was called with arguments"); + } + else + { + FAIL("initializer/constructor was not called with arguments"); + } + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/initializer-bounds-check/Makefile b/dyld/unit-tests/test-cases/initializer-bounds-check/Makefile new file mode 100644 index 0000000..42f9e42 --- /dev/null +++ b/dyld/unit-tests/test-cases/initializer-bounds-check/Makefile @@ -0,0 +1,38 @@ +## +# 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: + ${PASS_IFF_FAILURE} ./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 + diff --git a/dyld/unit-tests/test-cases/initializer-bounds-check/bar.c b/dyld/unit-tests/test-cases/initializer-bounds-check/bar.c new file mode 100644 index 0000000..12846f3 --- /dev/null +++ b/dyld/unit-tests/test-cases/initializer-bounds-check/bar.c @@ -0,0 +1,7 @@ + +int bar = 0; + +void altSecondInit() +{ + bar = 1; +} diff --git a/dyld/unit-tests/test-cases/initializer-bounds-check/foo1.c b/dyld/unit-tests/test-cases/initializer-bounds-check/foo1.c new file mode 100644 index 0000000..5333718 --- /dev/null +++ b/dyld/unit-tests/test-cases/initializer-bounds-check/foo1.c @@ -0,0 +1,14 @@ +#include + +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; +} + diff --git a/dyld/unit-tests/test-cases/initializer-bounds-check/foo2.c b/dyld/unit-tests/test-cases/initializer-bounds-check/foo2.c new file mode 100644 index 0000000..ea8cf54 --- /dev/null +++ b/dyld/unit-tests/test-cases/initializer-bounds-check/foo2.c @@ -0,0 +1,12 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +__attribute__((constructor)) +void secondInit() +{ + FAIL("initializer-bounds-check, second init called"); + exit(0); +} + diff --git a/dyld/unit-tests/test-cases/initializer-bounds-check/main.c b/dyld/unit-tests/test-cases/initializer-bounds-check/main.c new file mode 100644 index 0000000..bd4a512 --- /dev/null +++ b/dyld/unit-tests/test-cases/initializer-bounds-check/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int bar; + +int +main() +{ + if ( bar == 1 ) + FAIL("initializer-bounds-check, out of bounds initializer called"); + else + PASS("initializer-bounds-check"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile b/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile new file mode 100644 index 0000000..be551ef --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# verifies that weak symbols in inserted libraries work +# + + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES=libinsert.dylib && ./main + +all: main libinsert.dylib + + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libinsert.dylib: insert.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libinsert.dylib insert.c + + +clean: + ${RM} ${RMFLAGS} *~ main lib*.dylib + diff --git a/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c b/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c new file mode 100644 index 0000000..9bfcca7 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/insert.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +void __attribute__((weak)) foo() +{ + +} + + +static __attribute__((constructor)) void init() +{ + // have intializer call weak function + foo(); +} + diff --git a/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/main.c b/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/main.c new file mode 100644 index 0000000..8593697 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-weak-symbols/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +// since both main and insert.dylib have a weak _foo, +// dyld will coalesce. +void __attribute__((weak)) foo() +{ + +} + + +int main() +{ + PASS("insert-libraries-weak-symbols"); + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-initializer/Makefile b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/Makefile new file mode 100644 index 0000000..fc9269f --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/Makefile @@ -0,0 +1,59 @@ +## +# 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 + +# +# verifies that initializers for inserted libraries run before the +# main executables initializers +# + + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES=libinsert.dylib && ./main + +all: main libinsert.dylib + + +main: main.c libfoo1.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib + + +libfoo1.dylib: foo1.c libbase.dylib libfoo2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo1.dylib foo1.c libbase.dylib libfoo2.dylib + +libfoo2.dylib: foo2.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo2.dylib foo2.c libbase.dylib + +libinsert.dylib: insert.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libinsert.dylib insert.c libbase.dylib + +libbase.dylib: base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbase.dylib base.c + + +clean: + ${RM} ${RMFLAGS} *~ main lib*.dylib + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-initializer/base.c b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/base.c new file mode 100644 index 0000000..fcd63c6 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/base.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static int state = 1; +bool badOrder = false; + +void setState(int nextState) +{ + if ( nextState == state ) + ++state; + else + badOrder = true; + +} + + +void baseCheck() +{ + if ( badOrder ) + FAIL("insert-libraries-with-initializers bad state at %d", state); + else + PASS("insert-libraries-with-initializers"); +} + + + + + + +static __attribute__((constructor)) void base_init() +{ + //fprintf(stderr, "base_init()\n"); + setState(1); +} + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-initializer/base.h b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/base.h new file mode 100644 index 0000000..1dbf697 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/base.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +extern void setState(int); +extern void baseCheck(); + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c new file mode 100644 index 0000000..ac55290 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/foo1.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void foo1_init() +{ + //fprintf(stderr, "foo1_init()\n"); + setState(4); +} + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c new file mode 100644 index 0000000..6574e02 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/foo2.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + +// should run fourth because first initiallzer in second .o file +static __attribute__((constructor)) void foo2_init() +{ + //fprintf(stderr, "foo2_init()\n"); + setState(3); +} + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-initializer/insert.c b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/insert.c new file mode 100644 index 0000000..79d78a2 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/insert.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + + + +static __attribute__((constructor)) void insert() +{ + //fprintf(stderr, "insert()\n"); + setState(2); +} + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-initializer/main.c b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/main.c new file mode 100644 index 0000000..64df397 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-initializer/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + baseCheck(); + return EXIT_SUCCESS; +} + + +static __attribute__((constructor)) void main_init() +{ + //fprintf(stderr, "main_init()\n"); + setState(5); +} + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-suid/Makefile b/dyld/unit-tests/test-cases/insert-libraries-with-suid/Makefile new file mode 100644 index 0000000..c06cad5 --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-suid/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2005-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +PWD = `pwd` + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +all-check: all check + +check: + ${RUN_AS_USER} $(PWD)/main-with-env insert-libraries-with-suid 2>/dev/null + +all: main + +main: main.c + ${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 main-with-env + diff --git a/dyld/unit-tests/test-cases/insert-libraries-with-suid/main.c b/dyld/unit-tests/test-cases/insert-libraries-with-suid/main.c new file mode 100644 index 0000000..c9d015c --- /dev/null +++ b/dyld/unit-tests/test-cases/insert-libraries-with-suid/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never use DYLD_INSERT_LIBRARIES +// That environment variable is cleared by dyld (its right-hand-side is set to empty) +// + +int main(int argc, const char *argv[]) +{ + const char* rhs = getenv("DYLD_INSERT_LIBRARIES"); + if ( (rhs != NULL) && (rhs[0] != '\0') ) + FAIL("insert-libraries-with-suid DYLD_INSERT_LIBRARIES not cleared"); + else + PASS("insert-libraries-with-suid"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-basic-prebound/Makefile b/dyld/unit-tests/test-cases/interpose-basic-prebound/Makefile new file mode 100644 index 0000000..c98a29b --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic-prebound/Makefile @@ -0,0 +1,46 @@ +## +# 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: + export DYLD_INSERT_LIBRARIES="libmystrdup.dylib" && ./main + +all: main libmystrdup.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + export MACOSX_DEPLOYMENT_TARGET=10.3 && ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -prebind -seg1addr 20000 + +libmystrdup.dylib : mystrdup.c + ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libfoo.dylib + \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/interpose-basic-prebound/foo.c b/dyld/unit-tests/test-cases/interpose-basic-prebound/foo.c new file mode 100644 index 0000000..3b5f1ff --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic-prebound/foo.c @@ -0,0 +1,12 @@ + +#include +#include + +bool check_dylib_interposing() +{ + const char* x = strdup("123"); + const char* y = strdup("456"); + + return ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) ); +} + diff --git a/dyld/unit-tests/test-cases/interpose-basic-prebound/main.c b/dyld/unit-tests/test-cases/interpose-basic-prebound/main.c new file mode 100644 index 0000000..ba5a95c --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic-prebound/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool check_dylib_interposing(); + +int main() +{ + if ( check_dylib_interposing() ) + PASS("interpose-basic"); + else + FAIL("interpose-basic"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c b/dyld/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c new file mode 100644 index 0000000..18041ac --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic-prebound/mystrdup.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +char* mystrdup(const char* in) +{ + return strdup("hello"); +} + +DYLD_INTERPOSE(mystrdup, strdup) diff --git a/dyld/unit-tests/test-cases/interpose-basic/Makefile b/dyld/unit-tests/test-cases/interpose-basic/Makefile new file mode 100644 index 0000000..1ea5385 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmystrdup.dylib" && ./main + +all: main libmystrdup.dylib + +main : main.c libwrap.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main libwrap.dylib main.c + +libwrap.dylib: wrap.c + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o libwrap.dylib + +libmystrdup.dylib : mystrdup.c + ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libwrap.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-basic/main.c b/dyld/unit-tests/test-cases/interpose-basic/main.c new file mode 100644 index 0000000..ebcd741 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern char* wrap_strdup(const char*); + +int main() +{ + const char* x = strdup("123"); + const char* y = wrap_strdup("456"); + + if ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) ) + PASS("interpose-basic"); + else + FAIL("interpose-basic"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-basic/mystrdup.c b/dyld/unit-tests/test-cases/interpose-basic/mystrdup.c new file mode 100644 index 0000000..18041ac --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic/mystrdup.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +char* mystrdup(const char* in) +{ + return strdup("hello"); +} + +DYLD_INTERPOSE(mystrdup, strdup) diff --git a/dyld/unit-tests/test-cases/interpose-basic/wrap.c b/dyld/unit-tests/test-cases/interpose-basic/wrap.c new file mode 100644 index 0000000..f634ab2 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-basic/wrap.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +// make a pointer statically initiallized to strdup() +// since libwrap.dylib is staticlly linked to main +// this verfies that interposing happens properly +static char* (*proc)(const char*) = strdup; + +char* wrap_strdup(const char* str) +{ + return proc(str); +} + diff --git a/dyld/unit-tests/test-cases/interpose-chained/Makefile b/dyld/unit-tests/test-cases/interpose-chained/Makefile new file mode 100644 index 0000000..2920a5c --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-chained/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +# + +# +# This unit test verifies that multiple interposing libraries can all +# interpose the same function and the result is that they chain together. +# That is, each one calls through to the next. +# +# On Tiger (10.4.0), this test fails with infinite recursion. +# +# The function foo() does string appends. This allows us to check: +# 1) every interposer was called, and 2) they were called in the +# correct order. +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libfoo1.dylib:libfoo2.dylib:libfoo3.dylib" && ./main + +all: main libfoo1.dylib libfoo2.dylib libfoo3.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + +libfoo1.dylib : foo1.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo1.c libfoo.dylib -o libfoo1.dylib + +libfoo2.dylib : foo2.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo2.c libfoo.dylib -o libfoo2.dylib + +libfoo3.dylib : foo3.c libfoo.dylib + ${CC} ${CCFLAGS} -dynamiclib foo3.c libfoo.dylib -o libfoo3.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo1.dylib libfoo2.dylib libfoo3.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-chained/foo.c b/dyld/unit-tests/test-cases/interpose-chained/foo.c new file mode 100644 index 0000000..681bc29 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-chained/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "foo.h" + +const char* foo(const char* str) +{ + char* result; + asprintf(&result, "foo(%s)", str); + return result; +} + diff --git a/dyld/unit-tests/test-cases/interpose-chained/foo.h b/dyld/unit-tests/test-cases/interpose-chained/foo.h new file mode 100644 index 0000000..2bb7c94 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-chained/foo.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern const char* foo(const char* str); diff --git a/dyld/unit-tests/test-cases/interpose-chained/foo1.c b/dyld/unit-tests/test-cases/interpose-chained/foo1.c new file mode 100644 index 0000000..634cc3b --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-chained/foo1.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include "foo.h" + +const char* foo1(const char* str) +{ + char* result; + asprintf(&result, "foo1(%s)", foo(str)); + return result; +} + +DYLD_INTERPOSE(foo1, foo) diff --git a/dyld/unit-tests/test-cases/interpose-chained/foo2.c b/dyld/unit-tests/test-cases/interpose-chained/foo2.c new file mode 100644 index 0000000..fb21948 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-chained/foo2.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include "foo.h" + +const char* foo2(const char* str) +{ + char* result; + asprintf(&result, "foo2(%s)", foo(str)); + return result; +} + +DYLD_INTERPOSE(foo2, foo) diff --git a/dyld/unit-tests/test-cases/interpose-chained/foo3.c b/dyld/unit-tests/test-cases/interpose-chained/foo3.c new file mode 100644 index 0000000..b85392f --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-chained/foo3.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include "foo.h" + +const char* foo3(const char* str) +{ + char* result; + asprintf(&result, "foo3(%s)", foo(str)); + return result; +} + +DYLD_INTERPOSE(foo3, foo) diff --git a/dyld/unit-tests/test-cases/interpose-chained/main.c b/dyld/unit-tests/test-cases/interpose-chained/main.c new file mode 100644 index 0000000..5209b67 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-chained/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() +#include "foo.h" + +int main() +{ + const char* x = foo("seed"); + + if ( strcmp(x, "foo3(foo2(foo1(foo(seed))))") == 0 ) + PASS("interpose-chained"); + else + FAIL("interpose-chained %s", x); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-dlsym/Makefile b/dyld/unit-tests/test-cases/interpose-dlsym/Makefile new file mode 100644 index 0000000..33e6b2a --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dlsym/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmyfree.dylib" && ./main + +all: main libmyfree.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libmyfree.dylib : myfree.c + ${CC} ${CCFLAGS} -dynamiclib myfree.c -o libmyfree.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmyfree.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-dlsym/main.c b/dyld/unit-tests/test-cases/interpose-dlsym/main.c new file mode 100644 index 0000000..6857710 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dlsym/main.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main() +{ + void* handle = dlopen("/usr/lib/libSystem.B.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("interpose-dlsym: dlopen() error: %s", dlerror()); + exit(0); + } + void* dlsym_free = dlsym(handle, "free"); + if ( dlsym_free == NULL ) { + FAIL("interpose-dlsym: dlsym() error: %s", dlerror()); + exit(0); + } + + if ( dlsym_free == &free ) + PASS("interpose-dlsym"); + else + FAIL("interpose-dlsym: %p != %p", dlsym_free, &free); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-dlsym/myfree.c b/dyld/unit-tests/test-cases/interpose-dlsym/myfree.c new file mode 100644 index 0000000..f856475 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dlsym/myfree.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +void myfree(void* p) +{ + free(p); +} + +DYLD_INTERPOSE(myfree, free) diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-basic/Makefile b/dyld/unit-tests/test-cases/interpose-dynamic-basic/Makefile new file mode 100644 index 0000000..b1cf89d --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-basic/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-basic/main.c b/dyld/unit-tests/test-cases/interpose-dynamic-basic/main.c new file mode 100644 index 0000000..b302512 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-basic/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +extern const struct mach_header __dso_handle; + + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef char* (*DupProc)(const char*); + +char* mystrdup(const char* in) +{ + return "hello"; +} + +static const struct dyld_interpose_tuple sTable[] = { {&mystrdup, &strdup} }; + +int main() +{ + const char* preCall = strdup("123"); + DupProc preProc = &strdup; + + dyld_dynamic_interpose(&__dso_handle, sTable, 1); + + const char* postCall = strdup("123"); + DupProc postProc = &strdup; + + if ( strcmp(preCall, "123") != 0 ) { + FAIL("interpose-dynamic-basic control strdup failed"); + return EXIT_SUCCESS; + } + + if ( strcmp(postCall, "hello") != 0 ) { + FAIL("interpose-dynamic-basic interposed lazy strdup failed"); + return EXIT_SUCCESS; + } + + if ( preProc == postProc ) { + FAIL("interpose-dynamic-basic interposed non-lazy strdup failed"); + return EXIT_SUCCESS; + } + + PASS("interpose-dynamic-basic"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile b/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile new file mode 100644 index 0000000..410d9ae --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c b/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c new file mode 100644 index 0000000..5bfa892 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/foo.c @@ -0,0 +1,26 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + + +int foo() { return 0; } + +int alt_foo() { return 10; } + + + + +static const struct dyld_interpose_tuple sTable[] = { {&alt_foo, &foo} }; + + +__attribute__((constructor)) +void init() +{ + // switch main executable to use alt_foo() when it calls foo() + dyld_dynamic_interpose((const struct mach_header*)_NSGetMachExecuteHeader(), sTable, 1); + +} + diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/main.c b/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/main.c new file mode 100644 index 0000000..da21a5c --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-dlsym/main.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*FooProc)(); + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("interpose-dynamic-dlsym: dlopen() error: %s", dlerror()); + return 0; + } + FooProc dlsym_foo = (FooProc)dlsym(handle, "foo"); + if ( dlsym_foo == NULL ) { + FAIL("interpose-dynamic-dlsym: dlsym() error: %s", dlerror()); + return 0; + } + + int result = (*dlsym_foo)(); + if ( result == 10 ) + PASS("interpose-dynamic-dlsym"); + else + FAIL("interpose-dynamic-dlsym"); + + return 0; +} diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-lazy/Makefile b/dyld/unit-tests/test-cases/interpose-dynamic-lazy/Makefile new file mode 100644 index 0000000..61586cc --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-lazy/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2014 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-lazy/foo.c b/dyld/unit-tests/test-cases/interpose-dynamic-lazy/foo.c new file mode 100644 index 0000000..49b5897 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-lazy/foo.c @@ -0,0 +1,27 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include + + + +int foo() { return 0; } + +int alt_foo() { return 10; } + + + + +static const struct dyld_interpose_tuple sTable[] = { {&alt_foo, &foo} }; + + +__attribute__((constructor)) +void init() +{ + + dyld_dynamic_interpose((const struct mach_header*)_NSGetMachExecuteHeader(), sTable, 1); + +} + diff --git a/dyld/unit-tests/test-cases/interpose-dynamic-lazy/main.c b/dyld/unit-tests/test-cases/interpose-dynamic-lazy/main.c new file mode 100644 index 0000000..89e9ad9 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-dynamic-lazy/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int foo(); + +int main() +{ + int result = foo(); + if ( result == 10 ) + PASS("interpose-dynamic-lazy"); + else + FAIL("interpose-dynamic-lazy"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-multiple/Makefile b/dyld/unit-tests/test-cases/interpose-multiple/Makefile new file mode 100644 index 0000000..99041ca --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-multiple/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +# + +# +# This unit test verifies that multiple interposing libraries can all +# interpose the same function and the result is that they chain together. +# That is, each one calls through to the next. +# +# On Tiger (10.4.0), this test fails with infinite recursion. +# +# The function foo() does string appends. This allows us to check: +# 1) every interposer was called, and 2) they were called in the +# correct order. +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libfoo1.dylib:libfoo2.dylib" && ./main + export DYLD_INSERT_LIBRARIES="libfoo2.dylib:libfoo1.dylib" && ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib base.c -o libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libbase.dylib -o main + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo1.c libbase.dylib -o libfoo1.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib foo2.c libbase.dylib -o libfoo2.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libbase.dylib libfoo1.dylib libfoo2.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-multiple/base.c b/dyld/unit-tests/test-cases/interpose-multiple/base.c new file mode 100644 index 0000000..126f99e --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-multiple/base.c @@ -0,0 +1,6 @@ +#include +#include "base.h" + +int base1() { return 1; } +int base2() { return 2; } + diff --git a/dyld/unit-tests/test-cases/interpose-multiple/base.h b/dyld/unit-tests/test-cases/interpose-multiple/base.h new file mode 100644 index 0000000..658b8a1 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-multiple/base.h @@ -0,0 +1,3 @@ + +extern int base1(); +extern int base2(); diff --git a/dyld/unit-tests/test-cases/interpose-multiple/foo1.c b/dyld/unit-tests/test-cases/interpose-multiple/foo1.c new file mode 100644 index 0000000..0670559 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-multiple/foo1.c @@ -0,0 +1,26 @@ +#include +#include +#include +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int (*p2)() = &base2; + +__attribute__((constructor)) +void myinit() +{ + if ( (*p2)() == 20 ) + PASS("interpose-multiple"); + else + FAIL("interpose-multiple"); +} + + +int mybase1() +{ + return 10; +} + + +DYLD_INTERPOSE(mybase1, base1) diff --git a/dyld/unit-tests/test-cases/interpose-multiple/foo2.c b/dyld/unit-tests/test-cases/interpose-multiple/foo2.c new file mode 100644 index 0000000..1f2f362 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-multiple/foo2.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int (*p1)() = &base1; + +__attribute__((constructor)) +void myinit() +{ + if ( (*p1)() == 10 ) + PASS("interpose-multiple"); + else + FAIL("interpose-multiple"); +} + + +int mybase2() +{ + return 20; +} + + +DYLD_INTERPOSE(mybase2, base2) diff --git a/dyld/unit-tests/test-cases/interpose-multiple/main.c b/dyld/unit-tests/test-cases/interpose-multiple/main.c new file mode 100644 index 0000000..e0bd1d1 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-multiple/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() +#include "base.h" + +int main() +{ + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-not-inserted/Makefile b/dyld/unit-tests/test-cases/interpose-not-inserted/Makefile new file mode 100644 index 0000000..cccf28c --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-not-inserted/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libmystrdup.dylib + +main : main.c wrap.c mystrdup.c + ${CC} ${CCFLAGS} -dynamiclib mystrdup.c -o libmystrdup.dylib + ${CC} ${CCFLAGS} -dynamiclib wrap.c -o libwrap.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main libwrap.dylib libmystrdup.dylib main.c + + + +clean: + ${RM} ${RMFLAGS} *~ main libmystrdup.dylib libwrap.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-not-inserted/main.c b/dyld/unit-tests/test-cases/interpose-not-inserted/main.c new file mode 100644 index 0000000..c71ffb6 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-not-inserted/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern char* wrap_strdup(const char*); + +int main() +{ + const char* x = strdup("123"); + const char* y = wrap_strdup("456"); + + if ( (strcmp(x, "hello") == 0) && (strcmp(y, "hello") == 0) ) + PASS("interpose-not-inserted"); + else + FAIL("interpose-not-inserted"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-not-inserted/mystrdup.c b/dyld/unit-tests/test-cases/interpose-not-inserted/mystrdup.c new file mode 100644 index 0000000..18041ac --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-not-inserted/mystrdup.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include + +char* mystrdup(const char* in) +{ + return strdup("hello"); +} + +DYLD_INTERPOSE(mystrdup, strdup) diff --git a/dyld/unit-tests/test-cases/interpose-not-inserted/wrap.c b/dyld/unit-tests/test-cases/interpose-not-inserted/wrap.c new file mode 100644 index 0000000..f634ab2 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-not-inserted/wrap.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +// make a pointer statically initiallized to strdup() +// since libwrap.dylib is staticlly linked to main +// this verfies that interposing happens properly +static char* (*proc)(const char*) = strdup; + +char* wrap_strdup(const char* str) +{ + return proc(str); +} + diff --git a/dyld/unit-tests/test-cases/interpose-shared-cache/Makefile b/dyld/unit-tests/test-cases/interpose-shared-cache/Makefile new file mode 100644 index 0000000..c1cb7dd --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-shared-cache/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmymalloc.dylib" && ./main + +all: main libmymalloc.dylib + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +libmymalloc.dylib : mymalloc.c + ${CC} ${CCFLAGS} -dynamiclib mymalloc.c -o libmymalloc.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmymalloc.dylib + diff --git a/dyld/unit-tests/test-cases/interpose-shared-cache/main.c b/dyld/unit-tests/test-cases/interpose-shared-cache/main.c new file mode 100644 index 0000000..93ef898 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-shared-cache/main.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool allocationSeen(void* p); +typedef bool (*seenProc)(void*); + +int main() +{ + void* x = strdup("123"); + + seenProc seen = (seenProc)dlsym(RTLD_DEFAULT, "allocationSeen"); + + if ( (seen != NULL) && (*seen)(x) ) + PASS("interpose-basic-shared-cache"); + else + FAIL("interpose-basic-shared-cache"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/interpose-shared-cache/mymalloc.c b/dyld/unit-tests/test-cases/interpose-shared-cache/mymalloc.c new file mode 100644 index 0000000..bca0938 --- /dev/null +++ b/dyld/unit-tests/test-cases/interpose-shared-cache/mymalloc.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2005-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +static void* seenAllocations[128]; +static int seenAllocationIndex = 0; + +// record each malloc result in a ring buffer +void* my_malloc(size_t size) +{ + char* x = malloc(size); + seenAllocations[seenAllocationIndex++] = x; + seenAllocationIndex = (seenAllocationIndex & 127); //wrap around + return x; +} + +DYLD_INTERPOSE(my_malloc, malloc) + +bool allocationSeen(void* p) +{ + for(int i=0; i < 127; ++i) { + if ( seenAllocations[i] == p ) + return true; + } + return false; +} + + + diff --git a/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile new file mode 100644 index 0000000..e21b784 --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +### +### Test that when i386 "fast stubs" are updated early to +### avoid the cache line crossing race conditition, that +### dynamically looked up functions around bound lazily +### + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c -undefined dynamic_lookup + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/foo.c b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/foo.c new file mode 100644 index 0000000..35c9bb7 --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/foo.c @@ -0,0 +1,827 @@ +#include "foo.h" + +void foo000(int p) {} +void foo001(int p) {} +void foo002(int p) {} +void foo003(int p) {} +void foo004(int p) {} +void foo005(int p) {} +void foo006(int p) {} +void foo007(int p) {} +void foo008(int p) {} +void foo009(int p) {} +void foo010(int p) {} +void foo011(int p) {} +void foo012(int p) {} +void foo013(int p) {} +void foo014(int p) {} +void foo015(int p) {} +void foo016(int p) {} +void foo017(int p) {} +void foo018(int p) {} +void foo019(int p) {} +void foo020(int p) {} +void foo021(int p) {} +void foo022(int p) {} +void foo023(int p) {} +void foo024(int p) {} +void foo025(int p) {} +void foo026(int p) {} +void foo027(int p) {} +void foo028(int p) {} +void foo029(int p) {} +void foo030(int p) {} +void foo031(int p) {} +void foo032(int p) {} +void foo033(int p) {} +void foo034(int p) {} +void foo035(int p) {} +void foo036(int p) {} +void foo037(int p) {} +void foo038(int p) {} +void foo039(int p) {} +void foo040(int p) {} +void foo041(int p) {} +void foo042(int p) {} +void foo043(int p) {} +void foo044(int p) {} +void foo045(int p) {} +void foo046(int p) {} +void foo047(int p) {} +void foo048(int p) {} +void foo049(int p) {} +void foo050(int p) {} +void foo051(int p) {} +void foo052(int p) {} +void foo053(int p) {} +void foo054(int p) {} +void foo055(int p) {} +void foo056(int p) {} +void foo057(int p) {} +void foo058(int p) {} +void foo059(int p) {} +void foo060(int p) {} +void foo061(int p) {} +void foo062(int p) {} +void foo063(int p) {} +void foo064(int p) {} +void foo065(int p) {} +void foo066(int p) {} +void foo067(int p) {} +void foo068(int p) {} +void foo069(int p) {} +void foo070(int p) {} +void foo071(int p) {} +void foo072(int p) {} +void foo073(int p) {} +void foo074(int p) {} +void foo075(int p) {} +void foo076(int p) {} +void foo077(int p) {} +void foo078(int p) {} +void foo079(int p) {} +void foo080(int p) {} +void foo081(int p) {} +void foo082(int p) {} +void foo083(int p) {} +void foo084(int p) {} +void foo085(int p) {} +void foo086(int p) {} +void foo087(int p) {} +void foo088(int p) {} +void foo089(int p) {} +void foo090(int p) {} +void foo091(int p) {} +void foo092(int p) {} +void foo093(int p) {} +void foo094(int p) {} +void foo095(int p) {} +void foo096(int p) {} +void foo097(int p) {} +void foo098(int p) {} +void foo099(int p) {} +void foo100(int p) {} +void foo101(int p) {} +void foo102(int p) {} +void foo103(int p) {} +void foo104(int p) {} +void foo105(int p) {} +void foo106(int p) {} +void foo107(int p) {} +void foo108(int p) {} +void foo109(int p) {} +void foo110(int p) {} +void foo111(int p) {} +void foo112(int p) {} +void foo113(int p) {} +void foo114(int p) {} +void foo115(int p) {} +void foo116(int p) {} +void foo117(int p) {} +void foo118(int p) {} +void foo119(int p) {} +void foo120(int p) {} +void foo121(int p) {} +void foo122(int p) {} +void foo123(int p) {} +void foo124(int p) {} +void foo125(int p) {} +void foo126(int p) {} +void foo127(int p) {} +void foo128(int p) {} +void foo129(int p) {} +void foo130(int p) {} +void foo131(int p) {} +void foo132(int p) {} +void foo133(int p) {} +void foo134(int p) {} +void foo135(int p) {} +void foo136(int p) {} +void foo137(int p) {} +void foo138(int p) {} +void foo139(int p) {} +void foo140(int p) {} +void foo141(int p) {} +void foo142(int p) {} +void foo143(int p) {} +void foo144(int p) {} +void foo145(int p) {} +void foo146(int p) {} +void foo147(int p) {} +void foo148(int p) {} +void foo149(int p) {} +void foo150(int p) {} +void foo151(int p) {} +void foo152(int p) {} +void foo153(int p) {} +void foo154(int p) {} +void foo155(int p) {} +void foo156(int p) {} +void foo157(int p) {} +void foo158(int p) {} +void foo159(int p) {} +void foo160(int p) {} +void foo161(int p) {} +void foo162(int p) {} +void foo163(int p) {} +void foo164(int p) {} +void foo165(int p) {} +void foo166(int p) {} +void foo167(int p) {} +void foo168(int p) {} +void foo169(int p) {} +void foo170(int p) {} +void foo171(int p) {} +void foo172(int p) {} +void foo173(int p) {} +void foo174(int p) {} +void foo175(int p) {} +void foo176(int p) {} +void foo177(int p) {} +void foo178(int p) {} +void foo179(int p) {} +void foo180(int p) {} +void foo181(int p) {} +void foo182(int p) {} +void foo183(int p) {} +void foo184(int p) {} +void foo185(int p) {} +void foo186(int p) {} +void foo187(int p) {} +void foo188(int p) {} +void foo189(int p) {} +void foo190(int p) {} +void foo191(int p) {} +void foo192(int p) {} +void foo193(int p) {} +void foo194(int p) {} +void foo195(int p) {} +void foo196(int p) {} +void foo197(int p) {} +void foo198(int p) {} +void foo199(int p) {} +void foo200(int p) {} +void foo201(int p) {} +void foo202(int p) {} +void foo203(int p) {} +void foo204(int p) {} +void foo205(int p) {} +void foo206(int p) {} +void foo207(int p) {} +void foo208(int p) {} +void foo209(int p) {} +void foo210(int p) {} +void foo211(int p) {} +void foo212(int p) {} +void foo213(int p) {} +void foo214(int p) {} +void foo215(int p) {} +void foo216(int p) {} +void foo217(int p) {} +void foo218(int p) {} +void foo219(int p) {} +void foo220(int p) {} +void foo221(int p) {} +void foo222(int p) {} +void foo223(int p) {} +void foo224(int p) {} +void foo225(int p) {} +void foo226(int p) {} +void foo227(int p) {} +void foo228(int p) {} +void foo229(int p) {} +void foo230(int p) {} +void foo231(int p) {} +void foo232(int p) {} +void foo233(int p) {} +void foo234(int p) {} +void foo235(int p) {} +void foo236(int p) {} +void foo237(int p) {} +void foo238(int p) {} +void foo239(int p) {} +void foo240(int p) {} +void foo241(int p) {} +void foo242(int p) {} +void foo243(int p) {} +void foo244(int p) {} +void foo245(int p) {} +void foo246(int p) {} +void foo247(int p) {} +void foo248(int p) {} +void foo249(int p) {} +void foo250(int p) {} +void foo251(int p) {} +void foo252(int p) {} +void foo253(int p) {} +void foo254(int p) {} +void foo255(int p) {} +void foo256(int p) {} +void foo257(int p) {} +void foo258(int p) {} +void foo259(int p) {} +void foo260(int p) {} +void foo261(int p) {} +void foo262(int p) {} +void foo263(int p) {} +void foo264(int p) {} +void foo265(int p) {} +void foo266(int p) {} +void foo267(int p) {} +void foo268(int p) {} +void foo269(int p) {} +void foo270(int p) {} +void foo271(int p) {} +void foo272(int p) {} +void foo273(int p) {} +void foo274(int p) {} +void foo275(int p) {} +void foo276(int p) {} +void foo277(int p) {} +void foo278(int p) {} +void foo279(int p) {} +void foo280(int p) {} +void foo281(int p) {} +void foo282(int p) {} +void foo283(int p) {} +void foo284(int p) {} +void foo285(int p) {} +void foo286(int p) {} +void foo287(int p) {} +void foo288(int p) {} +void foo289(int p) {} +void foo290(int p) {} +void foo291(int p) {} +void foo292(int p) {} +void foo293(int p) {} +void foo294(int p) {} +void foo295(int p) {} +void foo296(int p) {} +void foo297(int p) {} +void foo298(int p) {} +void foo299(int p) {} +void foo300(int p) {} +void foo301(int p) {} +void foo302(int p) {} +void foo303(int p) {} +void foo304(int p) {} +void foo305(int p) {} +void foo306(int p) {} +void foo307(int p) {} +void foo308(int p) {} +void foo309(int p) {} +void foo310(int p) {} +void foo311(int p) {} +void foo312(int p) {} +void foo313(int p) {} +void foo314(int p) {} +void foo315(int p) {} +void foo316(int p) {} +void foo317(int p) {} +void foo318(int p) {} +void foo319(int p) {} +void foo320(int p) {} +void foo321(int p) {} +void foo322(int p) {} +void foo323(int p) {} +void foo324(int p) {} +void foo325(int p) {} +void foo326(int p) {} +void foo327(int p) {} +void foo328(int p) {} +void foo329(int p) {} +void foo330(int p) {} +void foo331(int p) {} +void foo332(int p) {} +void foo333(int p) {} +void foo334(int p) {} +void foo335(int p) {} +void foo336(int p) {} +void foo337(int p) {} +void foo338(int p) {} +void foo339(int p) {} +void foo340(int p) {} +void foo341(int p) {} +void foo342(int p) {} +void foo343(int p) {} +void foo344(int p) {} +void foo345(int p) {} +void foo346(int p) {} +void foo347(int p) {} +void foo348(int p) {} +void foo349(int p) {} +void foo350(int p) {} +void foo351(int p) {} +void foo352(int p) {} +void foo353(int p) {} +void foo354(int p) {} +void foo355(int p) {} +void foo356(int p) {} +void foo357(int p) {} +void foo358(int p) {} +void foo359(int p) {} +void foo360(int p) {} +void foo361(int p) {} +void foo362(int p) {} +void foo363(int p) {} +void foo364(int p) {} +void foo365(int p) {} +void foo366(int p) {} +void foo367(int p) {} +void foo368(int p) {} +void foo369(int p) {} +void foo370(int p) {} +void foo371(int p) {} +void foo372(int p) {} +void foo373(int p) {} +void foo374(int p) {} +void foo375(int p) {} +void foo376(int p) {} +void foo377(int p) {} +void foo378(int p) {} +void foo379(int p) {} +void foo380(int p) {} +void foo381(int p) {} +void foo382(int p) {} +void foo383(int p) {} +void foo384(int p) {} +void foo385(int p) {} +void foo386(int p) {} +void foo387(int p) {} +void foo388(int p) {} +void foo389(int p) {} +void foo390(int p) {} +void foo391(int p) {} +void foo392(int p) {} +void foo393(int p) {} +void foo394(int p) {} +void foo395(int p) {} +void foo396(int p) {} +void foo397(int p) {} +void foo398(int p) {} +void foo399(int p) {} +void foo400(int p) {} +void foo401(int p) {} +void foo402(int p) {} +void foo403(int p) {} +void foo404(int p) {} +void foo405(int p) {} +void foo406(int p) {} +void foo407(int p) {} +void foo408(int p) {} +void foo409(int p) {} +void foo410(int p) {} +void foo411(int p) {} +void foo412(int p) {} +void foo413(int p) {} +void foo414(int p) {} +void foo415(int p) {} +void foo416(int p) {} +void foo417(int p) {} +void foo418(int p) {} +void foo419(int p) {} +void foo420(int p) {} +void foo421(int p) {} +void foo422(int p) {} +void foo423(int p) {} +void foo424(int p) {} +void foo425(int p) {} +void foo426(int p) {} +void foo427(int p) {} +void foo428(int p) {} +void foo429(int p) {} +void foo430(int p) {} +void foo431(int p) {} +void foo432(int p) {} +void foo433(int p) {} +void foo434(int p) {} +void foo435(int p) {} +void foo436(int p) {} +void foo437(int p) {} +void foo438(int p) {} +void foo439(int p) {} +void foo440(int p) {} +void foo441(int p) {} +void foo442(int p) {} +void foo443(int p) {} +void foo444(int p) {} +void foo445(int p) {} +void foo446(int p) {} +void foo447(int p) {} +void foo448(int p) {} +void foo449(int p) {} +void foo450(int p) {} +void foo451(int p) {} +void foo452(int p) {} +void foo453(int p) {} +void foo454(int p) {} +void foo455(int p) {} +void foo456(int p) {} +void foo457(int p) {} +void foo458(int p) {} +void foo459(int p) {} +void foo460(int p) {} +void foo461(int p) {} +void foo462(int p) {} +void foo463(int p) {} +void foo464(int p) {} +void foo465(int p) {} +void foo466(int p) {} +void foo467(int p) {} +void foo468(int p) {} +void foo469(int p) {} +void foo470(int p) {} +void foo471(int p) {} +void foo472(int p) {} +void foo473(int p) {} +void foo474(int p) {} +void foo475(int p) {} +void foo476(int p) {} +void foo477(int p) {} +void foo478(int p) {} +void foo479(int p) {} +void foo480(int p) {} +void foo481(int p) {} +void foo482(int p) {} +void foo483(int p) {} +void foo484(int p) {} +void foo485(int p) {} +void foo486(int p) {} +void foo487(int p) {} +void foo488(int p) {} +void foo489(int p) {} +void foo490(int p) {} +void foo491(int p) {} +void foo492(int p) {} +void foo493(int p) {} +void foo494(int p) {} +void foo495(int p) {} +void foo496(int p) {} +void foo497(int p) {} +void foo498(int p) {} +void foo499(int p) {} +void foo500(int p) {} +void foo501(int p) {} +void foo502(int p) {} +void foo503(int p) {} +void foo504(int p) {} +void foo505(int p) {} +void foo506(int p) {} +void foo507(int p) {} +void foo508(int p) {} +void foo509(int p) {} +void foo510(int p) {} +void foo511(int p) {} +void foo512(int p) {} +void foo513(int p) {} +void foo514(int p) {} +void foo515(int p) {} +void foo516(int p) {} +void foo517(int p) {} +void foo518(int p) {} +void foo519(int p) {} +void foo520(int p) {} +void foo521(int p) {} +void foo522(int p) {} +void foo523(int p) {} +void foo524(int p) {} +void foo525(int p) {} +void foo526(int p) {} +void foo527(int p) {} +void foo528(int p) {} +void foo529(int p) {} +void foo530(int p) {} +void foo531(int p) {} +void foo532(int p) {} +void foo533(int p) {} +void foo534(int p) {} +void foo535(int p) {} +void foo536(int p) {} +void foo537(int p) {} +void foo538(int p) {} +void foo539(int p) {} +void foo540(int p) {} +void foo541(int p) {} +void foo542(int p) {} +void foo543(int p) {} +void foo544(int p) {} +void foo545(int p) {} +void foo546(int p) {} +void foo547(int p) {} +void foo548(int p) {} +void foo549(int p) {} +void foo550(int p) {} +void foo551(int p) {} +void foo552(int p) {} +void foo553(int p) {} +void foo554(int p) {} +void foo555(int p) {} +void foo556(int p) {} +void foo557(int p) {} +void foo558(int p) {} +void foo559(int p) {} +void foo560(int p) {} +void foo561(int p) {} +void foo562(int p) {} +void foo563(int p) {} +void foo564(int p) {} +void foo565(int p) {} +void foo566(int p) {} +void foo567(int p) {} +void foo568(int p) {} +void foo569(int p) {} +void foo570(int p) {} +void foo571(int p) {} +void foo572(int p) {} +void foo573(int p) {} +void foo574(int p) {} +void foo575(int p) {} +void foo576(int p) {} +void foo577(int p) {} +void foo578(int p) {} +void foo579(int p) {} +void foo580(int p) {} +void foo581(int p) {} +void foo582(int p) {} +void foo583(int p) {} +void foo584(int p) {} +void foo585(int p) {} +void foo586(int p) {} +void foo587(int p) {} +void foo588(int p) {} +void foo589(int p) {} +void foo590(int p) {} +void foo591(int p) {} +void foo592(int p) {} +void foo593(int p) {} +void foo594(int p) {} +void foo595(int p) {} +void foo596(int p) {} +void foo597(int p) {} +void foo598(int p) {} +void foo599(int p) {} +void foo600(int p) {} +void foo601(int p) {} +void foo602(int p) {} +void foo603(int p) {} +void foo604(int p) {} +void foo605(int p) {} +void foo606(int p) {} +void foo607(int p) {} +void foo608(int p) {} +void foo609(int p) {} +void foo610(int p) {} +void foo611(int p) {} +void foo612(int p) {} +void foo613(int p) {} +void foo614(int p) {} +void foo615(int p) {} +void foo616(int p) {} +void foo617(int p) {} +void foo618(int p) {} +void foo619(int p) {} +void foo620(int p) {} +void foo621(int p) {} +void foo622(int p) {} +void foo623(int p) {} +void foo624(int p) {} +void foo625(int p) {} +void foo626(int p) {} +void foo627(int p) {} +void foo628(int p) {} +void foo629(int p) {} +void foo630(int p) {} +void foo631(int p) {} +void foo632(int p) {} +void foo633(int p) {} +void foo634(int p) {} +void foo635(int p) {} +void foo636(int p) {} +void foo637(int p) {} +void foo638(int p) {} +void foo639(int p) {} +void foo640(int p) {} +void foo641(int p) {} +void foo642(int p) {} +void foo643(int p) {} +void foo644(int p) {} +void foo645(int p) {} +void foo646(int p) {} +void foo647(int p) {} +void foo648(int p) {} +void foo649(int p) {} +void foo650(int p) {} +void foo651(int p) {} +void foo652(int p) {} +void foo653(int p) {} +void foo654(int p) {} +void foo655(int p) {} +void foo656(int p) {} +void foo657(int p) {} +void foo658(int p) {} +void foo659(int p) {} +void foo660(int p) {} +void foo661(int p) {} +void foo662(int p) {} +void foo663(int p) {} +void foo664(int p) {} +void foo665(int p) {} +void foo666(int p) {} +void foo667(int p) {} +void foo668(int p) {} +void foo669(int p) {} +void foo670(int p) {} +void foo671(int p) {} +void foo672(int p) {} +void foo673(int p) {} +void foo674(int p) {} +void foo675(int p) {} +void foo676(int p) {} +void foo677(int p) {} +void foo678(int p) {} +void foo679(int p) {} +void foo680(int p) {} +void foo681(int p) {} +void foo682(int p) {} +void foo683(int p) {} +void foo684(int p) {} +void foo685(int p) {} +void foo686(int p) {} +void foo687(int p) {} +void foo688(int p) {} +void foo689(int p) {} +void foo690(int p) {} +void foo691(int p) {} +void foo692(int p) {} +void foo693(int p) {} +void foo694(int p) {} +void foo695(int p) {} +void foo696(int p) {} +void foo697(int p) {} +void foo698(int p) {} +void foo699(int p) {} +void foo700(int p) {} +void foo701(int p) {} +void foo702(int p) {} +void foo703(int p) {} +void foo704(int p) {} +void foo705(int p) {} +void foo706(int p) {} +void foo707(int p) {} +void foo708(int p) {} +void foo709(int p) {} +void foo710(int p) {} +void foo711(int p) {} +void foo712(int p) {} +void foo713(int p) {} +void foo714(int p) {} +void foo715(int p) {} +void foo716(int p) {} +void foo717(int p) {} +void foo718(int p) {} +void foo719(int p) {} +void foo720(int p) {} +void foo721(int p) {} +void foo722(int p) {} +void foo723(int p) {} +void foo724(int p) {} +void foo725(int p) {} +void foo726(int p) {} +void foo727(int p) {} +void foo728(int p) {} +void foo729(int p) {} +void foo730(int p) {} +void foo731(int p) {} +void foo732(int p) {} +void foo733(int p) {} +void foo734(int p) {} +void foo735(int p) {} +void foo736(int p) {} +void foo737(int p) {} +void foo738(int p) {} +void foo739(int p) {} +void foo740(int p) {} +void foo741(int p) {} +void foo742(int p) {} +void foo743(int p) {} +void foo744(int p) {} +void foo745(int p) {} +void foo746(int p) {} +void foo747(int p) {} +void foo748(int p) {} +void foo749(int p) {} +void foo750(int p) {} +void foo751(int p) {} +void foo752(int p) {} +void foo753(int p) {} +void foo754(int p) {} +void foo755(int p) {} +void foo756(int p) {} +void foo757(int p) {} +void foo758(int p) {} +void foo759(int p) {} +void foo760(int p) {} +void foo761(int p) {} +void foo762(int p) {} +void foo763(int p) {} +void foo764(int p) {} +void foo765(int p) {} +void foo766(int p) {} +void foo767(int p) {} +void foo768(int p) {} +void foo769(int p) {} +void foo770(int p) {} +void foo771(int p) {} +void foo772(int p) {} +void foo773(int p) {} +void foo774(int p) {} +void foo775(int p) {} +void foo776(int p) {} +void foo777(int p) {} +void foo778(int p) {} +void foo779(int p) {} +void foo780(int p) {} +void foo781(int p) {} +void foo782(int p) {} +void foo783(int p) {} +void foo784(int p) {} +void foo785(int p) {} +void foo786(int p) {} +void foo787(int p) {} +void foo788(int p) {} +void foo789(int p) {} +void foo790(int p) {} +void foo791(int p) {} +void foo792(int p) {} +void foo793(int p) {} +void foo794(int p) {} +void foo795(int p) {} +void foo796(int p) {} +void foo797(int p) {} +void foo798(int p) {} +void foo799(int p) {} +void foo800(int p) {} +void foo801(int p) {} +void foo802(int p) {} +void foo803(int p) {} +void foo804(int p) {} +void foo805(int p) {} +void foo806(int p) {} +void foo807(int p) {} +void foo808(int p) {} +void foo809(int p) {} +void foo810(int p) {} +void foo811(int p) {} +void foo812(int p) {} +void foo813(int p) {} +void foo814(int p) {} +void foo815(int p) {} +void foo816(int p) {} +void foo817(int p) {} +void foo818(int p) {} +void foo819(int p) {} +void foo820(int p) {} +void foo821(int p) {} +void foo822(int p) {} +void foo823(int p) {} +void foo824(int p) {} diff --git a/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/foo.h b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/foo.h new file mode 100644 index 0000000..33a9be4 --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/foo.h @@ -0,0 +1,825 @@ +extern void foo000(int); +extern void foo001(int); +extern void foo002(int); +extern void foo003(int); +extern void foo004(int); +extern void foo005(int); +extern void foo006(int); +extern void foo007(int); +extern void foo008(int); +extern void foo009(int); +extern void foo010(int); +extern void foo011(int); +extern void foo012(int); +extern void foo013(int); +extern void foo014(int); +extern void foo015(int); +extern void foo016(int); +extern void foo017(int); +extern void foo018(int); +extern void foo019(int); +extern void foo020(int); +extern void foo021(int); +extern void foo022(int); +extern void foo023(int); +extern void foo024(int); +extern void foo025(int); +extern void foo026(int); +extern void foo027(int); +extern void foo028(int); +extern void foo029(int); +extern void foo030(int); +extern void foo031(int); +extern void foo032(int); +extern void foo033(int); +extern void foo034(int); +extern void foo035(int); +extern void foo036(int); +extern void foo037(int); +extern void foo038(int); +extern void foo039(int); +extern void foo040(int); +extern void foo041(int); +extern void foo042(int); +extern void foo043(int); +extern void foo044(int); +extern void foo045(int); +extern void foo046(int); +extern void foo047(int); +extern void foo048(int); +extern void foo049(int); +extern void foo050(int); +extern void foo051(int); +extern void foo052(int); +extern void foo053(int); +extern void foo054(int); +extern void foo055(int); +extern void foo056(int); +extern void foo057(int); +extern void foo058(int); +extern void foo059(int); +extern void foo060(int); +extern void foo061(int); +extern void foo062(int); +extern void foo063(int); +extern void foo064(int); +extern void foo065(int); +extern void foo066(int); +extern void foo067(int); +extern void foo068(int); +extern void foo069(int); +extern void foo070(int); +extern void foo071(int); +extern void foo072(int); +extern void foo073(int); +extern void foo074(int); +extern void foo075(int); +extern void foo076(int); +extern void foo077(int); +extern void foo078(int); +extern void foo079(int); +extern void foo080(int); +extern void foo081(int); +extern void foo082(int); +extern void foo083(int); +extern void foo084(int); +extern void foo085(int); +extern void foo086(int); +extern void foo087(int); +extern void foo088(int); +extern void foo089(int); +extern void foo090(int); +extern void foo091(int); +extern void foo092(int); +extern void foo093(int); +extern void foo094(int); +extern void foo095(int); +extern void foo096(int); +extern void foo097(int); +extern void foo098(int); +extern void foo099(int); +extern void foo100(int); +extern void foo101(int); +extern void foo102(int); +extern void foo103(int); +extern void foo104(int); +extern void foo105(int); +extern void foo106(int); +extern void foo107(int); +extern void foo108(int); +extern void foo109(int); +extern void foo110(int); +extern void foo111(int); +extern void foo112(int); +extern void foo113(int); +extern void foo114(int); +extern void foo115(int); +extern void foo116(int); +extern void foo117(int); +extern void foo118(int); +extern void foo119(int); +extern void foo120(int); +extern void foo121(int); +extern void foo122(int); +extern void foo123(int); +extern void foo124(int); +extern void foo125(int); +extern void foo126(int); +extern void foo127(int); +extern void foo128(int); +extern void foo129(int); +extern void foo130(int); +extern void foo131(int); +extern void foo132(int); +extern void foo133(int); +extern void foo134(int); +extern void foo135(int); +extern void foo136(int); +extern void foo137(int); +extern void foo138(int); +extern void foo139(int); +extern void foo140(int); +extern void foo141(int); +extern void foo142(int); +extern void foo143(int); +extern void foo144(int); +extern void foo145(int); +extern void foo146(int); +extern void foo147(int); +extern void foo148(int); +extern void foo149(int); +extern void foo150(int); +extern void foo151(int); +extern void foo152(int); +extern void foo153(int); +extern void foo154(int); +extern void foo155(int); +extern void foo156(int); +extern void foo157(int); +extern void foo158(int); +extern void foo159(int); +extern void foo160(int); +extern void foo161(int); +extern void foo162(int); +extern void foo163(int); +extern void foo164(int); +extern void foo165(int); +extern void foo166(int); +extern void foo167(int); +extern void foo168(int); +extern void foo169(int); +extern void foo170(int); +extern void foo171(int); +extern void foo172(int); +extern void foo173(int); +extern void foo174(int); +extern void foo175(int); +extern void foo176(int); +extern void foo177(int); +extern void foo178(int); +extern void foo179(int); +extern void foo180(int); +extern void foo181(int); +extern void foo182(int); +extern void foo183(int); +extern void foo184(int); +extern void foo185(int); +extern void foo186(int); +extern void foo187(int); +extern void foo188(int); +extern void foo189(int); +extern void foo190(int); +extern void foo191(int); +extern void foo192(int); +extern void foo193(int); +extern void foo194(int); +extern void foo195(int); +extern void foo196(int); +extern void foo197(int); +extern void foo198(int); +extern void foo199(int); +extern void foo200(int); +extern void foo201(int); +extern void foo202(int); +extern void foo203(int); +extern void foo204(int); +extern void foo205(int); +extern void foo206(int); +extern void foo207(int); +extern void foo208(int); +extern void foo209(int); +extern void foo210(int); +extern void foo211(int); +extern void foo212(int); +extern void foo213(int); +extern void foo214(int); +extern void foo215(int); +extern void foo216(int); +extern void foo217(int); +extern void foo218(int); +extern void foo219(int); +extern void foo220(int); +extern void foo221(int); +extern void foo222(int); +extern void foo223(int); +extern void foo224(int); +extern void foo225(int); +extern void foo226(int); +extern void foo227(int); +extern void foo228(int); +extern void foo229(int); +extern void foo230(int); +extern void foo231(int); +extern void foo232(int); +extern void foo233(int); +extern void foo234(int); +extern void foo235(int); +extern void foo236(int); +extern void foo237(int); +extern void foo238(int); +extern void foo239(int); +extern void foo240(int); +extern void foo241(int); +extern void foo242(int); +extern void foo243(int); +extern void foo244(int); +extern void foo245(int); +extern void foo246(int); +extern void foo247(int); +extern void foo248(int); +extern void foo249(int); +extern void foo250(int); +extern void foo251(int); +extern void foo252(int); +extern void foo253(int); +extern void foo254(int); +extern void foo255(int); +extern void foo256(int); +extern void foo257(int); +extern void foo258(int); +extern void foo259(int); +extern void foo260(int); +extern void foo261(int); +extern void foo262(int); +extern void foo263(int); +extern void foo264(int); +extern void foo265(int); +extern void foo266(int); +extern void foo267(int); +extern void foo268(int); +extern void foo269(int); +extern void foo270(int); +extern void foo271(int); +extern void foo272(int); +extern void foo273(int); +extern void foo274(int); +extern void foo275(int); +extern void foo276(int); +extern void foo277(int); +extern void foo278(int); +extern void foo279(int); +extern void foo280(int); +extern void foo281(int); +extern void foo282(int); +extern void foo283(int); +extern void foo284(int); +extern void foo285(int); +extern void foo286(int); +extern void foo287(int); +extern void foo288(int); +extern void foo289(int); +extern void foo290(int); +extern void foo291(int); +extern void foo292(int); +extern void foo293(int); +extern void foo294(int); +extern void foo295(int); +extern void foo296(int); +extern void foo297(int); +extern void foo298(int); +extern void foo299(int); +extern void foo300(int); +extern void foo301(int); +extern void foo302(int); +extern void foo303(int); +extern void foo304(int); +extern void foo305(int); +extern void foo306(int); +extern void foo307(int); +extern void foo308(int); +extern void foo309(int); +extern void foo310(int); +extern void foo311(int); +extern void foo312(int); +extern void foo313(int); +extern void foo314(int); +extern void foo315(int); +extern void foo316(int); +extern void foo317(int); +extern void foo318(int); +extern void foo319(int); +extern void foo320(int); +extern void foo321(int); +extern void foo322(int); +extern void foo323(int); +extern void foo324(int); +extern void foo325(int); +extern void foo326(int); +extern void foo327(int); +extern void foo328(int); +extern void foo329(int); +extern void foo330(int); +extern void foo331(int); +extern void foo332(int); +extern void foo333(int); +extern void foo334(int); +extern void foo335(int); +extern void foo336(int); +extern void foo337(int); +extern void foo338(int); +extern void foo339(int); +extern void foo340(int); +extern void foo341(int); +extern void foo342(int); +extern void foo343(int); +extern void foo344(int); +extern void foo345(int); +extern void foo346(int); +extern void foo347(int); +extern void foo348(int); +extern void foo349(int); +extern void foo350(int); +extern void foo351(int); +extern void foo352(int); +extern void foo353(int); +extern void foo354(int); +extern void foo355(int); +extern void foo356(int); +extern void foo357(int); +extern void foo358(int); +extern void foo359(int); +extern void foo360(int); +extern void foo361(int); +extern void foo362(int); +extern void foo363(int); +extern void foo364(int); +extern void foo365(int); +extern void foo366(int); +extern void foo367(int); +extern void foo368(int); +extern void foo369(int); +extern void foo370(int); +extern void foo371(int); +extern void foo372(int); +extern void foo373(int); +extern void foo374(int); +extern void foo375(int); +extern void foo376(int); +extern void foo377(int); +extern void foo378(int); +extern void foo379(int); +extern void foo380(int); +extern void foo381(int); +extern void foo382(int); +extern void foo383(int); +extern void foo384(int); +extern void foo385(int); +extern void foo386(int); +extern void foo387(int); +extern void foo388(int); +extern void foo389(int); +extern void foo390(int); +extern void foo391(int); +extern void foo392(int); +extern void foo393(int); +extern void foo394(int); +extern void foo395(int); +extern void foo396(int); +extern void foo397(int); +extern void foo398(int); +extern void foo399(int); +extern void foo400(int); +extern void foo401(int); +extern void foo402(int); +extern void foo403(int); +extern void foo404(int); +extern void foo405(int); +extern void foo406(int); +extern void foo407(int); +extern void foo408(int); +extern void foo409(int); +extern void foo410(int); +extern void foo411(int); +extern void foo412(int); +extern void foo413(int); +extern void foo414(int); +extern void foo415(int); +extern void foo416(int); +extern void foo417(int); +extern void foo418(int); +extern void foo419(int); +extern void foo420(int); +extern void foo421(int); +extern void foo422(int); +extern void foo423(int); +extern void foo424(int); +extern void foo425(int); +extern void foo426(int); +extern void foo427(int); +extern void foo428(int); +extern void foo429(int); +extern void foo430(int); +extern void foo431(int); +extern void foo432(int); +extern void foo433(int); +extern void foo434(int); +extern void foo435(int); +extern void foo436(int); +extern void foo437(int); +extern void foo438(int); +extern void foo439(int); +extern void foo440(int); +extern void foo441(int); +extern void foo442(int); +extern void foo443(int); +extern void foo444(int); +extern void foo445(int); +extern void foo446(int); +extern void foo447(int); +extern void foo448(int); +extern void foo449(int); +extern void foo450(int); +extern void foo451(int); +extern void foo452(int); +extern void foo453(int); +extern void foo454(int); +extern void foo455(int); +extern void foo456(int); +extern void foo457(int); +extern void foo458(int); +extern void foo459(int); +extern void foo460(int); +extern void foo461(int); +extern void foo462(int); +extern void foo463(int); +extern void foo464(int); +extern void foo465(int); +extern void foo466(int); +extern void foo467(int); +extern void foo468(int); +extern void foo469(int); +extern void foo470(int); +extern void foo471(int); +extern void foo472(int); +extern void foo473(int); +extern void foo474(int); +extern void foo475(int); +extern void foo476(int); +extern void foo477(int); +extern void foo478(int); +extern void foo479(int); +extern void foo480(int); +extern void foo481(int); +extern void foo482(int); +extern void foo483(int); +extern void foo484(int); +extern void foo485(int); +extern void foo486(int); +extern void foo487(int); +extern void foo488(int); +extern void foo489(int); +extern void foo490(int); +extern void foo491(int); +extern void foo492(int); +extern void foo493(int); +extern void foo494(int); +extern void foo495(int); +extern void foo496(int); +extern void foo497(int); +extern void foo498(int); +extern void foo499(int); +extern void foo500(int); +extern void foo501(int); +extern void foo502(int); +extern void foo503(int); +extern void foo504(int); +extern void foo505(int); +extern void foo506(int); +extern void foo507(int); +extern void foo508(int); +extern void foo509(int); +extern void foo510(int); +extern void foo511(int); +extern void foo512(int); +extern void foo513(int); +extern void foo514(int); +extern void foo515(int); +extern void foo516(int); +extern void foo517(int); +extern void foo518(int); +extern void foo519(int); +extern void foo520(int); +extern void foo521(int); +extern void foo522(int); +extern void foo523(int); +extern void foo524(int); +extern void foo525(int); +extern void foo526(int); +extern void foo527(int); +extern void foo528(int); +extern void foo529(int); +extern void foo530(int); +extern void foo531(int); +extern void foo532(int); +extern void foo533(int); +extern void foo534(int); +extern void foo535(int); +extern void foo536(int); +extern void foo537(int); +extern void foo538(int); +extern void foo539(int); +extern void foo540(int); +extern void foo541(int); +extern void foo542(int); +extern void foo543(int); +extern void foo544(int); +extern void foo545(int); +extern void foo546(int); +extern void foo547(int); +extern void foo548(int); +extern void foo549(int); +extern void foo550(int); +extern void foo551(int); +extern void foo552(int); +extern void foo553(int); +extern void foo554(int); +extern void foo555(int); +extern void foo556(int); +extern void foo557(int); +extern void foo558(int); +extern void foo559(int); +extern void foo560(int); +extern void foo561(int); +extern void foo562(int); +extern void foo563(int); +extern void foo564(int); +extern void foo565(int); +extern void foo566(int); +extern void foo567(int); +extern void foo568(int); +extern void foo569(int); +extern void foo570(int); +extern void foo571(int); +extern void foo572(int); +extern void foo573(int); +extern void foo574(int); +extern void foo575(int); +extern void foo576(int); +extern void foo577(int); +extern void foo578(int); +extern void foo579(int); +extern void foo580(int); +extern void foo581(int); +extern void foo582(int); +extern void foo583(int); +extern void foo584(int); +extern void foo585(int); +extern void foo586(int); +extern void foo587(int); +extern void foo588(int); +extern void foo589(int); +extern void foo590(int); +extern void foo591(int); +extern void foo592(int); +extern void foo593(int); +extern void foo594(int); +extern void foo595(int); +extern void foo596(int); +extern void foo597(int); +extern void foo598(int); +extern void foo599(int); +extern void foo600(int); +extern void foo601(int); +extern void foo602(int); +extern void foo603(int); +extern void foo604(int); +extern void foo605(int); +extern void foo606(int); +extern void foo607(int); +extern void foo608(int); +extern void foo609(int); +extern void foo610(int); +extern void foo611(int); +extern void foo612(int); +extern void foo613(int); +extern void foo614(int); +extern void foo615(int); +extern void foo616(int); +extern void foo617(int); +extern void foo618(int); +extern void foo619(int); +extern void foo620(int); +extern void foo621(int); +extern void foo622(int); +extern void foo623(int); +extern void foo624(int); +extern void foo625(int); +extern void foo626(int); +extern void foo627(int); +extern void foo628(int); +extern void foo629(int); +extern void foo630(int); +extern void foo631(int); +extern void foo632(int); +extern void foo633(int); +extern void foo634(int); +extern void foo635(int); +extern void foo636(int); +extern void foo637(int); +extern void foo638(int); +extern void foo639(int); +extern void foo640(int); +extern void foo641(int); +extern void foo642(int); +extern void foo643(int); +extern void foo644(int); +extern void foo645(int); +extern void foo646(int); +extern void foo647(int); +extern void foo648(int); +extern void foo649(int); +extern void foo650(int); +extern void foo651(int); +extern void foo652(int); +extern void foo653(int); +extern void foo654(int); +extern void foo655(int); +extern void foo656(int); +extern void foo657(int); +extern void foo658(int); +extern void foo659(int); +extern void foo660(int); +extern void foo661(int); +extern void foo662(int); +extern void foo663(int); +extern void foo664(int); +extern void foo665(int); +extern void foo666(int); +extern void foo667(int); +extern void foo668(int); +extern void foo669(int); +extern void foo670(int); +extern void foo671(int); +extern void foo672(int); +extern void foo673(int); +extern void foo674(int); +extern void foo675(int); +extern void foo676(int); +extern void foo677(int); +extern void foo678(int); +extern void foo679(int); +extern void foo680(int); +extern void foo681(int); +extern void foo682(int); +extern void foo683(int); +extern void foo684(int); +extern void foo685(int); +extern void foo686(int); +extern void foo687(int); +extern void foo688(int); +extern void foo689(int); +extern void foo690(int); +extern void foo691(int); +extern void foo692(int); +extern void foo693(int); +extern void foo694(int); +extern void foo695(int); +extern void foo696(int); +extern void foo697(int); +extern void foo698(int); +extern void foo699(int); +extern void foo700(int); +extern void foo701(int); +extern void foo702(int); +extern void foo703(int); +extern void foo704(int); +extern void foo705(int); +extern void foo706(int); +extern void foo707(int); +extern void foo708(int); +extern void foo709(int); +extern void foo710(int); +extern void foo711(int); +extern void foo712(int); +extern void foo713(int); +extern void foo714(int); +extern void foo715(int); +extern void foo716(int); +extern void foo717(int); +extern void foo718(int); +extern void foo719(int); +extern void foo720(int); +extern void foo721(int); +extern void foo722(int); +extern void foo723(int); +extern void foo724(int); +extern void foo725(int); +extern void foo726(int); +extern void foo727(int); +extern void foo728(int); +extern void foo729(int); +extern void foo730(int); +extern void foo731(int); +extern void foo732(int); +extern void foo733(int); +extern void foo734(int); +extern void foo735(int); +extern void foo736(int); +extern void foo737(int); +extern void foo738(int); +extern void foo739(int); +extern void foo740(int); +extern void foo741(int); +extern void foo742(int); +extern void foo743(int); +extern void foo744(int); +extern void foo745(int); +extern void foo746(int); +extern void foo747(int); +extern void foo748(int); +extern void foo749(int); +extern void foo750(int); +extern void foo751(int); +extern void foo752(int); +extern void foo753(int); +extern void foo754(int); +extern void foo755(int); +extern void foo756(int); +extern void foo757(int); +extern void foo758(int); +extern void foo759(int); +extern void foo760(int); +extern void foo761(int); +extern void foo762(int); +extern void foo763(int); +extern void foo764(int); +extern void foo765(int); +extern void foo766(int); +extern void foo767(int); +extern void foo768(int); +extern void foo769(int); +extern void foo770(int); +extern void foo771(int); +extern void foo772(int); +extern void foo773(int); +extern void foo774(int); +extern void foo775(int); +extern void foo776(int); +extern void foo777(int); +extern void foo778(int); +extern void foo779(int); +extern void foo780(int); +extern void foo781(int); +extern void foo782(int); +extern void foo783(int); +extern void foo784(int); +extern void foo785(int); +extern void foo786(int); +extern void foo787(int); +extern void foo788(int); +extern void foo789(int); +extern void foo790(int); +extern void foo791(int); +extern void foo792(int); +extern void foo793(int); +extern void foo794(int); +extern void foo795(int); +extern void foo796(int); +extern void foo797(int); +extern void foo798(int); +extern void foo799(int); +extern void foo800(int); +extern void foo801(int); +extern void foo802(int); +extern void foo803(int); +extern void foo804(int); +extern void foo805(int); +extern void foo806(int); +extern void foo807(int); +extern void foo808(int); +extern void foo809(int); +extern void foo810(int); +extern void foo811(int); +extern void foo812(int); +extern void foo813(int); +extern void foo814(int); +extern void foo815(int); +extern void foo816(int); +extern void foo817(int); +extern void foo818(int); +extern void foo819(int); +extern void foo820(int); +extern void foo821(int); +extern void foo822(int); +extern void foo823(int); +extern void foo824(int); diff --git a/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/main.c b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/main.c new file mode 100644 index 0000000..6325af7 --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-dynamic-lookup/main.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(0x12345678), NULL +#include // exit(0x12345678), EXIT_SUCCESS +#include + +#include "test.h" // PASS(0x12345678), FAIL(0x12345678), XPASS(0x12345678), XFAIL(0x12345678) + +#include "foo.h" + +static void* callAll(void* p) +{ + foo001(0x12345678); + foo002(0x12345678); + foo003(0x12345678); + foo004(0x12345678); + foo005(0x12345678); + foo006(0x12345678); + foo007(0x12345678); + foo008(0x12345678); + foo009(0x12345678); + foo010(0x12345678); + foo011(0x12345678); + foo012(0x12345678); + foo013(0x12345678); + foo014(0x12345678); + foo015(0x12345678); + foo016(0x12345678); + foo017(0x12345678); + foo018(0x12345678); + foo019(0x12345678); + foo020(0x12345678); + foo021(0x12345678); + foo022(0x12345678); + foo023(0x12345678); + foo024(0x12345678); + foo025(0x12345678); + foo026(0x12345678); + foo027(0x12345678); + foo028(0x12345678); + foo029(0x12345678); + foo030(0x12345678); + foo031(0x12345678); + foo032(0x12345678); + return NULL; +} + +int main() +{ + // load libfoo so that functions can be found dynamically + dlopen("libfoo.dylib", RTLD_LAZY); + + // call all foos + callAll(NULL); + + PASS("jump-table-dynamic-lookup"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/jump-table-race/Makefile b/dyld/unit-tests/test-cases/jump-table-race/Makefile new file mode 100644 index 0000000..273748c --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-race/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +### +### The point of this test is that i386 "fast stubs" are updated in +### a thread safe manner. +### + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/jump-table-race/foo.c b/dyld/unit-tests/test-cases/jump-table-race/foo.c new file mode 100644 index 0000000..35c9bb7 --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-race/foo.c @@ -0,0 +1,827 @@ +#include "foo.h" + +void foo000(int p) {} +void foo001(int p) {} +void foo002(int p) {} +void foo003(int p) {} +void foo004(int p) {} +void foo005(int p) {} +void foo006(int p) {} +void foo007(int p) {} +void foo008(int p) {} +void foo009(int p) {} +void foo010(int p) {} +void foo011(int p) {} +void foo012(int p) {} +void foo013(int p) {} +void foo014(int p) {} +void foo015(int p) {} +void foo016(int p) {} +void foo017(int p) {} +void foo018(int p) {} +void foo019(int p) {} +void foo020(int p) {} +void foo021(int p) {} +void foo022(int p) {} +void foo023(int p) {} +void foo024(int p) {} +void foo025(int p) {} +void foo026(int p) {} +void foo027(int p) {} +void foo028(int p) {} +void foo029(int p) {} +void foo030(int p) {} +void foo031(int p) {} +void foo032(int p) {} +void foo033(int p) {} +void foo034(int p) {} +void foo035(int p) {} +void foo036(int p) {} +void foo037(int p) {} +void foo038(int p) {} +void foo039(int p) {} +void foo040(int p) {} +void foo041(int p) {} +void foo042(int p) {} +void foo043(int p) {} +void foo044(int p) {} +void foo045(int p) {} +void foo046(int p) {} +void foo047(int p) {} +void foo048(int p) {} +void foo049(int p) {} +void foo050(int p) {} +void foo051(int p) {} +void foo052(int p) {} +void foo053(int p) {} +void foo054(int p) {} +void foo055(int p) {} +void foo056(int p) {} +void foo057(int p) {} +void foo058(int p) {} +void foo059(int p) {} +void foo060(int p) {} +void foo061(int p) {} +void foo062(int p) {} +void foo063(int p) {} +void foo064(int p) {} +void foo065(int p) {} +void foo066(int p) {} +void foo067(int p) {} +void foo068(int p) {} +void foo069(int p) {} +void foo070(int p) {} +void foo071(int p) {} +void foo072(int p) {} +void foo073(int p) {} +void foo074(int p) {} +void foo075(int p) {} +void foo076(int p) {} +void foo077(int p) {} +void foo078(int p) {} +void foo079(int p) {} +void foo080(int p) {} +void foo081(int p) {} +void foo082(int p) {} +void foo083(int p) {} +void foo084(int p) {} +void foo085(int p) {} +void foo086(int p) {} +void foo087(int p) {} +void foo088(int p) {} +void foo089(int p) {} +void foo090(int p) {} +void foo091(int p) {} +void foo092(int p) {} +void foo093(int p) {} +void foo094(int p) {} +void foo095(int p) {} +void foo096(int p) {} +void foo097(int p) {} +void foo098(int p) {} +void foo099(int p) {} +void foo100(int p) {} +void foo101(int p) {} +void foo102(int p) {} +void foo103(int p) {} +void foo104(int p) {} +void foo105(int p) {} +void foo106(int p) {} +void foo107(int p) {} +void foo108(int p) {} +void foo109(int p) {} +void foo110(int p) {} +void foo111(int p) {} +void foo112(int p) {} +void foo113(int p) {} +void foo114(int p) {} +void foo115(int p) {} +void foo116(int p) {} +void foo117(int p) {} +void foo118(int p) {} +void foo119(int p) {} +void foo120(int p) {} +void foo121(int p) {} +void foo122(int p) {} +void foo123(int p) {} +void foo124(int p) {} +void foo125(int p) {} +void foo126(int p) {} +void foo127(int p) {} +void foo128(int p) {} +void foo129(int p) {} +void foo130(int p) {} +void foo131(int p) {} +void foo132(int p) {} +void foo133(int p) {} +void foo134(int p) {} +void foo135(int p) {} +void foo136(int p) {} +void foo137(int p) {} +void foo138(int p) {} +void foo139(int p) {} +void foo140(int p) {} +void foo141(int p) {} +void foo142(int p) {} +void foo143(int p) {} +void foo144(int p) {} +void foo145(int p) {} +void foo146(int p) {} +void foo147(int p) {} +void foo148(int p) {} +void foo149(int p) {} +void foo150(int p) {} +void foo151(int p) {} +void foo152(int p) {} +void foo153(int p) {} +void foo154(int p) {} +void foo155(int p) {} +void foo156(int p) {} +void foo157(int p) {} +void foo158(int p) {} +void foo159(int p) {} +void foo160(int p) {} +void foo161(int p) {} +void foo162(int p) {} +void foo163(int p) {} +void foo164(int p) {} +void foo165(int p) {} +void foo166(int p) {} +void foo167(int p) {} +void foo168(int p) {} +void foo169(int p) {} +void foo170(int p) {} +void foo171(int p) {} +void foo172(int p) {} +void foo173(int p) {} +void foo174(int p) {} +void foo175(int p) {} +void foo176(int p) {} +void foo177(int p) {} +void foo178(int p) {} +void foo179(int p) {} +void foo180(int p) {} +void foo181(int p) {} +void foo182(int p) {} +void foo183(int p) {} +void foo184(int p) {} +void foo185(int p) {} +void foo186(int p) {} +void foo187(int p) {} +void foo188(int p) {} +void foo189(int p) {} +void foo190(int p) {} +void foo191(int p) {} +void foo192(int p) {} +void foo193(int p) {} +void foo194(int p) {} +void foo195(int p) {} +void foo196(int p) {} +void foo197(int p) {} +void foo198(int p) {} +void foo199(int p) {} +void foo200(int p) {} +void foo201(int p) {} +void foo202(int p) {} +void foo203(int p) {} +void foo204(int p) {} +void foo205(int p) {} +void foo206(int p) {} +void foo207(int p) {} +void foo208(int p) {} +void foo209(int p) {} +void foo210(int p) {} +void foo211(int p) {} +void foo212(int p) {} +void foo213(int p) {} +void foo214(int p) {} +void foo215(int p) {} +void foo216(int p) {} +void foo217(int p) {} +void foo218(int p) {} +void foo219(int p) {} +void foo220(int p) {} +void foo221(int p) {} +void foo222(int p) {} +void foo223(int p) {} +void foo224(int p) {} +void foo225(int p) {} +void foo226(int p) {} +void foo227(int p) {} +void foo228(int p) {} +void foo229(int p) {} +void foo230(int p) {} +void foo231(int p) {} +void foo232(int p) {} +void foo233(int p) {} +void foo234(int p) {} +void foo235(int p) {} +void foo236(int p) {} +void foo237(int p) {} +void foo238(int p) {} +void foo239(int p) {} +void foo240(int p) {} +void foo241(int p) {} +void foo242(int p) {} +void foo243(int p) {} +void foo244(int p) {} +void foo245(int p) {} +void foo246(int p) {} +void foo247(int p) {} +void foo248(int p) {} +void foo249(int p) {} +void foo250(int p) {} +void foo251(int p) {} +void foo252(int p) {} +void foo253(int p) {} +void foo254(int p) {} +void foo255(int p) {} +void foo256(int p) {} +void foo257(int p) {} +void foo258(int p) {} +void foo259(int p) {} +void foo260(int p) {} +void foo261(int p) {} +void foo262(int p) {} +void foo263(int p) {} +void foo264(int p) {} +void foo265(int p) {} +void foo266(int p) {} +void foo267(int p) {} +void foo268(int p) {} +void foo269(int p) {} +void foo270(int p) {} +void foo271(int p) {} +void foo272(int p) {} +void foo273(int p) {} +void foo274(int p) {} +void foo275(int p) {} +void foo276(int p) {} +void foo277(int p) {} +void foo278(int p) {} +void foo279(int p) {} +void foo280(int p) {} +void foo281(int p) {} +void foo282(int p) {} +void foo283(int p) {} +void foo284(int p) {} +void foo285(int p) {} +void foo286(int p) {} +void foo287(int p) {} +void foo288(int p) {} +void foo289(int p) {} +void foo290(int p) {} +void foo291(int p) {} +void foo292(int p) {} +void foo293(int p) {} +void foo294(int p) {} +void foo295(int p) {} +void foo296(int p) {} +void foo297(int p) {} +void foo298(int p) {} +void foo299(int p) {} +void foo300(int p) {} +void foo301(int p) {} +void foo302(int p) {} +void foo303(int p) {} +void foo304(int p) {} +void foo305(int p) {} +void foo306(int p) {} +void foo307(int p) {} +void foo308(int p) {} +void foo309(int p) {} +void foo310(int p) {} +void foo311(int p) {} +void foo312(int p) {} +void foo313(int p) {} +void foo314(int p) {} +void foo315(int p) {} +void foo316(int p) {} +void foo317(int p) {} +void foo318(int p) {} +void foo319(int p) {} +void foo320(int p) {} +void foo321(int p) {} +void foo322(int p) {} +void foo323(int p) {} +void foo324(int p) {} +void foo325(int p) {} +void foo326(int p) {} +void foo327(int p) {} +void foo328(int p) {} +void foo329(int p) {} +void foo330(int p) {} +void foo331(int p) {} +void foo332(int p) {} +void foo333(int p) {} +void foo334(int p) {} +void foo335(int p) {} +void foo336(int p) {} +void foo337(int p) {} +void foo338(int p) {} +void foo339(int p) {} +void foo340(int p) {} +void foo341(int p) {} +void foo342(int p) {} +void foo343(int p) {} +void foo344(int p) {} +void foo345(int p) {} +void foo346(int p) {} +void foo347(int p) {} +void foo348(int p) {} +void foo349(int p) {} +void foo350(int p) {} +void foo351(int p) {} +void foo352(int p) {} +void foo353(int p) {} +void foo354(int p) {} +void foo355(int p) {} +void foo356(int p) {} +void foo357(int p) {} +void foo358(int p) {} +void foo359(int p) {} +void foo360(int p) {} +void foo361(int p) {} +void foo362(int p) {} +void foo363(int p) {} +void foo364(int p) {} +void foo365(int p) {} +void foo366(int p) {} +void foo367(int p) {} +void foo368(int p) {} +void foo369(int p) {} +void foo370(int p) {} +void foo371(int p) {} +void foo372(int p) {} +void foo373(int p) {} +void foo374(int p) {} +void foo375(int p) {} +void foo376(int p) {} +void foo377(int p) {} +void foo378(int p) {} +void foo379(int p) {} +void foo380(int p) {} +void foo381(int p) {} +void foo382(int p) {} +void foo383(int p) {} +void foo384(int p) {} +void foo385(int p) {} +void foo386(int p) {} +void foo387(int p) {} +void foo388(int p) {} +void foo389(int p) {} +void foo390(int p) {} +void foo391(int p) {} +void foo392(int p) {} +void foo393(int p) {} +void foo394(int p) {} +void foo395(int p) {} +void foo396(int p) {} +void foo397(int p) {} +void foo398(int p) {} +void foo399(int p) {} +void foo400(int p) {} +void foo401(int p) {} +void foo402(int p) {} +void foo403(int p) {} +void foo404(int p) {} +void foo405(int p) {} +void foo406(int p) {} +void foo407(int p) {} +void foo408(int p) {} +void foo409(int p) {} +void foo410(int p) {} +void foo411(int p) {} +void foo412(int p) {} +void foo413(int p) {} +void foo414(int p) {} +void foo415(int p) {} +void foo416(int p) {} +void foo417(int p) {} +void foo418(int p) {} +void foo419(int p) {} +void foo420(int p) {} +void foo421(int p) {} +void foo422(int p) {} +void foo423(int p) {} +void foo424(int p) {} +void foo425(int p) {} +void foo426(int p) {} +void foo427(int p) {} +void foo428(int p) {} +void foo429(int p) {} +void foo430(int p) {} +void foo431(int p) {} +void foo432(int p) {} +void foo433(int p) {} +void foo434(int p) {} +void foo435(int p) {} +void foo436(int p) {} +void foo437(int p) {} +void foo438(int p) {} +void foo439(int p) {} +void foo440(int p) {} +void foo441(int p) {} +void foo442(int p) {} +void foo443(int p) {} +void foo444(int p) {} +void foo445(int p) {} +void foo446(int p) {} +void foo447(int p) {} +void foo448(int p) {} +void foo449(int p) {} +void foo450(int p) {} +void foo451(int p) {} +void foo452(int p) {} +void foo453(int p) {} +void foo454(int p) {} +void foo455(int p) {} +void foo456(int p) {} +void foo457(int p) {} +void foo458(int p) {} +void foo459(int p) {} +void foo460(int p) {} +void foo461(int p) {} +void foo462(int p) {} +void foo463(int p) {} +void foo464(int p) {} +void foo465(int p) {} +void foo466(int p) {} +void foo467(int p) {} +void foo468(int p) {} +void foo469(int p) {} +void foo470(int p) {} +void foo471(int p) {} +void foo472(int p) {} +void foo473(int p) {} +void foo474(int p) {} +void foo475(int p) {} +void foo476(int p) {} +void foo477(int p) {} +void foo478(int p) {} +void foo479(int p) {} +void foo480(int p) {} +void foo481(int p) {} +void foo482(int p) {} +void foo483(int p) {} +void foo484(int p) {} +void foo485(int p) {} +void foo486(int p) {} +void foo487(int p) {} +void foo488(int p) {} +void foo489(int p) {} +void foo490(int p) {} +void foo491(int p) {} +void foo492(int p) {} +void foo493(int p) {} +void foo494(int p) {} +void foo495(int p) {} +void foo496(int p) {} +void foo497(int p) {} +void foo498(int p) {} +void foo499(int p) {} +void foo500(int p) {} +void foo501(int p) {} +void foo502(int p) {} +void foo503(int p) {} +void foo504(int p) {} +void foo505(int p) {} +void foo506(int p) {} +void foo507(int p) {} +void foo508(int p) {} +void foo509(int p) {} +void foo510(int p) {} +void foo511(int p) {} +void foo512(int p) {} +void foo513(int p) {} +void foo514(int p) {} +void foo515(int p) {} +void foo516(int p) {} +void foo517(int p) {} +void foo518(int p) {} +void foo519(int p) {} +void foo520(int p) {} +void foo521(int p) {} +void foo522(int p) {} +void foo523(int p) {} +void foo524(int p) {} +void foo525(int p) {} +void foo526(int p) {} +void foo527(int p) {} +void foo528(int p) {} +void foo529(int p) {} +void foo530(int p) {} +void foo531(int p) {} +void foo532(int p) {} +void foo533(int p) {} +void foo534(int p) {} +void foo535(int p) {} +void foo536(int p) {} +void foo537(int p) {} +void foo538(int p) {} +void foo539(int p) {} +void foo540(int p) {} +void foo541(int p) {} +void foo542(int p) {} +void foo543(int p) {} +void foo544(int p) {} +void foo545(int p) {} +void foo546(int p) {} +void foo547(int p) {} +void foo548(int p) {} +void foo549(int p) {} +void foo550(int p) {} +void foo551(int p) {} +void foo552(int p) {} +void foo553(int p) {} +void foo554(int p) {} +void foo555(int p) {} +void foo556(int p) {} +void foo557(int p) {} +void foo558(int p) {} +void foo559(int p) {} +void foo560(int p) {} +void foo561(int p) {} +void foo562(int p) {} +void foo563(int p) {} +void foo564(int p) {} +void foo565(int p) {} +void foo566(int p) {} +void foo567(int p) {} +void foo568(int p) {} +void foo569(int p) {} +void foo570(int p) {} +void foo571(int p) {} +void foo572(int p) {} +void foo573(int p) {} +void foo574(int p) {} +void foo575(int p) {} +void foo576(int p) {} +void foo577(int p) {} +void foo578(int p) {} +void foo579(int p) {} +void foo580(int p) {} +void foo581(int p) {} +void foo582(int p) {} +void foo583(int p) {} +void foo584(int p) {} +void foo585(int p) {} +void foo586(int p) {} +void foo587(int p) {} +void foo588(int p) {} +void foo589(int p) {} +void foo590(int p) {} +void foo591(int p) {} +void foo592(int p) {} +void foo593(int p) {} +void foo594(int p) {} +void foo595(int p) {} +void foo596(int p) {} +void foo597(int p) {} +void foo598(int p) {} +void foo599(int p) {} +void foo600(int p) {} +void foo601(int p) {} +void foo602(int p) {} +void foo603(int p) {} +void foo604(int p) {} +void foo605(int p) {} +void foo606(int p) {} +void foo607(int p) {} +void foo608(int p) {} +void foo609(int p) {} +void foo610(int p) {} +void foo611(int p) {} +void foo612(int p) {} +void foo613(int p) {} +void foo614(int p) {} +void foo615(int p) {} +void foo616(int p) {} +void foo617(int p) {} +void foo618(int p) {} +void foo619(int p) {} +void foo620(int p) {} +void foo621(int p) {} +void foo622(int p) {} +void foo623(int p) {} +void foo624(int p) {} +void foo625(int p) {} +void foo626(int p) {} +void foo627(int p) {} +void foo628(int p) {} +void foo629(int p) {} +void foo630(int p) {} +void foo631(int p) {} +void foo632(int p) {} +void foo633(int p) {} +void foo634(int p) {} +void foo635(int p) {} +void foo636(int p) {} +void foo637(int p) {} +void foo638(int p) {} +void foo639(int p) {} +void foo640(int p) {} +void foo641(int p) {} +void foo642(int p) {} +void foo643(int p) {} +void foo644(int p) {} +void foo645(int p) {} +void foo646(int p) {} +void foo647(int p) {} +void foo648(int p) {} +void foo649(int p) {} +void foo650(int p) {} +void foo651(int p) {} +void foo652(int p) {} +void foo653(int p) {} +void foo654(int p) {} +void foo655(int p) {} +void foo656(int p) {} +void foo657(int p) {} +void foo658(int p) {} +void foo659(int p) {} +void foo660(int p) {} +void foo661(int p) {} +void foo662(int p) {} +void foo663(int p) {} +void foo664(int p) {} +void foo665(int p) {} +void foo666(int p) {} +void foo667(int p) {} +void foo668(int p) {} +void foo669(int p) {} +void foo670(int p) {} +void foo671(int p) {} +void foo672(int p) {} +void foo673(int p) {} +void foo674(int p) {} +void foo675(int p) {} +void foo676(int p) {} +void foo677(int p) {} +void foo678(int p) {} +void foo679(int p) {} +void foo680(int p) {} +void foo681(int p) {} +void foo682(int p) {} +void foo683(int p) {} +void foo684(int p) {} +void foo685(int p) {} +void foo686(int p) {} +void foo687(int p) {} +void foo688(int p) {} +void foo689(int p) {} +void foo690(int p) {} +void foo691(int p) {} +void foo692(int p) {} +void foo693(int p) {} +void foo694(int p) {} +void foo695(int p) {} +void foo696(int p) {} +void foo697(int p) {} +void foo698(int p) {} +void foo699(int p) {} +void foo700(int p) {} +void foo701(int p) {} +void foo702(int p) {} +void foo703(int p) {} +void foo704(int p) {} +void foo705(int p) {} +void foo706(int p) {} +void foo707(int p) {} +void foo708(int p) {} +void foo709(int p) {} +void foo710(int p) {} +void foo711(int p) {} +void foo712(int p) {} +void foo713(int p) {} +void foo714(int p) {} +void foo715(int p) {} +void foo716(int p) {} +void foo717(int p) {} +void foo718(int p) {} +void foo719(int p) {} +void foo720(int p) {} +void foo721(int p) {} +void foo722(int p) {} +void foo723(int p) {} +void foo724(int p) {} +void foo725(int p) {} +void foo726(int p) {} +void foo727(int p) {} +void foo728(int p) {} +void foo729(int p) {} +void foo730(int p) {} +void foo731(int p) {} +void foo732(int p) {} +void foo733(int p) {} +void foo734(int p) {} +void foo735(int p) {} +void foo736(int p) {} +void foo737(int p) {} +void foo738(int p) {} +void foo739(int p) {} +void foo740(int p) {} +void foo741(int p) {} +void foo742(int p) {} +void foo743(int p) {} +void foo744(int p) {} +void foo745(int p) {} +void foo746(int p) {} +void foo747(int p) {} +void foo748(int p) {} +void foo749(int p) {} +void foo750(int p) {} +void foo751(int p) {} +void foo752(int p) {} +void foo753(int p) {} +void foo754(int p) {} +void foo755(int p) {} +void foo756(int p) {} +void foo757(int p) {} +void foo758(int p) {} +void foo759(int p) {} +void foo760(int p) {} +void foo761(int p) {} +void foo762(int p) {} +void foo763(int p) {} +void foo764(int p) {} +void foo765(int p) {} +void foo766(int p) {} +void foo767(int p) {} +void foo768(int p) {} +void foo769(int p) {} +void foo770(int p) {} +void foo771(int p) {} +void foo772(int p) {} +void foo773(int p) {} +void foo774(int p) {} +void foo775(int p) {} +void foo776(int p) {} +void foo777(int p) {} +void foo778(int p) {} +void foo779(int p) {} +void foo780(int p) {} +void foo781(int p) {} +void foo782(int p) {} +void foo783(int p) {} +void foo784(int p) {} +void foo785(int p) {} +void foo786(int p) {} +void foo787(int p) {} +void foo788(int p) {} +void foo789(int p) {} +void foo790(int p) {} +void foo791(int p) {} +void foo792(int p) {} +void foo793(int p) {} +void foo794(int p) {} +void foo795(int p) {} +void foo796(int p) {} +void foo797(int p) {} +void foo798(int p) {} +void foo799(int p) {} +void foo800(int p) {} +void foo801(int p) {} +void foo802(int p) {} +void foo803(int p) {} +void foo804(int p) {} +void foo805(int p) {} +void foo806(int p) {} +void foo807(int p) {} +void foo808(int p) {} +void foo809(int p) {} +void foo810(int p) {} +void foo811(int p) {} +void foo812(int p) {} +void foo813(int p) {} +void foo814(int p) {} +void foo815(int p) {} +void foo816(int p) {} +void foo817(int p) {} +void foo818(int p) {} +void foo819(int p) {} +void foo820(int p) {} +void foo821(int p) {} +void foo822(int p) {} +void foo823(int p) {} +void foo824(int p) {} diff --git a/dyld/unit-tests/test-cases/jump-table-race/foo.h b/dyld/unit-tests/test-cases/jump-table-race/foo.h new file mode 100644 index 0000000..33a9be4 --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-race/foo.h @@ -0,0 +1,825 @@ +extern void foo000(int); +extern void foo001(int); +extern void foo002(int); +extern void foo003(int); +extern void foo004(int); +extern void foo005(int); +extern void foo006(int); +extern void foo007(int); +extern void foo008(int); +extern void foo009(int); +extern void foo010(int); +extern void foo011(int); +extern void foo012(int); +extern void foo013(int); +extern void foo014(int); +extern void foo015(int); +extern void foo016(int); +extern void foo017(int); +extern void foo018(int); +extern void foo019(int); +extern void foo020(int); +extern void foo021(int); +extern void foo022(int); +extern void foo023(int); +extern void foo024(int); +extern void foo025(int); +extern void foo026(int); +extern void foo027(int); +extern void foo028(int); +extern void foo029(int); +extern void foo030(int); +extern void foo031(int); +extern void foo032(int); +extern void foo033(int); +extern void foo034(int); +extern void foo035(int); +extern void foo036(int); +extern void foo037(int); +extern void foo038(int); +extern void foo039(int); +extern void foo040(int); +extern void foo041(int); +extern void foo042(int); +extern void foo043(int); +extern void foo044(int); +extern void foo045(int); +extern void foo046(int); +extern void foo047(int); +extern void foo048(int); +extern void foo049(int); +extern void foo050(int); +extern void foo051(int); +extern void foo052(int); +extern void foo053(int); +extern void foo054(int); +extern void foo055(int); +extern void foo056(int); +extern void foo057(int); +extern void foo058(int); +extern void foo059(int); +extern void foo060(int); +extern void foo061(int); +extern void foo062(int); +extern void foo063(int); +extern void foo064(int); +extern void foo065(int); +extern void foo066(int); +extern void foo067(int); +extern void foo068(int); +extern void foo069(int); +extern void foo070(int); +extern void foo071(int); +extern void foo072(int); +extern void foo073(int); +extern void foo074(int); +extern void foo075(int); +extern void foo076(int); +extern void foo077(int); +extern void foo078(int); +extern void foo079(int); +extern void foo080(int); +extern void foo081(int); +extern void foo082(int); +extern void foo083(int); +extern void foo084(int); +extern void foo085(int); +extern void foo086(int); +extern void foo087(int); +extern void foo088(int); +extern void foo089(int); +extern void foo090(int); +extern void foo091(int); +extern void foo092(int); +extern void foo093(int); +extern void foo094(int); +extern void foo095(int); +extern void foo096(int); +extern void foo097(int); +extern void foo098(int); +extern void foo099(int); +extern void foo100(int); +extern void foo101(int); +extern void foo102(int); +extern void foo103(int); +extern void foo104(int); +extern void foo105(int); +extern void foo106(int); +extern void foo107(int); +extern void foo108(int); +extern void foo109(int); +extern void foo110(int); +extern void foo111(int); +extern void foo112(int); +extern void foo113(int); +extern void foo114(int); +extern void foo115(int); +extern void foo116(int); +extern void foo117(int); +extern void foo118(int); +extern void foo119(int); +extern void foo120(int); +extern void foo121(int); +extern void foo122(int); +extern void foo123(int); +extern void foo124(int); +extern void foo125(int); +extern void foo126(int); +extern void foo127(int); +extern void foo128(int); +extern void foo129(int); +extern void foo130(int); +extern void foo131(int); +extern void foo132(int); +extern void foo133(int); +extern void foo134(int); +extern void foo135(int); +extern void foo136(int); +extern void foo137(int); +extern void foo138(int); +extern void foo139(int); +extern void foo140(int); +extern void foo141(int); +extern void foo142(int); +extern void foo143(int); +extern void foo144(int); +extern void foo145(int); +extern void foo146(int); +extern void foo147(int); +extern void foo148(int); +extern void foo149(int); +extern void foo150(int); +extern void foo151(int); +extern void foo152(int); +extern void foo153(int); +extern void foo154(int); +extern void foo155(int); +extern void foo156(int); +extern void foo157(int); +extern void foo158(int); +extern void foo159(int); +extern void foo160(int); +extern void foo161(int); +extern void foo162(int); +extern void foo163(int); +extern void foo164(int); +extern void foo165(int); +extern void foo166(int); +extern void foo167(int); +extern void foo168(int); +extern void foo169(int); +extern void foo170(int); +extern void foo171(int); +extern void foo172(int); +extern void foo173(int); +extern void foo174(int); +extern void foo175(int); +extern void foo176(int); +extern void foo177(int); +extern void foo178(int); +extern void foo179(int); +extern void foo180(int); +extern void foo181(int); +extern void foo182(int); +extern void foo183(int); +extern void foo184(int); +extern void foo185(int); +extern void foo186(int); +extern void foo187(int); +extern void foo188(int); +extern void foo189(int); +extern void foo190(int); +extern void foo191(int); +extern void foo192(int); +extern void foo193(int); +extern void foo194(int); +extern void foo195(int); +extern void foo196(int); +extern void foo197(int); +extern void foo198(int); +extern void foo199(int); +extern void foo200(int); +extern void foo201(int); +extern void foo202(int); +extern void foo203(int); +extern void foo204(int); +extern void foo205(int); +extern void foo206(int); +extern void foo207(int); +extern void foo208(int); +extern void foo209(int); +extern void foo210(int); +extern void foo211(int); +extern void foo212(int); +extern void foo213(int); +extern void foo214(int); +extern void foo215(int); +extern void foo216(int); +extern void foo217(int); +extern void foo218(int); +extern void foo219(int); +extern void foo220(int); +extern void foo221(int); +extern void foo222(int); +extern void foo223(int); +extern void foo224(int); +extern void foo225(int); +extern void foo226(int); +extern void foo227(int); +extern void foo228(int); +extern void foo229(int); +extern void foo230(int); +extern void foo231(int); +extern void foo232(int); +extern void foo233(int); +extern void foo234(int); +extern void foo235(int); +extern void foo236(int); +extern void foo237(int); +extern void foo238(int); +extern void foo239(int); +extern void foo240(int); +extern void foo241(int); +extern void foo242(int); +extern void foo243(int); +extern void foo244(int); +extern void foo245(int); +extern void foo246(int); +extern void foo247(int); +extern void foo248(int); +extern void foo249(int); +extern void foo250(int); +extern void foo251(int); +extern void foo252(int); +extern void foo253(int); +extern void foo254(int); +extern void foo255(int); +extern void foo256(int); +extern void foo257(int); +extern void foo258(int); +extern void foo259(int); +extern void foo260(int); +extern void foo261(int); +extern void foo262(int); +extern void foo263(int); +extern void foo264(int); +extern void foo265(int); +extern void foo266(int); +extern void foo267(int); +extern void foo268(int); +extern void foo269(int); +extern void foo270(int); +extern void foo271(int); +extern void foo272(int); +extern void foo273(int); +extern void foo274(int); +extern void foo275(int); +extern void foo276(int); +extern void foo277(int); +extern void foo278(int); +extern void foo279(int); +extern void foo280(int); +extern void foo281(int); +extern void foo282(int); +extern void foo283(int); +extern void foo284(int); +extern void foo285(int); +extern void foo286(int); +extern void foo287(int); +extern void foo288(int); +extern void foo289(int); +extern void foo290(int); +extern void foo291(int); +extern void foo292(int); +extern void foo293(int); +extern void foo294(int); +extern void foo295(int); +extern void foo296(int); +extern void foo297(int); +extern void foo298(int); +extern void foo299(int); +extern void foo300(int); +extern void foo301(int); +extern void foo302(int); +extern void foo303(int); +extern void foo304(int); +extern void foo305(int); +extern void foo306(int); +extern void foo307(int); +extern void foo308(int); +extern void foo309(int); +extern void foo310(int); +extern void foo311(int); +extern void foo312(int); +extern void foo313(int); +extern void foo314(int); +extern void foo315(int); +extern void foo316(int); +extern void foo317(int); +extern void foo318(int); +extern void foo319(int); +extern void foo320(int); +extern void foo321(int); +extern void foo322(int); +extern void foo323(int); +extern void foo324(int); +extern void foo325(int); +extern void foo326(int); +extern void foo327(int); +extern void foo328(int); +extern void foo329(int); +extern void foo330(int); +extern void foo331(int); +extern void foo332(int); +extern void foo333(int); +extern void foo334(int); +extern void foo335(int); +extern void foo336(int); +extern void foo337(int); +extern void foo338(int); +extern void foo339(int); +extern void foo340(int); +extern void foo341(int); +extern void foo342(int); +extern void foo343(int); +extern void foo344(int); +extern void foo345(int); +extern void foo346(int); +extern void foo347(int); +extern void foo348(int); +extern void foo349(int); +extern void foo350(int); +extern void foo351(int); +extern void foo352(int); +extern void foo353(int); +extern void foo354(int); +extern void foo355(int); +extern void foo356(int); +extern void foo357(int); +extern void foo358(int); +extern void foo359(int); +extern void foo360(int); +extern void foo361(int); +extern void foo362(int); +extern void foo363(int); +extern void foo364(int); +extern void foo365(int); +extern void foo366(int); +extern void foo367(int); +extern void foo368(int); +extern void foo369(int); +extern void foo370(int); +extern void foo371(int); +extern void foo372(int); +extern void foo373(int); +extern void foo374(int); +extern void foo375(int); +extern void foo376(int); +extern void foo377(int); +extern void foo378(int); +extern void foo379(int); +extern void foo380(int); +extern void foo381(int); +extern void foo382(int); +extern void foo383(int); +extern void foo384(int); +extern void foo385(int); +extern void foo386(int); +extern void foo387(int); +extern void foo388(int); +extern void foo389(int); +extern void foo390(int); +extern void foo391(int); +extern void foo392(int); +extern void foo393(int); +extern void foo394(int); +extern void foo395(int); +extern void foo396(int); +extern void foo397(int); +extern void foo398(int); +extern void foo399(int); +extern void foo400(int); +extern void foo401(int); +extern void foo402(int); +extern void foo403(int); +extern void foo404(int); +extern void foo405(int); +extern void foo406(int); +extern void foo407(int); +extern void foo408(int); +extern void foo409(int); +extern void foo410(int); +extern void foo411(int); +extern void foo412(int); +extern void foo413(int); +extern void foo414(int); +extern void foo415(int); +extern void foo416(int); +extern void foo417(int); +extern void foo418(int); +extern void foo419(int); +extern void foo420(int); +extern void foo421(int); +extern void foo422(int); +extern void foo423(int); +extern void foo424(int); +extern void foo425(int); +extern void foo426(int); +extern void foo427(int); +extern void foo428(int); +extern void foo429(int); +extern void foo430(int); +extern void foo431(int); +extern void foo432(int); +extern void foo433(int); +extern void foo434(int); +extern void foo435(int); +extern void foo436(int); +extern void foo437(int); +extern void foo438(int); +extern void foo439(int); +extern void foo440(int); +extern void foo441(int); +extern void foo442(int); +extern void foo443(int); +extern void foo444(int); +extern void foo445(int); +extern void foo446(int); +extern void foo447(int); +extern void foo448(int); +extern void foo449(int); +extern void foo450(int); +extern void foo451(int); +extern void foo452(int); +extern void foo453(int); +extern void foo454(int); +extern void foo455(int); +extern void foo456(int); +extern void foo457(int); +extern void foo458(int); +extern void foo459(int); +extern void foo460(int); +extern void foo461(int); +extern void foo462(int); +extern void foo463(int); +extern void foo464(int); +extern void foo465(int); +extern void foo466(int); +extern void foo467(int); +extern void foo468(int); +extern void foo469(int); +extern void foo470(int); +extern void foo471(int); +extern void foo472(int); +extern void foo473(int); +extern void foo474(int); +extern void foo475(int); +extern void foo476(int); +extern void foo477(int); +extern void foo478(int); +extern void foo479(int); +extern void foo480(int); +extern void foo481(int); +extern void foo482(int); +extern void foo483(int); +extern void foo484(int); +extern void foo485(int); +extern void foo486(int); +extern void foo487(int); +extern void foo488(int); +extern void foo489(int); +extern void foo490(int); +extern void foo491(int); +extern void foo492(int); +extern void foo493(int); +extern void foo494(int); +extern void foo495(int); +extern void foo496(int); +extern void foo497(int); +extern void foo498(int); +extern void foo499(int); +extern void foo500(int); +extern void foo501(int); +extern void foo502(int); +extern void foo503(int); +extern void foo504(int); +extern void foo505(int); +extern void foo506(int); +extern void foo507(int); +extern void foo508(int); +extern void foo509(int); +extern void foo510(int); +extern void foo511(int); +extern void foo512(int); +extern void foo513(int); +extern void foo514(int); +extern void foo515(int); +extern void foo516(int); +extern void foo517(int); +extern void foo518(int); +extern void foo519(int); +extern void foo520(int); +extern void foo521(int); +extern void foo522(int); +extern void foo523(int); +extern void foo524(int); +extern void foo525(int); +extern void foo526(int); +extern void foo527(int); +extern void foo528(int); +extern void foo529(int); +extern void foo530(int); +extern void foo531(int); +extern void foo532(int); +extern void foo533(int); +extern void foo534(int); +extern void foo535(int); +extern void foo536(int); +extern void foo537(int); +extern void foo538(int); +extern void foo539(int); +extern void foo540(int); +extern void foo541(int); +extern void foo542(int); +extern void foo543(int); +extern void foo544(int); +extern void foo545(int); +extern void foo546(int); +extern void foo547(int); +extern void foo548(int); +extern void foo549(int); +extern void foo550(int); +extern void foo551(int); +extern void foo552(int); +extern void foo553(int); +extern void foo554(int); +extern void foo555(int); +extern void foo556(int); +extern void foo557(int); +extern void foo558(int); +extern void foo559(int); +extern void foo560(int); +extern void foo561(int); +extern void foo562(int); +extern void foo563(int); +extern void foo564(int); +extern void foo565(int); +extern void foo566(int); +extern void foo567(int); +extern void foo568(int); +extern void foo569(int); +extern void foo570(int); +extern void foo571(int); +extern void foo572(int); +extern void foo573(int); +extern void foo574(int); +extern void foo575(int); +extern void foo576(int); +extern void foo577(int); +extern void foo578(int); +extern void foo579(int); +extern void foo580(int); +extern void foo581(int); +extern void foo582(int); +extern void foo583(int); +extern void foo584(int); +extern void foo585(int); +extern void foo586(int); +extern void foo587(int); +extern void foo588(int); +extern void foo589(int); +extern void foo590(int); +extern void foo591(int); +extern void foo592(int); +extern void foo593(int); +extern void foo594(int); +extern void foo595(int); +extern void foo596(int); +extern void foo597(int); +extern void foo598(int); +extern void foo599(int); +extern void foo600(int); +extern void foo601(int); +extern void foo602(int); +extern void foo603(int); +extern void foo604(int); +extern void foo605(int); +extern void foo606(int); +extern void foo607(int); +extern void foo608(int); +extern void foo609(int); +extern void foo610(int); +extern void foo611(int); +extern void foo612(int); +extern void foo613(int); +extern void foo614(int); +extern void foo615(int); +extern void foo616(int); +extern void foo617(int); +extern void foo618(int); +extern void foo619(int); +extern void foo620(int); +extern void foo621(int); +extern void foo622(int); +extern void foo623(int); +extern void foo624(int); +extern void foo625(int); +extern void foo626(int); +extern void foo627(int); +extern void foo628(int); +extern void foo629(int); +extern void foo630(int); +extern void foo631(int); +extern void foo632(int); +extern void foo633(int); +extern void foo634(int); +extern void foo635(int); +extern void foo636(int); +extern void foo637(int); +extern void foo638(int); +extern void foo639(int); +extern void foo640(int); +extern void foo641(int); +extern void foo642(int); +extern void foo643(int); +extern void foo644(int); +extern void foo645(int); +extern void foo646(int); +extern void foo647(int); +extern void foo648(int); +extern void foo649(int); +extern void foo650(int); +extern void foo651(int); +extern void foo652(int); +extern void foo653(int); +extern void foo654(int); +extern void foo655(int); +extern void foo656(int); +extern void foo657(int); +extern void foo658(int); +extern void foo659(int); +extern void foo660(int); +extern void foo661(int); +extern void foo662(int); +extern void foo663(int); +extern void foo664(int); +extern void foo665(int); +extern void foo666(int); +extern void foo667(int); +extern void foo668(int); +extern void foo669(int); +extern void foo670(int); +extern void foo671(int); +extern void foo672(int); +extern void foo673(int); +extern void foo674(int); +extern void foo675(int); +extern void foo676(int); +extern void foo677(int); +extern void foo678(int); +extern void foo679(int); +extern void foo680(int); +extern void foo681(int); +extern void foo682(int); +extern void foo683(int); +extern void foo684(int); +extern void foo685(int); +extern void foo686(int); +extern void foo687(int); +extern void foo688(int); +extern void foo689(int); +extern void foo690(int); +extern void foo691(int); +extern void foo692(int); +extern void foo693(int); +extern void foo694(int); +extern void foo695(int); +extern void foo696(int); +extern void foo697(int); +extern void foo698(int); +extern void foo699(int); +extern void foo700(int); +extern void foo701(int); +extern void foo702(int); +extern void foo703(int); +extern void foo704(int); +extern void foo705(int); +extern void foo706(int); +extern void foo707(int); +extern void foo708(int); +extern void foo709(int); +extern void foo710(int); +extern void foo711(int); +extern void foo712(int); +extern void foo713(int); +extern void foo714(int); +extern void foo715(int); +extern void foo716(int); +extern void foo717(int); +extern void foo718(int); +extern void foo719(int); +extern void foo720(int); +extern void foo721(int); +extern void foo722(int); +extern void foo723(int); +extern void foo724(int); +extern void foo725(int); +extern void foo726(int); +extern void foo727(int); +extern void foo728(int); +extern void foo729(int); +extern void foo730(int); +extern void foo731(int); +extern void foo732(int); +extern void foo733(int); +extern void foo734(int); +extern void foo735(int); +extern void foo736(int); +extern void foo737(int); +extern void foo738(int); +extern void foo739(int); +extern void foo740(int); +extern void foo741(int); +extern void foo742(int); +extern void foo743(int); +extern void foo744(int); +extern void foo745(int); +extern void foo746(int); +extern void foo747(int); +extern void foo748(int); +extern void foo749(int); +extern void foo750(int); +extern void foo751(int); +extern void foo752(int); +extern void foo753(int); +extern void foo754(int); +extern void foo755(int); +extern void foo756(int); +extern void foo757(int); +extern void foo758(int); +extern void foo759(int); +extern void foo760(int); +extern void foo761(int); +extern void foo762(int); +extern void foo763(int); +extern void foo764(int); +extern void foo765(int); +extern void foo766(int); +extern void foo767(int); +extern void foo768(int); +extern void foo769(int); +extern void foo770(int); +extern void foo771(int); +extern void foo772(int); +extern void foo773(int); +extern void foo774(int); +extern void foo775(int); +extern void foo776(int); +extern void foo777(int); +extern void foo778(int); +extern void foo779(int); +extern void foo780(int); +extern void foo781(int); +extern void foo782(int); +extern void foo783(int); +extern void foo784(int); +extern void foo785(int); +extern void foo786(int); +extern void foo787(int); +extern void foo788(int); +extern void foo789(int); +extern void foo790(int); +extern void foo791(int); +extern void foo792(int); +extern void foo793(int); +extern void foo794(int); +extern void foo795(int); +extern void foo796(int); +extern void foo797(int); +extern void foo798(int); +extern void foo799(int); +extern void foo800(int); +extern void foo801(int); +extern void foo802(int); +extern void foo803(int); +extern void foo804(int); +extern void foo805(int); +extern void foo806(int); +extern void foo807(int); +extern void foo808(int); +extern void foo809(int); +extern void foo810(int); +extern void foo811(int); +extern void foo812(int); +extern void foo813(int); +extern void foo814(int); +extern void foo815(int); +extern void foo816(int); +extern void foo817(int); +extern void foo818(int); +extern void foo819(int); +extern void foo820(int); +extern void foo821(int); +extern void foo822(int); +extern void foo823(int); +extern void foo824(int); diff --git a/dyld/unit-tests/test-cases/jump-table-race/main.c b/dyld/unit-tests/test-cases/jump-table-race/main.c new file mode 100644 index 0000000..202fe28 --- /dev/null +++ b/dyld/unit-tests/test-cases/jump-table-race/main.c @@ -0,0 +1,865 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(0x12345678), NULL +#include // exit(0x12345678), EXIT_SUCCESS +#include + +#include "test.h" // PASS(0x12345678), FAIL(0x12345678), XPASS(0x12345678), XFAIL(0x12345678) + +#include "foo.h" + +static void* callAll(void* p) +{ + foo002(0x12345678); + foo003(0x12345678); + foo004(0x12345678); + foo005(0x12345678); + foo006(0x12345678); + foo007(0x12345678); + foo008(0x12345678); + foo009(0x12345678); + foo010(0x12345678); + foo011(0x12345678); + foo012(0x12345678); + foo013(0x12345678); + foo014(0x12345678); + foo015(0x12345678); + foo016(0x12345678); + foo017(0x12345678); + foo018(0x12345678); + foo019(0x12345678); + foo020(0x12345678); + foo021(0x12345678); + foo022(0x12345678); + foo023(0x12345678); + foo024(0x12345678); + foo025(0x12345678); + foo026(0x12345678); + foo027(0x12345678); + foo028(0x12345678); + foo029(0x12345678); + foo030(0x12345678); + foo031(0x12345678); + foo032(0x12345678); + foo033(0x12345678); + foo034(0x12345678); + foo035(0x12345678); + foo036(0x12345678); + foo037(0x12345678); + foo038(0x12345678); + foo039(0x12345678); + foo040(0x12345678); + foo041(0x12345678); + foo042(0x12345678); + foo043(0x12345678); + foo044(0x12345678); + foo045(0x12345678); + foo046(0x12345678); + foo047(0x12345678); + foo048(0x12345678); + foo049(0x12345678); + foo050(0x12345678); + foo051(0x12345678); + foo052(0x12345678); + foo053(0x12345678); + foo054(0x12345678); + foo055(0x12345678); + foo056(0x12345678); + foo057(0x12345678); + foo058(0x12345678); + foo059(0x12345678); + foo060(0x12345678); + foo061(0x12345678); + foo062(0x12345678); + foo063(0x12345678); + foo064(0x12345678); + foo065(0x12345678); + foo066(0x12345678); + foo067(0x12345678); + foo068(0x12345678); + foo069(0x12345678); + foo070(0x12345678); + foo071(0x12345678); + foo072(0x12345678); + foo073(0x12345678); + foo074(0x12345678); + foo075(0x12345678); + foo076(0x12345678); + foo077(0x12345678); + foo078(0x12345678); + foo079(0x12345678); + foo080(0x12345678); + foo081(0x12345678); + foo082(0x12345678); + foo083(0x12345678); + foo084(0x12345678); + foo085(0x12345678); + foo086(0x12345678); + foo087(0x12345678); + foo088(0x12345678); + foo089(0x12345678); + foo090(0x12345678); + foo091(0x12345678); + foo092(0x12345678); + foo093(0x12345678); + foo094(0x12345678); + foo095(0x12345678); + foo096(0x12345678); + foo097(0x12345678); + foo098(0x12345678); + foo099(0x12345678); + foo100(0x12345678); + foo101(0x12345678); + foo102(0x12345678); + foo103(0x12345678); + foo104(0x12345678); + foo105(0x12345678); + foo106(0x12345678); + foo107(0x12345678); + foo108(0x12345678); + foo109(0x12345678); + foo110(0x12345678); + foo111(0x12345678); + foo112(0x12345678); + foo113(0x12345678); + foo114(0x12345678); + foo115(0x12345678); + foo116(0x12345678); + foo117(0x12345678); + foo118(0x12345678); + foo119(0x12345678); + foo120(0x12345678); + foo121(0x12345678); + foo122(0x12345678); + foo123(0x12345678); + foo124(0x12345678); + foo125(0x12345678); + foo126(0x12345678); + foo127(0x12345678); + foo128(0x12345678); + foo129(0x12345678); + foo130(0x12345678); + foo131(0x12345678); + foo132(0x12345678); + foo133(0x12345678); + foo134(0x12345678); + foo135(0x12345678); + foo136(0x12345678); + foo137(0x12345678); + foo138(0x12345678); + foo139(0x12345678); + foo140(0x12345678); + foo141(0x12345678); + foo142(0x12345678); + foo143(0x12345678); + foo144(0x12345678); + foo145(0x12345678); + foo146(0x12345678); + foo147(0x12345678); + foo148(0x12345678); + foo149(0x12345678); + foo150(0x12345678); + foo151(0x12345678); + foo152(0x12345678); + foo153(0x12345678); + foo154(0x12345678); + foo155(0x12345678); + foo156(0x12345678); + foo157(0x12345678); + foo158(0x12345678); + foo159(0x12345678); + foo160(0x12345678); + foo161(0x12345678); + foo162(0x12345678); + foo163(0x12345678); + foo164(0x12345678); + foo165(0x12345678); + foo166(0x12345678); + foo167(0x12345678); + foo168(0x12345678); + foo169(0x12345678); + foo170(0x12345678); + foo171(0x12345678); + foo172(0x12345678); + foo173(0x12345678); + foo174(0x12345678); + foo175(0x12345678); + foo176(0x12345678); + foo177(0x12345678); + foo178(0x12345678); + foo179(0x12345678); + foo180(0x12345678); + foo181(0x12345678); + foo182(0x12345678); + foo183(0x12345678); + foo184(0x12345678); + foo185(0x12345678); + foo186(0x12345678); + foo187(0x12345678); + foo188(0x12345678); + foo189(0x12345678); + foo190(0x12345678); + foo191(0x12345678); + foo192(0x12345678); + foo193(0x12345678); + foo194(0x12345678); + foo195(0x12345678); + foo196(0x12345678); + foo197(0x12345678); + foo198(0x12345678); + foo199(0x12345678); + foo200(0x12345678); + foo201(0x12345678); + foo202(0x12345678); + foo203(0x12345678); + foo204(0x12345678); + foo205(0x12345678); + foo206(0x12345678); + foo207(0x12345678); + foo208(0x12345678); + foo209(0x12345678); + foo210(0x12345678); + foo211(0x12345678); + foo212(0x12345678); + foo213(0x12345678); + foo214(0x12345678); + foo215(0x12345678); + foo216(0x12345678); + foo217(0x12345678); + foo218(0x12345678); + foo219(0x12345678); + foo220(0x12345678); + foo221(0x12345678); + foo222(0x12345678); + foo223(0x12345678); + foo224(0x12345678); + foo225(0x12345678); + foo226(0x12345678); + foo227(0x12345678); + foo228(0x12345678); + foo229(0x12345678); + foo230(0x12345678); + foo231(0x12345678); + foo232(0x12345678); + foo233(0x12345678); + foo234(0x12345678); + foo235(0x12345678); + foo236(0x12345678); + foo237(0x12345678); + foo238(0x12345678); + foo239(0x12345678); + foo240(0x12345678); + foo241(0x12345678); + foo242(0x12345678); + foo243(0x12345678); + foo244(0x12345678); + foo245(0x12345678); + foo246(0x12345678); + foo247(0x12345678); + foo248(0x12345678); + foo249(0x12345678); + foo250(0x12345678); + foo251(0x12345678); + foo252(0x12345678); + foo253(0x12345678); + foo254(0x12345678); + foo255(0x12345678); + foo256(0x12345678); + foo257(0x12345678); + foo258(0x12345678); + foo259(0x12345678); + foo260(0x12345678); + foo261(0x12345678); + foo262(0x12345678); + foo263(0x12345678); + foo264(0x12345678); + foo265(0x12345678); + foo266(0x12345678); + foo267(0x12345678); + foo268(0x12345678); + foo269(0x12345678); + foo270(0x12345678); + foo271(0x12345678); + foo272(0x12345678); + foo273(0x12345678); + foo274(0x12345678); + foo275(0x12345678); + foo276(0x12345678); + foo277(0x12345678); + foo278(0x12345678); + foo279(0x12345678); + foo280(0x12345678); + foo281(0x12345678); + foo282(0x12345678); + foo283(0x12345678); + foo284(0x12345678); + foo285(0x12345678); + foo286(0x12345678); + foo287(0x12345678); + foo288(0x12345678); + foo289(0x12345678); + foo290(0x12345678); + foo291(0x12345678); + foo292(0x12345678); + foo293(0x12345678); + foo294(0x12345678); + foo295(0x12345678); + foo296(0x12345678); + foo297(0x12345678); + foo298(0x12345678); + foo299(0x12345678); + foo300(0x12345678); + foo301(0x12345678); + foo302(0x12345678); + foo303(0x12345678); + foo304(0x12345678); + foo305(0x12345678); + foo306(0x12345678); + foo307(0x12345678); + foo308(0x12345678); + foo309(0x12345678); + foo310(0x12345678); + foo311(0x12345678); + foo312(0x12345678); + foo313(0x12345678); + foo314(0x12345678); + foo315(0x12345678); + foo316(0x12345678); + foo317(0x12345678); + foo318(0x12345678); + foo319(0x12345678); + foo320(0x12345678); + foo321(0x12345678); + foo322(0x12345678); + foo323(0x12345678); + foo324(0x12345678); + foo325(0x12345678); + foo326(0x12345678); + foo327(0x12345678); + foo328(0x12345678); + foo329(0x12345678); + foo330(0x12345678); + foo331(0x12345678); + foo332(0x12345678); + foo333(0x12345678); + foo334(0x12345678); + foo335(0x12345678); + foo336(0x12345678); + foo337(0x12345678); + foo338(0x12345678); + foo339(0x12345678); + foo340(0x12345678); + foo341(0x12345678); + foo342(0x12345678); + foo343(0x12345678); + foo344(0x12345678); + foo345(0x12345678); + foo346(0x12345678); + foo347(0x12345678); + foo348(0x12345678); + foo349(0x12345678); + foo350(0x12345678); + foo351(0x12345678); + foo352(0x12345678); + foo353(0x12345678); + foo354(0x12345678); + foo355(0x12345678); + foo356(0x12345678); + foo357(0x12345678); + foo358(0x12345678); + foo359(0x12345678); + foo360(0x12345678); + foo361(0x12345678); + foo362(0x12345678); + foo363(0x12345678); + foo364(0x12345678); + foo365(0x12345678); + foo366(0x12345678); + foo367(0x12345678); + foo368(0x12345678); + foo369(0x12345678); + foo370(0x12345678); + foo371(0x12345678); + foo372(0x12345678); + foo373(0x12345678); + foo374(0x12345678); + foo375(0x12345678); + foo376(0x12345678); + foo377(0x12345678); + foo378(0x12345678); + foo379(0x12345678); + foo380(0x12345678); + foo381(0x12345678); + foo382(0x12345678); + foo383(0x12345678); + foo384(0x12345678); + foo385(0x12345678); + foo386(0x12345678); + foo387(0x12345678); + foo388(0x12345678); + foo389(0x12345678); + foo390(0x12345678); + foo391(0x12345678); + foo392(0x12345678); + foo393(0x12345678); + foo394(0x12345678); + foo395(0x12345678); + foo396(0x12345678); + foo397(0x12345678); + foo398(0x12345678); + foo399(0x12345678); + foo400(0x12345678); + foo401(0x12345678); + foo402(0x12345678); + foo403(0x12345678); + foo404(0x12345678); + foo405(0x12345678); + foo406(0x12345678); + foo407(0x12345678); + foo408(0x12345678); + foo409(0x12345678); + foo410(0x12345678); + foo411(0x12345678); + foo412(0x12345678); + foo413(0x12345678); + foo414(0x12345678); + foo415(0x12345678); + foo416(0x12345678); + foo417(0x12345678); + foo418(0x12345678); + foo419(0x12345678); + foo420(0x12345678); + foo421(0x12345678); + foo422(0x12345678); + foo423(0x12345678); + foo424(0x12345678); + foo425(0x12345678); + foo426(0x12345678); + foo427(0x12345678); + foo428(0x12345678); + foo429(0x12345678); + foo430(0x12345678); + foo431(0x12345678); + foo432(0x12345678); + foo433(0x12345678); + foo434(0x12345678); + foo435(0x12345678); + foo436(0x12345678); + foo437(0x12345678); + foo438(0x12345678); + foo439(0x12345678); + foo440(0x12345678); + foo441(0x12345678); + foo442(0x12345678); + foo443(0x12345678); + foo444(0x12345678); + foo445(0x12345678); + foo446(0x12345678); + foo447(0x12345678); + foo448(0x12345678); + foo449(0x12345678); + foo450(0x12345678); + foo451(0x12345678); + foo452(0x12345678); + foo453(0x12345678); + foo454(0x12345678); + foo455(0x12345678); + foo456(0x12345678); + foo457(0x12345678); + foo458(0x12345678); + foo459(0x12345678); + foo460(0x12345678); + foo461(0x12345678); + foo462(0x12345678); + foo463(0x12345678); + foo464(0x12345678); + foo465(0x12345678); + foo466(0x12345678); + foo467(0x12345678); + foo468(0x12345678); + foo469(0x12345678); + foo470(0x12345678); + foo471(0x12345678); + foo472(0x12345678); + foo473(0x12345678); + foo474(0x12345678); + foo475(0x12345678); + foo476(0x12345678); + foo477(0x12345678); + foo478(0x12345678); + foo479(0x12345678); + foo480(0x12345678); + foo481(0x12345678); + foo482(0x12345678); + foo483(0x12345678); + foo484(0x12345678); + foo485(0x12345678); + foo486(0x12345678); + foo487(0x12345678); + foo488(0x12345678); + foo489(0x12345678); + foo490(0x12345678); + foo491(0x12345678); + foo492(0x12345678); + foo493(0x12345678); + foo494(0x12345678); + foo495(0x12345678); + foo496(0x12345678); + foo497(0x12345678); + foo498(0x12345678); + foo499(0x12345678); + foo500(0x12345678); + foo501(0x12345678); + foo502(0x12345678); + foo503(0x12345678); + foo504(0x12345678); + foo505(0x12345678); + foo506(0x12345678); + foo507(0x12345678); + foo508(0x12345678); + foo509(0x12345678); + foo510(0x12345678); + foo511(0x12345678); + foo512(0x12345678); + foo513(0x12345678); + foo514(0x12345678); + foo515(0x12345678); + foo516(0x12345678); + foo517(0x12345678); + foo518(0x12345678); + foo519(0x12345678); + foo520(0x12345678); + foo521(0x12345678); + foo522(0x12345678); + foo523(0x12345678); + foo524(0x12345678); + foo525(0x12345678); + foo526(0x12345678); + foo527(0x12345678); + foo528(0x12345678); + foo529(0x12345678); + foo530(0x12345678); + foo531(0x12345678); + foo532(0x12345678); + foo533(0x12345678); + foo534(0x12345678); + foo535(0x12345678); + foo536(0x12345678); + foo537(0x12345678); + foo538(0x12345678); + foo539(0x12345678); + foo540(0x12345678); + foo541(0x12345678); + foo542(0x12345678); + foo543(0x12345678); + foo544(0x12345678); + foo545(0x12345678); + foo546(0x12345678); + foo547(0x12345678); + foo548(0x12345678); + foo549(0x12345678); + foo550(0x12345678); + foo551(0x12345678); + foo552(0x12345678); + foo553(0x12345678); + foo554(0x12345678); + foo555(0x12345678); + foo556(0x12345678); + foo557(0x12345678); + foo558(0x12345678); + foo559(0x12345678); + foo560(0x12345678); + foo561(0x12345678); + foo562(0x12345678); + foo563(0x12345678); + foo564(0x12345678); + foo565(0x12345678); + foo566(0x12345678); + foo567(0x12345678); + foo568(0x12345678); + foo569(0x12345678); + foo570(0x12345678); + foo571(0x12345678); + foo572(0x12345678); + foo573(0x12345678); + foo574(0x12345678); + foo575(0x12345678); + foo576(0x12345678); + foo577(0x12345678); + foo578(0x12345678); + foo579(0x12345678); + foo580(0x12345678); + foo581(0x12345678); + foo582(0x12345678); + foo583(0x12345678); + foo584(0x12345678); + foo585(0x12345678); + foo586(0x12345678); + foo587(0x12345678); + foo588(0x12345678); + foo589(0x12345678); + foo590(0x12345678); + foo591(0x12345678); + foo592(0x12345678); + foo593(0x12345678); + foo594(0x12345678); + foo595(0x12345678); + foo596(0x12345678); + foo597(0x12345678); + foo598(0x12345678); + foo599(0x12345678); + foo600(0x12345678); + foo601(0x12345678); + foo602(0x12345678); + foo603(0x12345678); + foo604(0x12345678); + foo605(0x12345678); + foo606(0x12345678); + foo607(0x12345678); + foo608(0x12345678); + foo609(0x12345678); + foo610(0x12345678); + foo611(0x12345678); + foo612(0x12345678); + foo613(0x12345678); + foo614(0x12345678); + foo615(0x12345678); + foo616(0x12345678); + foo617(0x12345678); + foo618(0x12345678); + foo619(0x12345678); + foo620(0x12345678); + foo621(0x12345678); + foo622(0x12345678); + foo623(0x12345678); + foo624(0x12345678); + foo625(0x12345678); + foo626(0x12345678); + foo627(0x12345678); + foo628(0x12345678); + foo629(0x12345678); + foo630(0x12345678); + foo631(0x12345678); + foo632(0x12345678); + foo633(0x12345678); + foo634(0x12345678); + foo635(0x12345678); + foo636(0x12345678); + foo637(0x12345678); + foo638(0x12345678); + foo639(0x12345678); + foo640(0x12345678); + foo641(0x12345678); + foo642(0x12345678); + foo643(0x12345678); + foo644(0x12345678); + foo645(0x12345678); + foo646(0x12345678); + foo647(0x12345678); + foo648(0x12345678); + foo649(0x12345678); + foo650(0x12345678); + foo651(0x12345678); + foo652(0x12345678); + foo653(0x12345678); + foo654(0x12345678); + foo655(0x12345678); + foo656(0x12345678); + foo657(0x12345678); + foo658(0x12345678); + foo659(0x12345678); + foo660(0x12345678); + foo661(0x12345678); + foo662(0x12345678); + foo663(0x12345678); + foo664(0x12345678); + foo665(0x12345678); + foo666(0x12345678); + foo667(0x12345678); + foo668(0x12345678); + foo669(0x12345678); + foo670(0x12345678); + foo671(0x12345678); + foo672(0x12345678); + foo673(0x12345678); + foo674(0x12345678); + foo675(0x12345678); + foo676(0x12345678); + foo677(0x12345678); + foo678(0x12345678); + foo679(0x12345678); + foo680(0x12345678); + foo681(0x12345678); + foo682(0x12345678); + foo683(0x12345678); + foo684(0x12345678); + foo685(0x12345678); + foo686(0x12345678); + foo687(0x12345678); + foo688(0x12345678); + foo689(0x12345678); + foo690(0x12345678); + foo691(0x12345678); + foo692(0x12345678); + foo693(0x12345678); + foo694(0x12345678); + foo695(0x12345678); + foo696(0x12345678); + foo697(0x12345678); + foo698(0x12345678); + foo699(0x12345678); + foo700(0x12345678); + foo701(0x12345678); + foo702(0x12345678); + foo703(0x12345678); + foo704(0x12345678); + foo705(0x12345678); + foo706(0x12345678); + foo707(0x12345678); + foo708(0x12345678); + foo709(0x12345678); + foo710(0x12345678); + foo711(0x12345678); + foo712(0x12345678); + foo713(0x12345678); + foo714(0x12345678); + foo715(0x12345678); + foo716(0x12345678); + foo717(0x12345678); + foo718(0x12345678); + foo719(0x12345678); + foo720(0x12345678); + foo721(0x12345678); + foo722(0x12345678); + foo723(0x12345678); + foo724(0x12345678); + foo725(0x12345678); + foo726(0x12345678); + foo727(0x12345678); + foo728(0x12345678); + foo729(0x12345678); + foo730(0x12345678); + foo731(0x12345678); + foo732(0x12345678); + foo733(0x12345678); + foo734(0x12345678); + foo735(0x12345678); + foo736(0x12345678); + foo737(0x12345678); + foo738(0x12345678); + foo739(0x12345678); + foo740(0x12345678); + foo741(0x12345678); + foo742(0x12345678); + foo743(0x12345678); + foo744(0x12345678); + foo745(0x12345678); + foo746(0x12345678); + foo747(0x12345678); + foo748(0x12345678); + foo749(0x12345678); + foo750(0x12345678); + foo751(0x12345678); + foo752(0x12345678); + foo753(0x12345678); + foo754(0x12345678); + foo755(0x12345678); + foo756(0x12345678); + foo757(0x12345678); + foo758(0x12345678); + foo759(0x12345678); + foo760(0x12345678); + foo761(0x12345678); + foo762(0x12345678); + foo763(0x12345678); + foo764(0x12345678); + foo765(0x12345678); + foo766(0x12345678); + foo767(0x12345678); + foo768(0x12345678); + foo769(0x12345678); + foo770(0x12345678); + foo771(0x12345678); + foo772(0x12345678); + foo773(0x12345678); + foo774(0x12345678); + foo775(0x12345678); + foo776(0x12345678); + foo777(0x12345678); + foo778(0x12345678); + foo779(0x12345678); + foo780(0x12345678); + foo781(0x12345678); + foo782(0x12345678); + foo783(0x12345678); + foo784(0x12345678); + foo785(0x12345678); + foo786(0x12345678); + foo787(0x12345678); + foo788(0x12345678); + foo789(0x12345678); + foo790(0x12345678); + foo791(0x12345678); + foo792(0x12345678); + foo793(0x12345678); + foo794(0x12345678); + foo795(0x12345678); + foo796(0x12345678); + foo797(0x12345678); + foo798(0x12345678); + foo799(0x12345678); + foo800(0x12345678); + foo801(0x12345678); + foo802(0x12345678); + foo803(0x12345678); + foo804(0x12345678); + foo805(0x12345678); + foo806(0x12345678); + foo807(0x12345678); + foo808(0x12345678); + foo809(0x12345678); + foo810(0x12345678); + foo811(0x12345678); + foo812(0x12345678); + foo813(0x12345678); + foo814(0x12345678); + foo815(0x12345678); + foo816(0x12345678); + foo817(0x12345678); + return NULL; +} + +int main() +{ + // call all foo's on another thread + pthread_t otherThread; + pthread_create(&otherThread, NULL, &callAll, NULL); + + // call all foo's on this thread + callAll(NULL); + + PASS("jump-table-race"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/lazy-binding-reg-params/Makefile b/dyld/unit-tests/test-cases/lazy-binding-reg-params/Makefile new file mode 100644 index 0000000..73ce203 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-binding-reg-params/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +MACHINE = $(shell machine) + +ifeq "ppc" "$(ARCH)" + EXTRA_FLAG = -maltivec -force_cpusubtype_ALL +else + ifeq "ppc64" "$(ARCH)" + EXTRA_FLAG = -maltivec + else + ifeq "i386" "$(ARCH)" + EXTRA_FLAG = "" + else + ifeq "" "$(ARCH)" + ifeq "ppc970" "$(MACHINE)" + EXTRA_FLAG = -maltivec -force_cpusubtype_ALL + endif + endif + endif + endif +endif + + + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib ${EXTRA_FLAG} + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib ${EXTRA_FLAG} + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/lazy-binding-reg-params/foo.c b/dyld/unit-tests/test-cases/lazy-binding-reg-params/foo.c new file mode 100644 index 0000000..e2683b3 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-binding-reg-params/foo.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2005 Apple Computer p1, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "foo.h" + + +#if __i386__ +__attribute__((regparm(3))) +#endif +bool dointtest(int p1, int p2, int p3, int p4, int p5) +{ + if ( p1 != 123) + return false; + if ( p2 != 456) + return false; + if ( p3 != 789) + return false; + if ( p4 != 4444) + return false; + if ( p5 != 55555) + return false; + return true; +} + +#if __ppc__ || __ppc64__ +bool dofloattest(double p1, double p2, double p3, double p4, double p5, double p6, double p7, + double p8, double p9, double p10, double p11, double p12, double p13) +{ + if ( p1 != 1.0 ) + return false; + if ( p2 != 2.0 ) + return false; + if ( p3 != 3.0 ) + return false; + if ( p4 != 4.0 ) + return false; + if ( p5 != 5.0 ) + return false; + if ( p6 != 6.0 ) + return false; + if ( p7 != 7.0 ) + return false; + if ( p8 != 8.0 ) + return false; + if ( p9 != 9.0 ) + return false; + if ( p10 != 10.0) + return false; + if ( p11 != 11.0) + return false; + if ( p12 != 12.0) + return false; + if ( p13 != 13.0) + return false; + return true; +} +#endif + + + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + +static bool comparevFloat(vFloat p1, vFloat p2) +{ + return (memcmp(&p1, &p2, 16) == 0); +} + +bool dovectortest(vFloat p1, vFloat p2, vFloat p3, vFloat p4, vFloat p5) +{ + vFloat r1 = { 1.1, 1.2, 1.3, 1.4 }; + vFloat r2 = { 2.1, 2.2, 2.3, 2.4 }; + vFloat r3 = { 3.1, 3.2, 3.3, 3.4 }; + vFloat r4 = { 4.1, 4.2, 4.3, 4.4 }; + vFloat r5 = { 5.1, 5.2, 5.3, 5.4 }; + + if ( !comparevFloat(p1, r1) ) + return false; + if ( !comparevFloat(p2, r2) ) + return false; + if ( !comparevFloat(p3, r3) ) + return false; + if ( !comparevFloat(p4, r4) ) + return false; + if ( !comparevFloat(p5, r5) ) + return false; + return true; +} + +#endif + + + + diff --git a/dyld/unit-tests/test-cases/lazy-binding-reg-params/foo.h b/dyld/unit-tests/test-cases/lazy-binding-reg-params/foo.h new file mode 100644 index 0000000..8b76407 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-binding-reg-params/foo.h @@ -0,0 +1,25 @@ +#include // fprintf(), NULL + + +extern +#if __i386__ +__attribute__((regparm(3))) +#endif +bool dointtest(int p1, int p2, int p3, int p4, int p5); + +#if __ppc__ || __ppc64__ +extern bool dofloattest(double,double,double,double,double,double,double,double,double,double,double,double,double); +#endif + + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + + #if __i386__ || __x86_64__ + typedef float vFloat __attribute__ ((__vector_size__ (16))); + #elif __ppc__ || __ppc64__ + typedef __vector float vFloat; + #endif + + extern bool dovectortest(vFloat, vFloat, vFloat, vFloat, vFloat); + +#endif \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/lazy-binding-reg-params/main.c b/dyld/unit-tests/test-cases/lazy-binding-reg-params/main.c new file mode 100644 index 0000000..fc66bd1 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-binding-reg-params/main.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +/// +/// rdar://problem/4132378 support __attribute__((regparm())) +/// +/// The stub binding helper for dyld needs to preserve registers +/// + + +#include "foo.h" + + +static bool inttest() +{ + return dointtest(123,456,789,4444,55555); +} + +static bool floattest() +{ +#if __ppc__ || __ppc64__ + return dofloattest(1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0); +#elif __i386__ || __x86_64__ || __arm__ + return true; +#else + #error unknown arch +#endif +} + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ +static bool vectorSupport() +{ +#if __ppc__ + uint32_t value; + size_t size=sizeof(uint32_t); + int err = sysctlbyname("hw.optional.altivec", &value, &size, NULL, 0); + //fprintf(stderr, "sysctlbyname() = %d\n", err); + return ( err == 0 ); +#else + return true; +#endif +} + +static bool vectortest() +{ + vFloat p1 = { 1.1, 1.2, 1.3, 1.4 }; + vFloat p2 = { 2.1, 2.2, 2.3, 2.4 }; + vFloat p3 = { 3.1, 3.2, 3.3, 3.4 }; + vFloat p4 = { 4.1, 4.2, 4.3, 4.4 }; + vFloat p5 = { 5.1, 5.2, 5.3, 5.4 }; + return dovectortest(p1, p2, p3, p4, p5); +} +#endif + +int main() +{ + if ( ! inttest() ) { + FAIL("lazy-binding-reg-params int parameters"); + return EXIT_SUCCESS; + } + + if ( ! floattest() ) { + FAIL("lazy-binding-reg-params float parameters"); + return EXIT_SUCCESS; + } + +#if __i386__ || __x86_64__ || __ppc__ || __ppc64__ + if ( vectorSupport() && ! vectortest() ) { + FAIL("lazy-binding-reg-params vector parameters"); + return EXIT_SUCCESS; + } +#endif + + PASS("lazy-binding-reg-params"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/lazy-dylib-init-order/Makefile b/dyld/unit-tests/test-cases/lazy-dylib-init-order/Makefile new file mode 100644 index 0000000..f3e0ed5 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-init-order/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib has initializers run in correct order +# + +all-check: all check + +check: + ./main > actual.out + ${PASS_IFF} diff expected.out actual.out + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + ${CC} ${CCFLAGS} main.c -Wl,-lazy_library,libfoo.dylib -o main + + +clean: + rm -f libfoo.dylib main actual.out diff --git a/dyld/unit-tests/test-cases/lazy-dylib-init-order/expected.out b/dyld/unit-tests/test-cases/lazy-dylib-init-order/expected.out new file mode 100644 index 0000000..3302190 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-init-order/expected.out @@ -0,0 +1,6 @@ +main_init +main +foo_init +foo +foo_term +main_term diff --git a/dyld/unit-tests/test-cases/lazy-dylib-init-order/foo.c b/dyld/unit-tests/test-cases/lazy-dylib-init-order/foo.c new file mode 100644 index 0000000..1047dce --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-init-order/foo.c @@ -0,0 +1,21 @@ + +#include + +int foo() +{ + printf("foo\n"); + return 1; +} +int bar() { return 1; } + + +static __attribute__((constructor)) void foo_init() +{ + printf("foo_init\n"); +} + +static __attribute__((destructor)) void foo_term() +{ + printf("foo_term\n"); +} + diff --git a/dyld/unit-tests/test-cases/lazy-dylib-init-order/main.c b/dyld/unit-tests/test-cases/lazy-dylib-init-order/main.c new file mode 100644 index 0000000..06b33ce --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-init-order/main.c @@ -0,0 +1,25 @@ +#include +#include + + +extern int foo(); +extern int bar(); + +static __attribute__((constructor)) void main_init() +{ + printf("main_init\n"); +} + +static __attribute__((destructor)) void main_term() +{ + printf("main_term\n"); +} + + +int main() +{ + printf("main\n"); + foo(); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile b/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile new file mode 100644 index 0000000..4cddd76 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib that can't be found errors out cleanly +# + +all-check: all check + +check: + ./main + ./main2 + +all: + ${CC} ${CCFLAGS} foo.c -DBAR=1 -dynamiclib -o libfoo.dylib.full -install_name libfoo.dylib + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -Wl,-lazy_library,libfoo.dylib.full -o main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -DLAZY_HANDLER=1 -Wl,-lazy_library,libfoo.dylib.full -o main2 + + +clean: + rm -f libfoo.dylib.full main main2 diff --git a/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c b/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c new file mode 100644 index 0000000..f41dbba --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/foo.c @@ -0,0 +1,6 @@ + + +int foo() +{ + return 1; +} diff --git a/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c b/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c new file mode 100644 index 0000000..9ff0032 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-missing-dylib/main.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +#if LAZY_HANDLER +// If a linkage unit defines dyld_lazy_dylib_proxy() +// then when binding lazy symbols, if one +// cannot be bound, it is bound to this function +// instead. +int dyld_lazy_dylib_proxy() +{ + return 7; +} +#endif + + +extern int foo(); // should not exist + +int main() +{ +#if LAZY_HANDLER + // sanity check that foo was not found + // dyld_lazy_dylib_proxy returns 7 when function cannot be found + if ( foo() != 7 ) { + FAIL("lazy-dylib-missing-dylib: dyld_lazy_dylib_proxy() not used for foo"); + return 0; + } +#else + // sanity check that foo was not found + // lazy loading return 0 when function cannot be found + if ( foo() != 0 ) { + FAIL("lazy-dylib-missing-dylib: foo found"); + return 0; + } +#endif + PASS("lazy-dylib-missing-dylib"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile b/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile new file mode 100644 index 0000000..cd631ce --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Verify that a lazy loaded dylib has initializers run in correct order +# + +all-check: all check + +check: + ./main + ./main2 + +all: + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -install_name libfoo.dylib + ${CC} ${CCFLAGS} foo.c -DBAR=1 -dynamiclib -o libfoo.dylib.full -install_name libfoo.dylib + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -Wl,-lazy_library,libfoo.dylib.full -o main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include -DLAZY_HANDLER=1 -Wl,-lazy_library,libfoo.dylib.full -o main2 + + +clean: + rm -f libfoo.dylib libfoo.dylib.full main main2 diff --git a/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c b/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c new file mode 100644 index 0000000..2c56b39 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/foo.c @@ -0,0 +1,15 @@ + + +int foo() +{ + return 1; +} + +#if BAR +int bar() +{ + return 1; +} +#endif + + diff --git a/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c b/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c new file mode 100644 index 0000000..934e48a --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-dylib-missing-symbol/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +#if LAZY_HANDLER +// If a linkage unit defines dyld_lazy_dylib_proxy() +// then when binding lazy symbols, if one +// cannot be bound, it is bound to this function +// instead. +int dyld_lazy_dylib_proxy() +{ + return 7; +} +#endif + + +extern int foo(); // should exist +extern int bar(); // should not exist + +int main() +{ + // sanity check that foo was found + if ( foo() != 1 ) { + FAIL("lazy-dylib-missing-symbol: foo not found"); + return 0; + } + +#if LAZY_HANDLER + // sanity check that bar was not found + // lazy loading return 7 when function cannot be found + if ( bar() != 7 ) { + FAIL("lazy-dylib-missing-symbol: dyld_lazy_dylib_proxy() not used for bar"); + return 0; + } +#else + // sanity check that bar was not found + // lazy loading return 0 when function cannot be found + if ( bar() != 0 ) { + FAIL("lazy-dylib-missing-symbol: bar found"); + return 0; + } +#endif + PASS("lazy-dylib-missing-symbol"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/lazy-pointer-binding/Makefile b/dyld/unit-tests/test-cases/lazy-pointer-binding/Makefile new file mode 100644 index 0000000..6d42f96 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-pointer-binding/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/lazy-pointer-binding/foo.c b/dyld/unit-tests/test-cases/lazy-pointer-binding/foo.c new file mode 100644 index 0000000..53d2618 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-pointer-binding/foo.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL + +bool floattest(double p1, float p2, double p3, float p4, double p5, float p6, + double p7, float p8, double p9, float p10, double p11, float p12, + double p13, float p14) +{ + if ( p1 != 1.0 ) + return false; + if ( p2 != 2.0 ) + return false; + if ( p3 != 3.0 ) + return false; + if ( p4 != 4.0 ) + return false; + if ( p5 != 5.0 ) + return false; + if ( p6 != 6.0 ) + return false; + if ( p7 != 7.0 ) + return false; + if ( p8 != 8.0 ) + return false; + if ( p9 != 9.0 ) + return false; + if ( p10 != 10.0 ) + return false; + if ( p11 != 11.0 ) + return false; + if ( p12 != 12.0 ) + return false; + if ( p13 != 13.0 ) + return false; + if ( p14 != 14.0 ) + return false; + return true; +} + +bool inttest(long long p1, long long p2, long long p3, long long p4, long long p5) +{ + if ( p1 != 0x100000002) + return false; + if ( p2 != 0x300000004) + return false; + if ( p3 != 0x500000006) + return false; + if ( p4 != 0x700000008) + return false; + if ( p5 != 0x90000000A) + return false; + return true; +} + + diff --git a/dyld/unit-tests/test-cases/lazy-pointer-binding/main.c b/dyld/unit-tests/test-cases/lazy-pointer-binding/main.c new file mode 100644 index 0000000..c2554b2 --- /dev/null +++ b/dyld/unit-tests/test-cases/lazy-pointer-binding/main.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool floattest(double p1, float p2, double p3, float p4, double p5, float p6, + double p7, float p8, double p9, float p10, double p11, float p12, + double p13, float p14); + +extern bool inttest(long long p1, long long p2, long long p3, long long p4, long long p5); + + + +int main() +{ + if ( ! floattest(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0) ) { + FAIL("lazy-pointer-binding float parameters"); + return EXIT_SUCCESS; + } + + if ( ! inttest(0x100000002, 0x300000004, 0x500000006, 0x700000008, 0x90000000A) ) { + FAIL("lazy-pointer-binding int parameters"); + return EXIT_SUCCESS; + } + + PASS("lazy-pointer-binding"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/lib-name-overload/Makefile b/dyld/unit-tests/test-cases/lib-name-overload/Makefile new file mode 100644 index 0000000..c5a211f --- /dev/null +++ b/dyld/unit-tests/test-cases/lib-name-overload/Makefile @@ -0,0 +1,60 @@ +## +# 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 + + +### +### rdar://problem/3684168 +### +### The process has two different libfoo.dylib. +### When DYLD_LIBRARY_PATH is used, both resolve to the same library. +### It gets worse. One of the libraries re-exports from the other. +### So, without loop detection in dyld, it will infinitely recurse. +### + +PWD = `pwd` + +all-check: all check + +check: + # verify it runs as-is + ./main + # verify dyld doesn't hang on the circularity + DYLD_LIBRARY_PATH=$(PWD) && export DYLD_LIBRARY_PATH && ${TESTROOT}/bin/exit-zero-pass.pl "lib-name-overload" "lib-name-overload" ./main + +all: main + +other/libfoo.dylib : foo2.c + mkdir -p other + ${CC} foo2.c -dynamiclib -o $(PWD)/other/libfoo.dylib + +libfoo.dylib : foo.c other/libfoo.dylib + ${CC} foo.c -dynamiclib $(PWD)/other/libfoo.dylib -sub_library libfoo -o $(PWD)/libfoo.dylib + +main : main.c libfoo.dylib + ${CC} main.c -I${TESTROOT}/include -o main libfoo.dylib + + +clean: + rm -rf main other libfoo.dylib \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/lib-name-overload/foo.c b/dyld/unit-tests/test-cases/lib-name-overload/foo.c new file mode 100644 index 0000000..4b2ea98 --- /dev/null +++ b/dyld/unit-tests/test-cases/lib-name-overload/foo.c @@ -0,0 +1,25 @@ +/* + * 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 = 0; + diff --git a/dyld/unit-tests/test-cases/lib-name-overload/foo2.c b/dyld/unit-tests/test-cases/lib-name-overload/foo2.c new file mode 100644 index 0000000..b34dc76 --- /dev/null +++ b/dyld/unit-tests/test-cases/lib-name-overload/foo2.c @@ -0,0 +1,26 @@ +/* + * 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 foo2 = 0; + + diff --git a/dyld/unit-tests/test-cases/lib-name-overload/main.c b/dyld/unit-tests/test-cases/lib-name-overload/main.c new file mode 100644 index 0000000..73cc95a --- /dev/null +++ b/dyld/unit-tests/test-cases/lib-name-overload/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + +/// +/// rdar://problem/3684168 +/// + + + +extern int foo2; + +int main() +{ + return foo2; +} diff --git a/dyld/unit-tests/test-cases/loader_path-dup/Makefile b/dyld/unit-tests/test-cases/loader_path-dup/Makefile new file mode 100644 index 0000000..981e577 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-dup/Makefile @@ -0,0 +1,70 @@ +## +# 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 + +PWD = $(shell pwd) + +### +### This test case is to verify that two different dylibs with the same name +### and each loaded via the same @loader_path does not confuse dyld +### into just loading one of them. +### + +## Note, until ld understands @loader_path we have to do a funky make + + +all-check: all check + +check: + ./main + +all: main + +foo/libfoo.dylib : foo.c foo/libbase.dylib + mkdir -p foo + ${CC} foo.c foo/libbase.dylib -dynamiclib -o "${PWD}/foo/libfoo.dylib" + +foo/libbase.dylib : base.c + mkdir -p foo + ${CC} base.c -DFOO -dynamiclib -o foo/libbase.dylib + + +bar/libbar.dylib : bar.c bar/libbase.dylib + mkdir -p bar + ${CC} bar.c bar/libbase.dylib -dynamiclib -o "${PWD}/bar/libbar.dylib" + +bar/libbase.dylib : base.c + mkdir -p bar + ${CC} base.c -Dbar -dynamiclib -o bar/libbase.dylib + + +main : main.c foo/libfoo.dylib bar/libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main foo/libfoo.dylib bar/libbar.dylib + # this breaks partial makes, but ld can't see @loader_path or it freaks + ${INSTALL_NAME_TOOL} -change foo/libbase.dylib '@loader_path/libbase.dylib' foo/libfoo.dylib + ${INSTALL_NAME_TOOL} -change bar/libbase.dylib '@loader_path/libbase.dylib' bar/libbar.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main foo bar diff --git a/dyld/unit-tests/test-cases/loader_path-dup/bar.c b/dyld/unit-tests/test-cases/loader_path-dup/bar.c new file mode 100644 index 0000000..edd9f35 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-dup/bar.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + +extern int base; + +bool bar() +{ + return (base == 2); +} diff --git a/dyld/unit-tests/test-cases/loader_path-dup/base.c b/dyld/unit-tests/test-cases/loader_path-dup/base.c new file mode 100644 index 0000000..03b9160 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-dup/base.c @@ -0,0 +1,29 @@ +/* + * 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@ + */ + +#if FOO +int base = 1; +#else +int base = 2; +#endif + diff --git a/dyld/unit-tests/test-cases/loader_path-dup/foo.c b/dyld/unit-tests/test-cases/loader_path-dup/foo.c new file mode 100644 index 0000000..7f0cfbb --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-dup/foo.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern int base; + +bool foo() +{ + return (base == 1); +} diff --git a/dyld/unit-tests/test-cases/loader_path-dup/main.c b/dyld/unit-tests/test-cases/loader_path-dup/main.c new file mode 100644 index 0000000..9c12ccc --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-dup/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" + +extern bool foo(); +extern bool bar(); + +int main() +{ + if ( foo() && bar() ) + PASS("loader_path"); + else + FAIL("loader_path-dup both libraries not loaded"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/loader_path-symlink/Makefile b/dyld/unit-tests/test-cases/loader_path-symlink/Makefile new file mode 100644 index 0000000..7fab8fc --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-symlink/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a dylib is found via symlink and must find another dylib relative to the symlink +# + +all-check: all check + +check: + ./bin/main + +all: + mkdir -p Frameworks/Foo.framework/Versions/A + mkdir -p Frameworks/Bar.framework/Versions/A + ${CC} bar.c -dynamiclib -o Frameworks/Bar.framework/Versions/A/Bar -install_name @loader_path/../Bar.framework/Bar + (cd Frameworks/Foo.framework; ln -fs Versions/A/Foo) + (cd Frameworks/Bar.framework; ln -fs Versions/A/Bar) + ${CC} foo.c -dynamiclib -o Frameworks/Foo.framework/Versions/A/Foo -install_name @loader_path/../Frameworks/Foo.framework/Foo Frameworks/Bar.framework/Versions/A/Bar + mkdir -p bin + ${CC} -I${TESTROOT}/include main.c -o bin/main Frameworks/Foo.framework/Versions/A/Foo + + +clean: + ${RM} -rf *~ Frameworks bin diff --git a/dyld/unit-tests/test-cases/loader_path-symlink/bar.c b/dyld/unit-tests/test-cases/loader_path-symlink/bar.c new file mode 100644 index 0000000..b72a1a5 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-symlink/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/dyld/unit-tests/test-cases/loader_path-symlink/foo.c b/dyld/unit-tests/test-cases/loader_path-symlink/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-symlink/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/loader_path-symlink/main.c b/dyld/unit-tests/test-cases/loader_path-symlink/main.c new file mode 100644 index 0000000..4c91a7c --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path-symlink/main.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("loader_path-symlink"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/loader_path/Makefile b/dyld/unit-tests/test-cases/loader_path/Makefile new file mode 100644 index 0000000..06eeb2b --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name libfoo.dylib + +hide/libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o hide/libbar.dylib -install_name libbar.dylib hide/hole/libfoo.dylib + install_name_tool -change libfoo.dylib '@loader_path/hole/libfoo.dylib' hide/libbar.dylib + +hide/libfoo3.dylib : foo.c + ${CC} foo.c -dynamiclib -o hide/libfoo3.dylib -install_name libfoo3.dylib + +libfoo2.dylib : foo.c + ${CC} foo.c -dynamiclib -o libfoo2.dylib + + +main : main.c libfoo2.dylib hide/libbar.dylib hide/libfoo3.dylib + ${CC} -Wno-deprecated-declarations -I${TESTROOT}/include main.c -o main hide/libbar.dylib libfoo2.dylib + ${INSTALL_NAME_TOOL} -change libfoo2.dylib '@loader_path/libfoo2.dylib' main + ${INSTALL_NAME_TOOL} -change libbar.dylib '@loader_path/hide/libbar.dylib' main + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib hide libfoo2.dylib diff --git a/dyld/unit-tests/test-cases/loader_path/bar.c b/dyld/unit-tests/test-cases/loader_path/bar.c new file mode 100644 index 0000000..902e563 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path/bar.c @@ -0,0 +1,23 @@ +/* + * 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@ + */ +void bar() {} diff --git a/dyld/unit-tests/test-cases/loader_path/foo.c b/dyld/unit-tests/test-cases/loader_path/foo.c new file mode 100644 index 0000000..1bcf3c2 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path/foo.c @@ -0,0 +1,25 @@ +/* + * 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@ + */ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/loader_path/main.c b/dyld/unit-tests/test-cases/loader_path/main.c new file mode 100644 index 0000000..f8600b4 --- /dev/null +++ b/dyld/unit-tests/test-cases/loader_path/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + +#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); +#else + if ( dlopen("@loader_path/hide/libfoo3.dylib", 0) == NULL ) + FAIL("loader_path"); + else +#endif + PASS("loader_path"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/Foo.c b/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/Foo.c new file mode 100644 index 0000000..100cfc6 --- /dev/null +++ b/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/Foo.c @@ -0,0 +1,22 @@ +/* + * 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@ + */ diff --git a/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile b/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile new file mode 100644 index 0000000..217074c --- /dev/null +++ b/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/Makefile @@ -0,0 +1,50 @@ +## +# 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: + export DYLD_FALLBACK_FRAMEWORK_PATH="dir" && ${TESTROOT}/bin/exit-zero-pass.pl "pass message" "fail message" ./main + +all: main dir/Foo.framework/Versions/Current/Foo + +Foo.framework/Versions/Current/Foo: + mkdir -p Foo.framework/Versions/A + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o Foo.framework/Versions/A/Foo Foo.c + cd Foo.framework/Versions/ && ln -s A Current + cd Foo.framework/ && ln -s Versions/Current/Foo Foo + +dir/Foo.framework/Versions/Current/Foo: + $(MAKE) Foo.framework/Versions/Current/Foo + rm -f Foo.framework/Foo + mkdir dir + mv Foo.framework dir + +main: + $(MAKE) Foo.framework/Versions/Current/Foo + ${CC} ${CCFLAGS} -I${TESTROOT}/include -F. -framework Foo -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main dir/ Foo.framework/ diff --git a/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c b/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c new file mode 100644 index 0000000..a2233c8 --- /dev/null +++ b/dyld/unit-tests/test-cases/missing-symlink-framework-fallback-path/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/non-lazy-slide/Makefile b/dyld/unit-tests/test-cases/non-lazy-slide/Makefile new file mode 100644 index 0000000..2cf9c9a --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-slide/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/non-lazy-slide/bar.c b/dyld/unit-tests/test-cases/non-lazy-slide/bar.c new file mode 100644 index 0000000..fa8deff --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-slide/bar.c @@ -0,0 +1,3 @@ + + +int bar = 10; diff --git a/dyld/unit-tests/test-cases/non-lazy-slide/foo.c b/dyld/unit-tests/test-cases/non-lazy-slide/foo.c new file mode 100644 index 0000000..1b2c9f7 --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-slide/foo.c @@ -0,0 +1,7 @@ + +extern int bar; + +int foo() +{ + return bar; +} diff --git a/dyld/unit-tests/test-cases/non-lazy-slide/main.c b/dyld/unit-tests/test-cases/non-lazy-slide/main.c new file mode 100644 index 0000000..a73a1db --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-slide/main.c @@ -0,0 +1,23 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib is built from multiple source files +// It internally uses non-lazy pointer to compute +// the result for foo() + + +extern int foo(); + +int main() +{ + if ( foo() == 10 ) + PASS("non-lazy-slide"); + else + FAIL("non-lazy-slide"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/non-lazy-weak/Makefile b/dyld/unit-tests/test-cases/non-lazy-weak/Makefile new file mode 100644 index 0000000..2cf9c9a --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-weak/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main libfoo.dylib + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c bar.c + ${CC} ${CCFLAGS} -dynamiclib foo.c bar.c -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/non-lazy-weak/bar.c b/dyld/unit-tests/test-cases/non-lazy-weak/bar.c new file mode 100644 index 0000000..91a216d --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-weak/bar.c @@ -0,0 +1,3 @@ + + +__attribute__((weak)) int bar = 10; diff --git a/dyld/unit-tests/test-cases/non-lazy-weak/foo.c b/dyld/unit-tests/test-cases/non-lazy-weak/foo.c new file mode 100644 index 0000000..1b2c9f7 --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-weak/foo.c @@ -0,0 +1,7 @@ + +extern int bar; + +int foo() +{ + return bar; +} diff --git a/dyld/unit-tests/test-cases/non-lazy-weak/main.c b/dyld/unit-tests/test-cases/non-lazy-weak/main.c new file mode 100644 index 0000000..16a78b7 --- /dev/null +++ b/dyld/unit-tests/test-cases/non-lazy-weak/main.c @@ -0,0 +1,20 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib has a weak global variable that foo() returns + +extern int foo(); + +int main() +{ + if ( foo() == 10 ) + PASS("non-lazy-weak"); + else + FAIL("non-lazy-weak"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/non-weak-library/Makefile b/dyld/unit-tests/test-cases/non-weak-library/Makefile new file mode 100644 index 0000000..540909d --- /dev/null +++ b/dyld/unit-tests/test-cases/non-weak-library/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + cd absent/ && ../${TESTROOT}/bin/exit-non-zero-pass.pl "non-weak-library" "non-weak-library" ./main + cd present/ && ./main + +all: foo.c main.c + ${CC} -dynamiclib -prebind -seg1addr 400000 -o libfoo.dylib foo.c + mkdir -p absent/ + ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_ABSENT -o absent/main main.c + mkdir -p present/ + ${CC} -prebind -I${TESTROOT}/include -L. -lfoo -DLIB_PRESENT -o present/main main.c + mv libfoo.dylib present/ + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib absent present diff --git a/dyld/unit-tests/test-cases/non-weak-library/foo.c b/dyld/unit-tests/test-cases/non-weak-library/foo.c new file mode 100644 index 0000000..c57adba --- /dev/null +++ b/dyld/unit-tests/test-cases/non-weak-library/foo.c @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/non-weak-library/main.c b/dyld/unit-tests/test-cases/non-weak-library/main.c new file mode 100644 index 0000000..9487058 --- /dev/null +++ b/dyld/unit-tests/test-cases/non-weak-library/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + +int +main(int argc, char **argv) +{ +#if defined(LIB_PRESENT) + PASS("non-weak-library"); +#endif +#if defined(LIB_ABSENT) + FAIL("non-weak-library"); +#endif + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/operator-new-dylib/Makefile b/dyld/unit-tests/test-cases/operator-new-dylib/Makefile new file mode 100644 index 0000000..fbaac19 --- /dev/null +++ b/dyld/unit-tests/test-cases/operator-new-dylib/Makefile @@ -0,0 +1,33 @@ + + + + +# +# test that if the main executable overrides operator new +# that a dylib using operator new is redirected to it. +# +# + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + + +main: main.cxx libfoo.dylib + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx libfoo.dylib + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib foo.cxx -o libfoo.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/operator-new-dylib/foo.cxx b/dyld/unit-tests/test-cases/operator-new-dylib/foo.cxx new file mode 100644 index 0000000..f91f88c --- /dev/null +++ b/dyld/unit-tests/test-cases/operator-new-dylib/foo.cxx @@ -0,0 +1,13 @@ + +#include +#include + + + + + +char* foo() +{ + return new char[24]; +} + diff --git a/dyld/unit-tests/test-cases/operator-new-dylib/main.cxx b/dyld/unit-tests/test-cases/operator-new-dylib/main.cxx new file mode 100644 index 0000000..25be30d --- /dev/null +++ b/dyld/unit-tests/test-cases/operator-new-dylib/main.cxx @@ -0,0 +1,31 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + + +extern char* foo(); + +static char all[] = "hello"; + +void* operator new[](std::size_t) throw (std::bad_alloc) +{ + return all; +} + + +int main() +{ + char* newArray = new char[24]; + char* fromFoo = foo(); + + if ( fromFoo == newArray ) + PASS("operator-new-dylib"); + else + FAIL("operator-new-dylib"); + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/operator-new/Makefile b/dyld/unit-tests/test-cases/operator-new/Makefile new file mode 100644 index 0000000..2fbed98 --- /dev/null +++ b/dyld/unit-tests/test-cases/operator-new/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + + +main: main.cxx + ${CXX} ${CXXFLAGS} -I${TESTROOT}/include -o main main.cxx + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/operator-new/main.cxx b/dyld/unit-tests/test-cases/operator-new/main.cxx new file mode 100644 index 0000000..a0c25ce --- /dev/null +++ b/dyld/unit-tests/test-cases/operator-new/main.cxx @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + +// +// This test case verifies that calling operator new[] in libstdc++.dylib +// will turn around and call operator new in this main exectuable +// + +static void* ptr; + +void* operator new(size_t s) throw (std::bad_alloc) +{ + ptr = malloc(s); + return ptr; +} + + +int main() +{ + char* stuff = new char[24]; + if ( (void*)stuff == ptr ) + PASS("operator-new"); + else + FAIL("operator-new"); + return EXIT_SUCCESS; +} + diff --git a/dyld/unit-tests/test-cases/partial-library-load/Makefile b/dyld/unit-tests/test-cases/partial-library-load/Makefile new file mode 100644 index 0000000..83ef9bd --- /dev/null +++ b/dyld/unit-tests/test-cases/partial-library-load/Makefile @@ -0,0 +1,59 @@ +## +# 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 + +### +### rdar://problem/3884004 Libraries can be half-baked if an error occurs during their first use +### +### We indirectly load libfoo which depends on libbar. The libbar that is loaded does not contain +### what libfoo needs, so libfoo fails to bind. The bug was that libfoo was left marked as bound, +### so the next use of libfoo seemed to succeed, when it should have failed. +### + + +all-check: all check + +check: + export DYLD_IMAGE_SUFFIX="_missing" && ${TESTROOT}/bin/exit-zero-pass.pl "partial-library-load" "partial-library-load" ./main + +all: main test.bundle + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +test.bundle : bundle.c libfoo.dylib + ${CC} ${CCFLAGS} -bundle -o test.bundle bundle.c libfoo.dylib + +libfoo.dylib : foo.c libbar_missing.dylib libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib libbar.dylib + +libbar_missing.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar_missing.dylib -install_name libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -DHAS_BAR2=1 + +clean: + ${RM} ${RMFLAGS} *~ main test.bundle libfoo.dylib libbar.dylib libbar_missing.dylib + diff --git a/dyld/unit-tests/test-cases/partial-library-load/bar.c b/dyld/unit-tests/test-cases/partial-library-load/bar.c new file mode 100644 index 0000000..400e1bf --- /dev/null +++ b/dyld/unit-tests/test-cases/partial-library-load/bar.c @@ -0,0 +1,30 @@ +/* + * 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 bar1 = 1; +#if HAS_BAR2 +int bar2 = 2; +#endif +int bar3 = 3; + diff --git a/dyld/unit-tests/test-cases/partial-library-load/bundle.c b/dyld/unit-tests/test-cases/partial-library-load/bundle.c new file mode 100644 index 0000000..a7aad9e --- /dev/null +++ b/dyld/unit-tests/test-cases/partial-library-load/bundle.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +extern int foo(); + +void bun() +{ + foo(); + +} + diff --git a/dyld/unit-tests/test-cases/partial-library-load/foo.c b/dyld/unit-tests/test-cases/partial-library-load/foo.c new file mode 100644 index 0000000..8abc708 --- /dev/null +++ b/dyld/unit-tests/test-cases/partial-library-load/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int bar1; +extern int bar2; +extern int bar3; + + + +int foo() +{ + return bar1 + bar2 + bar3; +} diff --git a/dyld/unit-tests/test-cases/partial-library-load/main.c b/dyld/unit-tests/test-cases/partial-library-load/main.c new file mode 100644 index 0000000..9993ac4 --- /dev/null +++ b/dyld/unit-tests/test-cases/partial-library-load/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#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 + // load bundle which indirectly loads libfoo and libbar + NSObjectFileImage ofi; + if ( NSCreateObjectFileImageFromFile("test.bundle", &ofi) != NSObjectFileImageSuccess ) { + FAIL("NSCreateObjectFileImageFromFile failed"); + return 1; + } + + // link bundle, this will fail because bar2 is missing from libbar + NSModule mod = NSLinkModule(ofi, "test.bundle", NSLINKMODULE_OPTION_RETURN_ON_ERROR); + if ( mod != NULL ) { + FAIL("NSLinkModule succeeded but should have failed"); + return 1; + } + + // load libfoo, this should fail because it can't be loaded + const struct mach_header* mh = NSAddImage("libfoo.dylib", NSADDIMAGE_OPTION_RETURN_ON_ERROR); + if ( mh != NULL ) { + return 1; + } +#endif +#if 0 + // find foo + NSSymbol sym = NSLookupSymbolInImage(mh, "_foo", NSLOOKUPSYMBOLINIMAGE_OPTION_BIND); + if ( sym == NULL ) { + FAIL("NSLookupSymbolInImage failed"); + return 1; + } + + // if foo() was only partially bound, this will crash + int (*fooPtr)() = NSAddressOfSymbol(sym); + (*fooPtr)(); +#endif + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/pie-basic/Makefile b/dyld/unit-tests/test-cases/pie-basic/Makefile new file mode 100644 index 0000000..bac7b71 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-basic/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-basic"; \ + else \ + echo "FAIL pie-basic"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/dyld/unit-tests/test-cases/pie-basic/main.c b/dyld/unit-tests/test-cases/pie-basic/main.c new file mode 100644 index 0000000..3a6571d --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-basic/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +int main() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/dyld/unit-tests/test-cases/pie-big/Makefile b/dyld/unit-tests/test-cases/pie-big/Makefile new file mode 100644 index 0000000..e8a5fd0 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-big/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-basic"; \ + else \ + echo "XFAIL pie-basic"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/dyld/unit-tests/test-cases/pie-big/main.c b/dyld/unit-tests/test-cases/pie-big/main.c new file mode 100644 index 0000000..49d9a88 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-big/main.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#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() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/dyld/unit-tests/test-cases/pie-custom-stack/Makefile b/dyld/unit-tests/test-cases/pie-custom-stack/Makefile new file mode 100644 index 0000000..e795c80 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-custom-stack/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-custom-stack"; \ + else \ + echo "FAIL pie-custom-stack"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie -o main main.c -Wl,-stack_size,0x10000000 + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/dyld/unit-tests/test-cases/pie-custom-stack/main.c b/dyld/unit-tests/test-cases/pie-custom-stack/main.c new file mode 100644 index 0000000..3a6571d --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-custom-stack/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +int main() +{ + //int local; + + printf("&main=%p\n", &main); + //printf("&local=%p\n",&local); + return 0; +} diff --git a/dyld/unit-tests/test-cases/pie-dylib/Makefile b/dyld/unit-tests/test-cases/pie-dylib/Makefile new file mode 100644 index 0000000..3b709dd --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-dylib/Makefile @@ -0,0 +1,41 @@ +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# If pie, ignore preferred load address +# +# run a PIE four times and verify libfoo.dylib load address was different every time +# + +FOO_ADDRESS = 0x10000000 + +ifeq "x86_64" "$(ARCH)" + FOO_ADDRESS = 0x300000000 +endif + + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-dylib"; \ + else \ + echo "FAIL pie-dylib"; \ + fi; \ + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie libfoo.dylib -o main main.c + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib -seg1addr ${FOO_ADDRESS} + +clean: + ${RM} ${RMFLAGS} *~ main main.out libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/pie-dylib/foo.c b/dyld/unit-tests/test-cases/pie-dylib/foo.c new file mode 100644 index 0000000..0c9d080 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-dylib/foo.c @@ -0,0 +1,7 @@ +#include +#include + +void foo() +{ + printf("&foo=%p\n", &foo); +} diff --git a/dyld/unit-tests/test-cases/pie-dylib/main.c b/dyld/unit-tests/test-cases/pie-dylib/main.c new file mode 100644 index 0000000..d36c9b8 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-dylib/main.c @@ -0,0 +1,10 @@ +#include +#include + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/dyld/unit-tests/test-cases/pie-text-reloc/Makefile b/dyld/unit-tests/test-cases/pie-text-reloc/Makefile new file mode 100644 index 0000000..31d75d6 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-text-reloc/Makefile @@ -0,0 +1,61 @@ +## +# 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 + +EXTRA_OPTIONS = -mdynamic-no-pic -read_only_relocs suppress + +ifeq "x86_64" "$(ARCH)" + EXTRA_OPTIONS = +endif + +ifeq "iPhoneOS" "$(OS_NAME)" + EXTRA_OPTIONS = +endif + + +# run a PIE four times and verify its load address was different every time + +all-check: all check + +check: + ./main > main.out + ./main >> main.out + ./main >> main.out + ./main >> main.out + if [ `sort main.out -u | wc -l` == 4 ]; \ + then \ + echo "PASS pie-text-reloc"; \ + else \ + echo "FAIL pie-text-reloc"; \ + fi; \ + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -Wl,-pie $(EXTRA_OPTIONS) -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main main.out + diff --git a/dyld/unit-tests/test-cases/pie-text-reloc/main.c b/dyld/unit-tests/test-cases/pie-text-reloc/main.c new file mode 100644 index 0000000..f1c8b85 --- /dev/null +++ b/dyld/unit-tests/test-cases/pie-text-reloc/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + + +static int data=0; + +int main() +{ + printf("&data=%p\n", &data); + return 0; +} diff --git a/dyld/unit-tests/test-cases/prebased-performance/Makefile b/dyld/unit-tests/test-cases/prebased-performance/Makefile new file mode 100644 index 0000000..ce0d7d9 --- /dev/null +++ b/dyld/unit-tests/test-cases/prebased-performance/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +DYLIB_BASES_ADDRESS = 0x10000000 + +ifeq "ppc64" "$(ARCH)" + DYLIB_BASES_ADDRESS = 0x300000000 +else + ifeq "x86_64" "$(ARCH)" + DYLIB_BASES_ADDRESS = 0x300000000 + endif +endif + + + +all-check: all check + +check: + ./main + +all: main + +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} + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/prebased-performance/foo.c b/dyld/unit-tests/test-cases/prebased-performance/foo.c new file mode 100644 index 0000000..1836973 --- /dev/null +++ b/dyld/unit-tests/test-cases/prebased-performance/foo.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +struct page +{ + struct page* other; + char pad[4096-sizeof(void*)]; +}; + + +struct page p1 = { &p1 }; +struct page p2 = { &p1 }; +struct page p3 = { &p1 }; +struct page p4 = { &p1 }; +struct page p5 = { &p1 }; +struct page p6 = { &p1 }; +struct page p7 = { &p1 }; +struct page p8 = { &p1 }; +struct page p9 = { &p1 }; +struct page p10 = { &p1 }; + +// +// This dylib has 10 pages of data and each page will be adjusted +// by dyld if this dylib is not loaded at its preferred address. +// +bool checkRebasing() +{ + int dirtyPageCount = 0; + vm_address_t start = (((uintptr_t)&p1) + 4095) & (-4096); + vm_address_t end = (((uintptr_t)&p10) + 4095) & (-4096); + task_port_t task; + task_for_pid(mach_task_self(), getpid(), &task); + + for (vm_address_t addr = start; addr < end; addr += 4096) { + integer_t disposition = 0; + integer_t ref_count = 0; + vm_map_page_query(task, start, &disposition, &ref_count); + if ( disposition & VM_PAGE_QUERY_PAGE_DIRTY ) + ++dirtyPageCount; + } + + //fprintf(stderr, "dirtyPageCount=%d\n", dirtyPageCount); + + // if there are too many dirty pages, then dyld is inefficient + return ( dirtyPageCount < 2 ); +} + + + + + diff --git a/dyld/unit-tests/test-cases/prebased-performance/main.c b/dyld/unit-tests/test-cases/prebased-performance/main.c new file mode 100644 index 0000000..98fe608 --- /dev/null +++ b/dyld/unit-tests/test-cases/prebased-performance/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern bool checkRebasing(); + +int main(int argc, const char* argv[]) +{ + if ( checkRebasing() ) + PASS("prebased-performance"); + else + FAIL("prebased-performance"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/progname/Makefile b/dyld/unit-tests/test-cases/progname/Makefile new file mode 100644 index 0000000..5caec06 --- /dev/null +++ b/dyld/unit-tests/test-cases/progname/Makefile @@ -0,0 +1,36 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/progname/main.c b/dyld/unit-tests/test-cases/progname/main.c new file mode 100644 index 0000000..1f05111 --- /dev/null +++ b/dyld/unit-tests/test-cases/progname/main.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +static const char* name = NULL; + + +static void checker() __attribute__((constructor)); +static void checker() +{ + name = strdup(getprogname()); +} + + + +int +main(int argc, const char* argv[]) +{ + // verify that getprogname() works during initializers + if ( strcmp(name, getprogname()) == 0 ) + PASS("progname"); + else + FAIL("progname"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/pthread-keys/Makefile b/dyld/unit-tests/test-cases/pthread-keys/Makefile new file mode 100644 index 0000000..a6a3242 --- /dev/null +++ b/dyld/unit-tests/test-cases/pthread-keys/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} *~ main diff --git a/dyld/unit-tests/test-cases/pthread-keys/main.c b/dyld/unit-tests/test-cases/pthread-keys/main.c new file mode 100644 index 0000000..e1832d8 --- /dev/null +++ b/dyld/unit-tests/test-cases/pthread-keys/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +/// +/// rdar://problem/4313830 +/// The key index for main executables starts at 4 +/// + +int main() +{ + pthread_key_t key; + + int result = pthread_key_create(&key, NULL); + //printf("key=%u\n", key); + if ( result != 0 ) + FAIL("pthread-keys creation failure"); + else if ( key < 4 ) + FAIL("pthread-keys key out of bounds"); + else + PASS("pthread-keys"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/re-export-dylib/Makefile b/dyld/unit-tests/test-cases/re-export-dylib/Makefile new file mode 100644 index 0000000..5cea7a1 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-dylib/Makefile @@ -0,0 +1,54 @@ +## +# 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 + +PWD = `pwd` + + +# +# Test that dylib re-exports work +# + +all-check: all check + +check: + ./main + +all: main + + + +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 + +main : main.c libfoo.dylib + ${CC} main.c -I${TESTROOT}/include -o main libfoo.dylib + + +clean: + rm -rf main libfoo.dylib libbar.dylib + \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/re-export-dylib/bar.c b/dyld/unit-tests/test-cases/re-export-dylib/bar.c new file mode 100644 index 0000000..b0faf2f --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-dylib/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/dyld/unit-tests/test-cases/re-export-dylib/foo.c b/dyld/unit-tests/test-cases/re-export-dylib/foo.c new file mode 100644 index 0000000..2486c7a --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-dylib/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/dyld/unit-tests/test-cases/re-export-dylib/main.c b/dyld/unit-tests/test-cases/re-export-dylib/main.c new file mode 100644 index 0000000..2d54c74 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-dylib/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-dylib"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/re-export-framework/Makefile b/dyld/unit-tests/test-cases/re-export-framework/Makefile new file mode 100644 index 0000000..9e5cea6 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-framework/Makefile @@ -0,0 +1,58 @@ +## +# 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 + +PWD = `pwd` + + +# +# Test re-exported frameworks work +# + +all-check: all check + +check: + ./main + +all: main + + + +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 + +main : main.c Foo.framework/Foo + ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F. + + + + +clean: + rm -rf main Foo.framework Bar.framework + \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/re-export-framework/bar.c b/dyld/unit-tests/test-cases/re-export-framework/bar.c new file mode 100644 index 0000000..b0faf2f --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-framework/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/dyld/unit-tests/test-cases/re-export-framework/foo.c b/dyld/unit-tests/test-cases/re-export-framework/foo.c new file mode 100644 index 0000000..2486c7a --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-framework/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/dyld/unit-tests/test-cases/re-export-framework/main.c b/dyld/unit-tests/test-cases/re-export-framework/main.c new file mode 100644 index 0000000..7ad3fed --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-framework/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-framework"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/re-export-sub-framework/Makefile b/dyld/unit-tests/test-cases/re-export-sub-framework/Makefile new file mode 100644 index 0000000..d77a607 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-sub-framework/Makefile @@ -0,0 +1,56 @@ +## +# 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 + +PWD = `pwd` + + +# +# Test that the ways to re-export a sub framework work +# + +all-check: all check + +check: + ./main + +all: main + + +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 + +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 + +main : main.c Foo.framework/Foo + ${CC} main.c -I${TESTROOT}/include -o main -framework Foo -F. + + + +clean: + rm -rf main Foo.framework Bar.framework + \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/re-export-sub-framework/bar.c b/dyld/unit-tests/test-cases/re-export-sub-framework/bar.c new file mode 100644 index 0000000..b0faf2f --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-sub-framework/bar.c @@ -0,0 +1,6 @@ +int bar() +{ + return 1; +} + + diff --git a/dyld/unit-tests/test-cases/re-export-sub-framework/foo.c b/dyld/unit-tests/test-cases/re-export-sub-framework/foo.c new file mode 100644 index 0000000..2486c7a --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-sub-framework/foo.c @@ -0,0 +1,5 @@ +int foo() +{ + return 0; +} + diff --git a/dyld/unit-tests/test-cases/re-export-sub-framework/main.c b/dyld/unit-tests/test-cases/re-export-sub-framework/main.c new file mode 100644 index 0000000..fe2ae5e --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-sub-framework/main.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + + + +extern int foo(); +extern int bar(); + +int main() +{ + foo(); + bar(); + PASS("re-export-sub-framework"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/Makefile b/dyld/unit-tests/test-cases/re-export-symbol-dylib/Makefile new file mode 100644 index 0000000..9465192 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/Makefile @@ -0,0 +1,59 @@ +## +# Copyright (c) 2011 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_1: + ./main + +all_1: + # build frob + ${CC} ${CCFLAGS} -dynamiclib frob.c -o libfrob.dylib + # build baz to re-export all of frob + ${CC} ${CCFLAGS} -dynamiclib baz.c -o libbaz.dylib -exported_symbols_list baz.exp libfrob.dylib + # build bar to re-export all of baz + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -Wl,-reexport_library,libbaz.dylib + # build foo to re-export all of bar + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -exported_symbols_list foo.exp libbar.dylib + # buid main + ${CC} ${CCFLAGS} main.c -I${TESTROOT}/include libfoo.dylib -o main + +check_: + ${PASS_IFF} true + +all_: + +clean: + rm -rf libbar.dylib libfoo.dylib libbaz.dylib libfrob.dylib main diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/bar.c b/dyld/unit-tests/test-cases/re-export-symbol-dylib/bar.c new file mode 100644 index 0000000..18d7633 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 2; +} diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/baz.c b/dyld/unit-tests/test-cases/re-export-symbol-dylib/baz.c new file mode 100644 index 0000000..cb30818 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/baz.c @@ -0,0 +1,5 @@ +int baz() +{ + return 3; +} + diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/baz.exp b/dyld/unit-tests/test-cases/re-export-symbol-dylib/baz.exp new file mode 100644 index 0000000..5fbf7f4 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/baz.exp @@ -0,0 +1,2 @@ +_baz +_frob diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/foo.c b/dyld/unit-tests/test-cases/re-export-symbol-dylib/foo.c new file mode 100644 index 0000000..72b3c29 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 1; +} diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/foo.exp b/dyld/unit-tests/test-cases/re-export-symbol-dylib/foo.exp new file mode 100644 index 0000000..aff96a1 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/foo.exp @@ -0,0 +1,4 @@ +_foo +_bar +_baz +_frob diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/frob.c b/dyld/unit-tests/test-cases/re-export-symbol-dylib/frob.c new file mode 100644 index 0000000..3df9d3d --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/frob.c @@ -0,0 +1,5 @@ +int frob() +{ + return 4; +} + diff --git a/dyld/unit-tests/test-cases/re-export-symbol-dylib/main.c b/dyld/unit-tests/test-cases/re-export-symbol-dylib/main.c new file mode 100644 index 0000000..f12dd10 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol-dylib/main.c @@ -0,0 +1,44 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +extern int foo(); +extern int bar(); +extern int baz(); +extern int frob(); + +int (*pfoo)() = &foo; +int (*pbar)() = &bar; +int (*pbaz)() = &baz; +int (*pfrob)() = &frob; + + +int main() +{ + if ( foo() != 1 ) + FAIL("re-export-symbol-dylib: foo() returned wrong value"); + else if ( bar() != 2 ) + FAIL("re-export-symbol-dylib: bar() returned wrong value"); + else if ( baz() != 3 ) + FAIL("re-export-symbol-dylib: baz() returned wrong value"); + else if ( frob() != 4 ) + FAIL("re-export-symbol-dylib: frob() returned wrong value"); + + else if ( (*pfoo)() != 1 ) + FAIL("re-export-symbol-dylib: (*pfoo)() returned wrong value"); + else if ( (*pbar)() != 2 ) + FAIL("re-export-symbol-dylib: (*pbar)() returned wrong value"); + else if ( (*pbaz)() != 3 ) + FAIL("re-export-symbol-dylib: (*pbaz)() returned wrong value"); + else if ( (*pfrob)() != 4 ) + FAIL("re-export-symbol-dylib: (*pfrob)() returned wrong value"); + else + PASS("re-export-symbol-dylib"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/re-export-symbol/Makefile b/dyld/unit-tests/test-cases/re-export-symbol/Makefile new file mode 100644 index 0000000..c02f912 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol/Makefile @@ -0,0 +1,62 @@ +## +# 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_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 diff --git a/dyld/unit-tests/test-cases/re-export-symbol/bar.c b/dyld/unit-tests/test-cases/re-export-symbol/bar.c new file mode 100644 index 0000000..f5837c1 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol/bar.c @@ -0,0 +1,5 @@ + +int bar(void) +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/re-export-symbol/foo.c b/dyld/unit-tests/test-cases/re-export-symbol/foo.c new file mode 100644 index 0000000..9232d4c --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol/foo.c @@ -0,0 +1,4 @@ +int foo(void) +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/re-export-symbol/foo.exp b/dyld/unit-tests/test-cases/re-export-symbol/foo.exp new file mode 100644 index 0000000..b9e50b8 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol/foo.exp @@ -0,0 +1,2 @@ +_foo +_bar diff --git a/dyld/unit-tests/test-cases/re-export-symbol/foo2.exp b/dyld/unit-tests/test-cases/re-export-symbol/foo2.exp new file mode 100644 index 0000000..d55b748 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol/foo2.exp @@ -0,0 +1,2 @@ +_foo +_mybar diff --git a/dyld/unit-tests/test-cases/re-export-symbol/main1.c b/dyld/unit-tests/test-cases/re-export-symbol/main1.c new file mode 100644 index 0000000..cd2bab7 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol/main1.c @@ -0,0 +1,23 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/re-export-symbol/main2.c b/dyld/unit-tests/test-cases/re-export-symbol/main2.c new file mode 100644 index 0000000..7a11c92 --- /dev/null +++ b/dyld/unit-tests/test-cases/re-export-symbol/main2.c @@ -0,0 +1,26 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#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; +} + diff --git a/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile b/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile new file mode 100644 index 0000000..5364cbd --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -o main main.c + +libfoo.dylib: foo.cxx + ${CXX} ${CXXFLAGS} -dynamiclib -I${TESTROOT}/include -o libfoo.dylib foo.cxx + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + + diff --git a/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx b/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx new file mode 100644 index 0000000..9bf26a8 --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/foo.cxx @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2006-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include +#include + +// +// This test case verifies that calling operator new[] in libstdc++.dylib +// will turn around and call operator new in this main exectuable +// + +static void* ptr; + +void* operator new(size_t s) throw (std::bad_alloc) +{ + ptr = malloc(s); + return ptr; +} + + +int check() +{ + char* stuff = new char[24]; + if ( (void*)stuff == ptr ) + PASS("operator-new"); + else + FAIL("operator-new"); + return 0; +} + +int x = check(); // runs when library is loaded + +// add this so WEAK_DEFINES is set, so dyld searchs this image +int __attribute__((weak)) junk = 2; diff --git a/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c b/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c new file mode 100644 index 0000000..4579fe4 --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-import-shared-cache-coalesce/main.c @@ -0,0 +1,12 @@ + +#include + +int main() +{ + // dynamically load libfoo.dylib which depends on libstdc++.dylib + // being re-bound to libfoo's operator new. + dlopen("libfoo.dylib", RTLD_LAZY); + return 0; +} + + diff --git a/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile b/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile new file mode 100644 index 0000000..59cf69c --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + export DYLD_INSERT_LIBRARIES="libmymalloc.dylib" && ./main + +all: main libmymalloc.dylib + +main: main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +libmymalloc.dylib : mymalloc.c + ${CC} ${CCFLAGS} -dynamiclib mymalloc.c -o libmymalloc.dylib + + + +clean: + ${RM} ${RMFLAGS} *~ main libmymalloc.dylib + + diff --git a/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/main.c b/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/main.c new file mode 100644 index 0000000..b3c927d --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/main.c @@ -0,0 +1,14 @@ +#include + +#include "test.h" + +int main() +{ + // dynamically load libz.dylib which imports _malloc from libSystem + dlopen("/usr/lib/libz.dylib", RTLD_LAZY); + + + PASS("read-only-import-shared-cache-dlopen-override"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/mymalloc.c b/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/mymalloc.c new file mode 100644 index 0000000..b0dd819 --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-import-shared-cache-dlopen-override/mymalloc.c @@ -0,0 +1,9 @@ +#include +#include + +void* mymalloc(size_t s) +{ + return malloc(s); +} + +DYLD_INTERPOSE(mymalloc, malloc) diff --git a/dyld/unit-tests/test-cases/read-only-stubs/Makefile b/dyld/unit-tests/test-cases/read-only-stubs/Makefile new file mode 100644 index 0000000..7e70097 --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-stubs/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include main.c libfoo.dylib -o main + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include -dynamiclib foo.c libbar.dylib -o libfoo.dylib + +libbar.dylib: bar.c + ${CC} ${CCFLAGS} -Wl,-read_only_stubs -I${TESTROOT}/include -dynamiclib bar.c -o libbar.dylib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/read-only-stubs/bar.c b/dyld/unit-tests/test-cases/read-only-stubs/bar.c new file mode 100644 index 0000000..d9a1c20 --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-stubs/bar.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int barData = 1; + +void bar() +{ +} + + diff --git a/dyld/unit-tests/test-cases/read-only-stubs/foo.c b/dyld/unit-tests/test-cases/read-only-stubs/foo.c new file mode 100644 index 0000000..68fb1fb --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-stubs/foo.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void bar(); +extern int barData; + + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ +#if __LP64__ + uint64_t size; +#else + uint32_t size; +#endif + uintptr_t slide = (uintptr_t)&_mh_dylib_header; // assume dylib is zero-base so slide == load address +#if __i386__ + return getsectdatafromheader(&_mh_dylib_header, "__IMPORT", "__jump_table", &size) + slide; +#elif __ppc__ + return getsectdatafromheader(&_mh_dylib_header, "TEXT", "__picsymbolstub1", &size) + slide; +#elif __ppc64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__picsymbolstub1", &size) + slide; +#elif __x86_64__ + return getsectdatafromheader_64(&_mh_dylib_header, "__TEXT", "__symbol_stub1", &size) + slide; +#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); + if ( (perm == 0) || ((perm & VM_PROT_WRITE) != 0) ) { + FAIL("read-only-stubs: bad permissions %d at address %p", perm, addr); + exit(0); + } +} + +int fooData = 1; + +void foo() +{ + void* stubAddr = getStubAddr(); + checkStubs(stubAddr); + barData = 0; + bar(); + checkStubs(stubAddr); +} + + diff --git a/dyld/unit-tests/test-cases/read-only-stubs/main.c b/dyld/unit-tests/test-cases/read-only-stubs/main.c new file mode 100644 index 0000000..b9420c7 --- /dev/null +++ b/dyld/unit-tests/test-cases/read-only-stubs/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +extern void foo(); +extern int fooData; + +static vm_prot_t getPermission(void* addr) +{ + mach_vm_address_t address = (mach_vm_address_t)(uintptr_t)addr; + kern_return_t result; + mach_port_t object_name; + vm_region_basic_info_data_64_t info; + mach_msg_type_number_t count; + mach_vm_size_t size = 4096; + + count = VM_REGION_BASIC_INFO_COUNT_64; + result = mach_vm_region(mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t)&info, + &count, + &object_name); + //fprintf(stderr, "result=%X, info.protection=%X\n", result, info.protection); + if ( result == KERN_SUCCESS ) + return info.protection; + return 0; +} + + +static void* getStubAddr() +{ + unsigned long size; +#if __i386__ + return getsectdata("__IMPORT", "__jump_table", &size); +#elif __ppc__ + void* p = getsectdata("__TEXT", "__picsymbolstub1", &size); + if ( p != NULL ) + return p; + return getsectdata("__TEXT", "__symbol_stub1", &size); +#elif __ppc64__ + return getsectdata("__TEXT", "__picsymbolstub1", &size); +#elif __x86_64__ + return getsectdata("__TEXT", "__symbol_stub1", &size); +#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() +{ + void* stubAddr = getStubAddr(); +#if __i386__ + if ( stubAddr != NULL ) +#endif + { + checkStubs(stubAddr); + fooData = 1; + foo(); + checkStubs(stubAddr); + } + PASS("read-only-stubs"); + return EXIT_SUCCESS; +} + +#else + +int main() +{ + // iOS does not have modifiable stubs + PASS("read-only-stubs"); + return EXIT_SUCCESS; +} + +#endif + diff --git a/dyld/unit-tests/test-cases/restrict-environ/Makefile b/dyld/unit-tests/test-cases/restrict-environ/Makefile new file mode 100644 index 0000000..0d2aa80 --- /dev/null +++ b/dyld/unit-tests/test-cases/restrict-environ/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2009-2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile + RUN_AS_ROOT = +else + RUN_AS_USER = + RUN_AS_ROOT = sudo +endif + +PWD = `pwd` + +all-check: all check + +check: + ${RUN_AS_USER} $(PWD)/main-with-env 2>/dev/null + ${RUN_AS_ROOT} $(PWD)/main-with-env 2>/dev/null + +all: main + +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 main-with-env + diff --git a/dyld/unit-tests/test-cases/restrict-environ/main.c b/dyld/unit-tests/test-cases/restrict-environ/main.c new file mode 100644 index 0000000..e68d038 --- /dev/null +++ b/dyld/unit-tests/test-cases/restrict-environ/main.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include // dyld_process_is_restricted() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never see any DYLD_ environment variables +// + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + // verify no DYLD_ variables + const char** p; + for(p = envp; *p != NULL; p++) { + //fprintf(stderr, "%s\n", *p); + if ( strncmp(*p, "DYLD_", 5) == 0 ) { + FAIL("restrict-environ: found %s", *p); + return EXIT_SUCCESS; + } + } + // verify same as apple parameter + ++p; + if ( apple != p ) { + FAIL("restrict-environ: apple parameter not at end of envp"); + return EXIT_SUCCESS; + } + + // verify apple parameter is not NULL and ends in main + if ( *apple == NULL ) { + FAIL("restrict-environ: apple parameter is empty"); + return EXIT_SUCCESS; + } + if ( strstr(*apple, "/main") == NULL ) { + FAIL("restrict-environ: apple parameter is not path to main"); + return EXIT_SUCCESS; + } + + // verify SPI says process is restricted + if ( !dyld_process_is_restricted() ) { + FAIL("restrict-environ: dyld_process_is_restrictet() returns false"); + return EXIT_SUCCESS; + } + + + PASS("restrict-environ"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/restrict-executable_path/Makefile b/dyld/unit-tests/test-cases/restrict-executable_path/Makefile new file mode 100644 index 0000000..ceb2e3b --- /dev/null +++ b/dyld/unit-tests/test-cases/restrict-executable_path/Makefile @@ -0,0 +1,76 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +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 +# Use of @loader_path in restricted binaries is not allowed +# Use of relative paths in restricted binaries is not allowed +# + +all-check: all check + +check: + ${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 + +dir1/libfoo.dylib : foo.c + mkdir -p dir1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir1/libfoo.dylib -install_name @executable_path/dir1/libfoo.dylib + +dir2/libbar.dylib : foo.c + mkdir -p dir2 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir2/libbar.dylib -install_name @loader_path/dir2/libbar.dylib + +dir3/libbaz.dylib : foo.c + mkdir -p dir3 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o ./dir3/libbaz.dylib + +main_exe: main.c dir1/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_exe main.c dir1/libfoo.dylib -sectcreate __RESTRICT __restrict /dev/null + +main_loader: main.c dir2/libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_loader main.c dir2/libbar.dylib -sectcreate __RESTRICT __restrict /dev/null + +main_rel: main.c dir3/libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_rel main.c dir3/libbaz.dylib -sectcreate __RESTRICT __restrict /dev/null + + + +clean: + ${RM} ${RMFLAGS} *~ main_exe main_loader main_rel dir1 dir2 dir3 + diff --git a/dyld/unit-tests/test-cases/restrict-executable_path/foo.c b/dyld/unit-tests/test-cases/restrict-executable_path/foo.c new file mode 100644 index 0000000..d2b44c4 --- /dev/null +++ b/dyld/unit-tests/test-cases/restrict-executable_path/foo.c @@ -0,0 +1,5 @@ + + +int foo() { return 1; } + + diff --git a/dyld/unit-tests/test-cases/restrict-executable_path/main.c b/dyld/unit-tests/test-cases/restrict-executable_path/main.c new file mode 100644 index 0000000..de19bb6 --- /dev/null +++ b/dyld/unit-tests/test-cases/restrict-executable_path/main.c @@ -0,0 +1,14 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include // issetugid +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + foo(); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..d85c89b --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# 1) a main executable built with -rpath run with DYLD_FALLBACK_LIBRARY_PATH and linked rpath wins +# 2) a main executable built without -rpath run with DYLD_FALLBACK_LIBRARY_PATH and dylib found +# + + +all-check: all check + +check: + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/bad && ./main + export DYLD_FALLBACK_LIBRARY_PATH=`pwd`/good && ./main2 + +all: main main2 bad/libfoo.dylib + + +good/libfoo.dylib : foo.c + mkdir -p good + ${CC} foo.c -dynamiclib -o good/libfoo.dylib -install_name @rpath/libfoo.dylib + +bad/libfoo.dylib : foo.c + mkdir -p bad + ${CC} foo.c -DBAD -dynamiclib -o bad/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c good/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main good/libfoo.dylib -Wl,-rpath -Wl,@loader_path/good + +main2 : main.c good/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main2 good/libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main main2 good bad diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c b/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..a8fe5b0 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +int foo() +{ +#if BAD + return 0; +#else + return 1; +#endif +} diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c new file mode 100644 index 0000000..72afbf7 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_FALLBACK_LIBRARY_PATH/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-DYLD_FALLBACK_LIBRARY_PATH"); + else + FAIL("rpath-DYLD_FALLBACK_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..6a7c133 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable run with DYLD_LIBRARY_PATH will override its rpath +# + +all-check: all check + +check: + export DYLD_LIBRARY_PATH=`pwd`/hide1 && ./main + +all: main hide2/libfoo.dylib hide1/libfoo.dylib + + +hide1/libfoo.dylib : foo.c + mkdir -p hide1 + ${CC} foo.c -dynamiclib -o hide1/libfoo.dylib -install_name @rpath/libfoo.dylib + +hide2/libfoo.dylib : foo.c + mkdir -p hide2 + ${CC} foo.c -DBAD -dynamiclib -o hide2/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c hide2/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide2/libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide2 + +clean: + ${RM} ${RMFLAGS} *~ main hide1 hide2 diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c b/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..a8fe5b0 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/foo.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +int foo() +{ +#if BAD + return 0; +#else + return 1; +#endif +} diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..01a144b --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_LIBRARY_PATH/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-DYLD_LIBRARY_PATH"); + else + FAIL("rpath-DYLD_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile b/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile new file mode 100644 index 0000000..26692af --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/Makefile @@ -0,0 +1,28 @@ + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# DYLD_ROOT_PATH should apply to LC_RPATH rpaths +# + +all-check: all check + +check: + export DYLD_ROOT_PATH=`pwd` && ${PASS_IFF} ./main + +all: main + + +hide/libfoo.dylib : foo.c + mkdir -p hide + ${CC} foo.c -dynamiclib -o hide/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/libfoo.dylib -Wl,-rpath -Wl,/hide + + +clean: + ${RM} ${RMFLAGS} *~ main hide diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c b/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c new file mode 100644 index 0000000..c5563c5 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/foo.c @@ -0,0 +1,4 @@ +int foo() +{ + return 1; +} diff --git a/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c b/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c new file mode 100644 index 0000000..b379c36 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-DYLD_ROOT_PATH/main.c @@ -0,0 +1,16 @@ + +#include +#include +#include + +#include "test.h" + +extern int foo(); + +int main() +{ + if ( foo() ) + return EXIT_SUCCESS; + else + return EXIT_FAILURE; +} diff --git a/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile b/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile new file mode 100644 index 0000000..f235c02 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/Makefile @@ -0,0 +1,47 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable run with LD_LIBRARY_PATH can locate a dylib it links against +# + +all-check: all check + +check: + export LD_LIBRARY_PATH=`pwd`/hide/hole && ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c b/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c b/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c new file mode 100644 index 0000000..784dee3 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-LD_LIBRARY_PATH/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-LD_LIBRARY_PATH"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-basic/Makefile b/dyld/unit-tests/test-cases/rpath-basic/Makefile new file mode 100644 index 0000000..c559f75 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-basic/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath used to locate a dylib it links against +# + +all-check: all check + +check: + ./main + +all: main + + +hide/hole/libbar.dylib : bar.c + mkdir -p hide/hole + ${CC} bar.c -dynamiclib -o hide/hole/libbar.dylib -install_name @rpath/libbar.dylib + +hide/hole/libfoo.dylib : foo.c hide/hole/libbar.dylib + ${CC} foo.c hide/hole/libbar.dylib -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/hole + +clean: + ${RM} -rf *~ main hide diff --git a/dyld/unit-tests/test-cases/rpath-basic/bar.c b/dyld/unit-tests/test-cases/rpath-basic/bar.c new file mode 100644 index 0000000..b72a1a5 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-basic/bar.c @@ -0,0 +1,3 @@ +void bar() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-basic/foo.c b/dyld/unit-tests/test-cases/rpath-basic/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-basic/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-basic/main.c b/dyld/unit-tests/test-cases/rpath-basic/main.c new file mode 100644 index 0000000..bf6c7fa --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-basic/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-basic"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile new file mode 100644 index 0000000..f5ebbc7 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath calls into a dylib which calls +# dlopen(). The -rpath from the dylib should be used to +# find the dlopen path. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include -Wl,-rpath -Wl,${PWD}/hide/hole + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide libbar.dylib diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c new file mode 100644 index 0000000..17ab4f6 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/bar.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include + +#include "test.h" + +void bar() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } +} + + diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c new file mode 100644 index 0000000..1eb7429 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-in-dylib/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + + PASS("rpath-dlopen-in-dylib"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-indirect/Makefile b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/Makefile new file mode 100644 index 0000000..5856ea7 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath calls into a dylib which calls +# dlopen(). The -rpath from the main executable should be used to +# find the dlopen path. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib -I${TESTROOT}/include + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,${PWD}/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide libbar.dylib diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-indirect/bar.c b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/bar.c new file mode 100644 index 0000000..160109b --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/bar.c @@ -0,0 +1,23 @@ + +#include +#include +#include + +#include "test.h" + +void bar() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen-indirect: %s", dlerror()); + exit(EXIT_SUCCESS); + } +} + + diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-indirect/foo.c b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-indirect/main.c b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/main.c new file mode 100644 index 0000000..ba7efb6 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-indirect/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + + PASS("rpath-dlopen-indirect"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-leak/Makefile b/dyld/unit-tests/test-cases/rpath-dlopen-leak/Makefile new file mode 100644 index 0000000..9d7195a --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-leak/Makefile @@ -0,0 +1,65 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# verify there are no leaks with using @loader_path based rpaths +# + +# leaks does not work on rosetta processes +EMULATED = 0 +ifeq "ppc" "$(ARCH)" + MACHINE = $(shell arch) + ifeq "i386" "$(MACHINE)" + EMULATED = 1 + endif +endif + +all-check: all check + +check: + if [ ${EMULATED} == 0 ]; \ + then \ + ./main ; \ + ./main.bad ; \ + else \ + echo "XFAIL rpath-dlopen-leak"; \ + fi; + +all: main main.bad + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@loader_path/hide/hole + +main.bad : main.c hide/hole/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main.bad -Wl,-rpath -Wl,@loader_path/bad + +clean: + ${RM} ${RMFLAGS} *~ main main.bad hide/hole/libfoo.dylib hide diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-leak/foo.c b/dyld/unit-tests/test-cases/rpath-dlopen-leak/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-leak/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-leak/main.c b/dyld/unit-tests/test-cases/rpath-dlopen-leak/main.c new file mode 100644 index 0000000..993ea25 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-leak/main.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + + +#include "test.h" + + +int main() +{ + for (int i=0; i < 100; ++i) { + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle != NULL ) + dlclose(handle); + } + + // execute leaks command on myself + char cmd[512]; + int result = system(cmd); + if ( result == EXIT_SUCCESS ) + PASS("rpath-dlopen-leak"); + else + FAIL("rpath-dlopen-leak"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/Makefile b/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/Makefile new file mode 100644 index 0000000..c166d10 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +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 diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c b/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c b/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c new file mode 100644 index 0000000..9857c71 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen-rm-executable/main.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen/Makefile b/dyld/unit-tests/test-cases/rpath-dlopen/Makefile new file mode 100644 index 0000000..f08dfc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen/Makefile @@ -0,0 +1,48 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath and uses dlopen can find a dylib +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,${PWD}/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/dyld/unit-tests/test-cases/rpath-dlopen/foo.c b/dyld/unit-tests/test-cases/rpath-dlopen/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-dlopen/main.c b/dyld/unit-tests/test-cases/rpath-dlopen/main.c new file mode 100644 index 0000000..d413906 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-dlopen/main.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +int main() +{ + void* handle = dlopen("libfoo.dylib", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("rpath-dlopen: %s", dlerror()); + return EXIT_SUCCESS; + } + + void* pFoo = dlsym(handle, "foo"); + if ( pFoo == NULL ) { + FAIL("rpath-dlopen: %s", dlerror()); + return EXIT_SUCCESS; + } + + PASS("rpath-dlopen"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-executable_path/Makefile b/dyld/unit-tests/test-cases/rpath-executable_path/Makefile new file mode 100644 index 0000000..de9f672 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-executable_path/Makefile @@ -0,0 +1,51 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @executable_path. +# The @executable_path in the rpath should expand at runtime to the directory +# of the main executable. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + +libbar.dylib : bar.c hide/hole/libfoo.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib hide/hole/libfoo.dylib + +main : main.c libbar.dylib + ${CC} -I${TESTROOT}/include main.c -o main libbar.dylib -Wl,-rpath -Wl,@executable_path/hide/hole + +clean: + ${RM} -rf *~ main hide libbar.dylib diff --git a/dyld/unit-tests/test-cases/rpath-executable_path/bar.c b/dyld/unit-tests/test-cases/rpath-executable_path/bar.c new file mode 100644 index 0000000..31a758c --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-executable_path/bar.c @@ -0,0 +1,8 @@ +extern void foo(); + +void bar() +{ + foo(); +} + + diff --git a/dyld/unit-tests/test-cases/rpath-executable_path/foo.c b/dyld/unit-tests/test-cases/rpath-executable_path/foo.c new file mode 100644 index 0000000..1624757 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-executable_path/foo.c @@ -0,0 +1,5 @@ + + +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-executable_path/main.c b/dyld/unit-tests/test-cases/rpath-executable_path/main.c new file mode 100644 index 0000000..4612793 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-executable_path/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void bar(); + +int main() +{ + bar(); + PASS("rpath-executable_path"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-indirect-suid/Makefile b/dyld/unit-tests/test-cases/rpath-indirect-suid/Makefile new file mode 100644 index 0000000..6f96b7e --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-indirect-suid/Makefile @@ -0,0 +1,78 @@ +## +# 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@ +## +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 + +# +# a setuid main executable linked with -rpath links against a dylib +# that uses rpath to find another dylib. It is an error if +# LC_RPATH uses @loader_path or a relative path, but ok if it is an absolute path +# + +all-check: all check + +check: + ./main || echo "FAIL rpath-indirect-suid absolute path" + ${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 + +hide/hole/libbar.dylib : bar.c + mkdir -p hide/hole + ${CC} bar.c -dynamiclib -o hide/hole/libbar.dylib -install_name @rpath/libbar.dylib + +libfoo.dylib : foo.c hide/hole/libbar.dylib + ${CC} foo.c -dynamiclib -o "${PWD}/libfoo.dylib" hide/hole/libbar.dylib + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/hole + sudo chown root main + sudo chmod 4755 main + +main_bad1 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad1 libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide/hole + sudo chown root main_bad1 + sudo chmod 4755 main_bad1 + +main_bad2 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad2 libfoo.dylib -Wl,-rpath -Wl,hide/hole + sudo chown root main_bad2 + sudo chmod 4755 main_bad2 + +main_bad3 : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -DDEFAULT_FAIL -o main_bad3 libfoo.dylib + ln -s hide/hole @rpath + sudo chown root main_bad3 + sudo chmod 4755 main_bad3 + +clean: + ${RM} ${RMFLAGS} *~ main main_bad1 main_bad2 main_bad3 hide libfoo.dylib @rpath diff --git a/dyld/unit-tests/test-cases/rpath-indirect-suid/bar.c b/dyld/unit-tests/test-cases/rpath-indirect-suid/bar.c new file mode 100644 index 0000000..b1b8fd7 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-indirect-suid/bar.c @@ -0,0 +1,6 @@ + +void bar() +{ +} + + diff --git a/dyld/unit-tests/test-cases/rpath-indirect-suid/foo.c b/dyld/unit-tests/test-cases/rpath-indirect-suid/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-indirect-suid/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-indirect-suid/main.c b/dyld/unit-tests/test-cases/rpath-indirect-suid/main.c new file mode 100644 index 0000000..bdea9f9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-indirect-suid/main.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); +#if DEFAULT_FAIL + FAIL("rpath-indirect-setuid"); +#else + PASS("rpath-indirect-setuid"); +#endif + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-install-name/Makefile b/dyld/unit-tests/test-cases/rpath-install-name/Makefile new file mode 100644 index 0000000..622ade0 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-install-name/Makefile @@ -0,0 +1,55 @@ +## +# 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 diff --git a/dyld/unit-tests/test-cases/rpath-install-name/bar.c b/dyld/unit-tests/test-cases/rpath-install-name/bar.c new file mode 100644 index 0000000..a27d788 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-install-name/bar.c @@ -0,0 +1,29 @@ +/* + * 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(); +} diff --git a/dyld/unit-tests/test-cases/rpath-install-name/main.c b/dyld/unit-tests/test-cases/rpath-install-name/main.c new file mode 100644 index 0000000..87e0ae4 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-install-name/main.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +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; +} diff --git a/dyld/unit-tests/test-cases/rpath-install-name/stuff.c b/dyld/unit-tests/test-cases/rpath-install-name/stuff.c new file mode 100644 index 0000000..6b60cb2 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-install-name/stuff.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + + +void stuff() +{ +#if BETTER + PASS("rpath-install-name"); +#else + FAIL("rpath-install-name"); +#endif +} diff --git a/dyld/unit-tests/test-cases/rpath-introspection/Makefile b/dyld/unit-tests/test-cases/rpath-introspection/Makefile new file mode 100644 index 0000000..ec5d680 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-introspection/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2012 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# Checks that @rpath that expands to have ../ in it will get realpathed and +# .. will not be seen when introspecting dylibs +# + +all-check: all check + +check: + ./main + +all: + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide/../hide/hole + +clean: + ${RM} -rf *~ main hide diff --git a/dyld/unit-tests/test-cases/rpath-introspection/foo.c b/dyld/unit-tests/test-cases/rpath-introspection/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-introspection/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-introspection/main.c b/dyld/unit-tests/test-cases/rpath-introspection/main.c new file mode 100644 index 0000000..6eba6da --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-introspection/main.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + + // make sure foo's path does not have .. in it + int count = _dyld_image_count(); + bool found = false; + for(int i=0; i < count; ++i) { + const char* name = _dyld_get_image_name(i); + if ( strstr(name, "libfoo.dylib") == 0 ) { + found = true; + if ( strstr(name, "..") != NULL ) { + FAIL("rpath-introspection: _dyld_get_image_name(%d) returned %s", i, name); + return EXIT_SUCCESS; + } + } + } + if ( !found ) { + FAIL("rpath-introspection: _dyld_get_image_name() never returned libfoo.dylib"); + return EXIT_SUCCESS; + } + + // make sure dladdr path does not have .. in it + Dl_info info; + if ( dladdr(&foo, &info) == 0 ) { + FAIL("rpath-introspection: dladdr() failed"); + return EXIT_SUCCESS; + } + if ( strstr(info.dli_fname, "..") != NULL ) { + FAIL("rpath-introspection: dladdr() returned path with .. in it"); + return EXIT_SUCCESS; + } + + PASS("rpath-introspection"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile new file mode 100644 index 0000000..220b1ad --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2006-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @loader_path. +# The @loader_path in the rpath should expand at runtime to the directory +# of the main executable during calls to dlopen() +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libbaz.dylib : baz.c + mkdir -p hide/hole + ${CC} baz.c -dynamiclib -o hide/hole/libbaz.dylib -install_name @rpath/libbaz.dylib + +libbar.dylib : bar.c hide/hole/libbaz.dylib + ${CC} bar.c -dynamiclib -o libbar.dylib hide/hole/libbaz.dylib + +libfoo.dylib : foo.c libbar.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main -Wl,-rpath -Wl,@loader_path/hide/hole libfoo.dylib + +clean: + ${RM} -rf *~ main hide libbar.dylib libfoo.dylib \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c new file mode 100644 index 0000000..f9c0742 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/bar.c @@ -0,0 +1,8 @@ +extern void baz(); + +void bar() +{ + baz(); +} + + diff --git a/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c new file mode 100644 index 0000000..0793d97 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/baz.c @@ -0,0 +1,6 @@ + +void baz() +{ +} + + diff --git a/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c new file mode 100644 index 0000000..92d2257 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/foo.c @@ -0,0 +1,8 @@ +#include +#include +#include + +bool foo() +{ + return (dlopen("./libbar.dylib", RTLD_NOW) != NULL); +} diff --git a/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/main.c b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/main.c new file mode 100644 index 0000000..69b4a59 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path-dlopen/main.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include + +#include "test.h" + +extern bool foo(); + +int main() +{ + if ( foo() ) + PASS("rpath-loader_path-dlopen"); + else + FAIL("rpath-loader_path-dlopen: %s", dlerror()); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-loader_path/Makefile b/dyld/unit-tests/test-cases/rpath-loader_path/Makefile new file mode 100644 index 0000000..addee06 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path/Makefile @@ -0,0 +1,49 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# a main executable linked with -rpath that uses @loader_path. +# The @loader_path in the rpath should expand at runtime to the directory +# of the binary. +# + +all-check: all check + +check: + ./main + +all: main + +hide/hole/libfoo.dylib : foo.c + mkdir -p hide/hole + ${CC} foo.c -dynamiclib -o hide/hole/libfoo.dylib -install_name @rpath/libfoo.dylib + + +main : main.c hide/hole/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main hide/hole/libfoo.dylib -Wl,-rpath -Wl,@loader_path/hide/hole + +clean: + ${RM} ${RMFLAGS} *~ main hide/hole/libfoo.dylib hide diff --git a/dyld/unit-tests/test-cases/rpath-loader_path/foo.c b/dyld/unit-tests/test-cases/rpath-loader_path/foo.c new file mode 100644 index 0000000..79572f9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path/foo.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-loader_path/main.c b/dyld/unit-tests/test-cases/rpath-loader_path/main.c new file mode 100644 index 0000000..bf6c7fa --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-loader_path/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-basic"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-nesting/Makefile b/dyld/unit-tests/test-cases/rpath-nesting/Makefile new file mode 100644 index 0000000..14e3a99 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-nesting/Makefile @@ -0,0 +1,57 @@ +## +# Copyright (c) 2006 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# The main executable supplies an rpath. libfoo.dylib supplies an +# rpath. libfoo.dylib needs libbar.dylib and libbaz.dylib. One +# is found from the main executable's rpath and one from libfoo.dylib's +# rpath. +# + +all-check: all check + +check: + ./main + +all: main + +hide1/libbar.dylib : bar.c + mkdir -p hide1 + ${CC} bar.c -dynamiclib -o hide1/libbar.dylib -install_name @rpath/libbar.dylib + +hide2/libbaz.dylib : baz.c + mkdir -p hide2 + ${CC} baz.c -dynamiclib -o hide2/libbaz.dylib -install_name @rpath/libbaz.dylib + +libfoo.dylib : foo.c hide1/libbar.dylib hide2/libbaz.dylib + ${CC} foo.c -dynamiclib -o libfoo.dylib hide1/libbar.dylib hide2/libbaz.dylib -Wl,-rpath -Wl,${PWD}/hide2 + +main : main.c libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o main libfoo.dylib -Wl,-rpath -Wl,${PWD}/hide1 + +clean: + ${RM} ${RMFLAGS} *~ main hide1 hide2 libfoo.dylib diff --git a/dyld/unit-tests/test-cases/rpath-nesting/bar.c b/dyld/unit-tests/test-cases/rpath-nesting/bar.c new file mode 100644 index 0000000..d47ba49 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-nesting/bar.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +void bar() +{ +} + + diff --git a/dyld/unit-tests/test-cases/rpath-nesting/baz.c b/dyld/unit-tests/test-cases/rpath-nesting/baz.c new file mode 100644 index 0000000..dc61b62 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-nesting/baz.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +void baz() +{ +} + + diff --git a/dyld/unit-tests/test-cases/rpath-nesting/foo.c b/dyld/unit-tests/test-cases/rpath-nesting/foo.c new file mode 100644 index 0000000..7b46c28 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-nesting/foo.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern void bar(); +extern void baz(); + +void foo() +{ + bar(); + baz(); +} diff --git a/dyld/unit-tests/test-cases/rpath-nesting/main.c b/dyld/unit-tests/test-cases/rpath-nesting/main.c new file mode 100644 index 0000000..47d3ae2 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-nesting/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + + PASS("rpath-nesting"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/rpath-no-trailing-slash/Makefile b/dyld/unit-tests/test-cases/rpath-no-trailing-slash/Makefile new file mode 100644 index 0000000..e10933d --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-no-trailing-slash/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2014 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +PWD = $(shell pwd) + +# +# a main executable linked with -rpath used to locate a dylib it links against +# + +all-check: all check + +check: + ./hole/main + +all: main + + +main: + mkdir -p hole + ${CC} foo.c -dynamiclib -o hole/libfoo.dylib -install_name @rpath/libfoo.dylib + ${CC} -I${TESTROOT}/include main.c -o hole/main hole/libfoo.dylib -Wl,-rpath,@executable_path + +clean: + ${RM} -rf *~ hole diff --git a/dyld/unit-tests/test-cases/rpath-no-trailing-slash/foo.c b/dyld/unit-tests/test-cases/rpath-no-trailing-slash/foo.c new file mode 100644 index 0000000..3695dc9 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-no-trailing-slash/foo.c @@ -0,0 +1,3 @@ +void foo() +{ +} diff --git a/dyld/unit-tests/test-cases/rpath-no-trailing-slash/main.c b/dyld/unit-tests/test-cases/rpath-no-trailing-slash/main.c new file mode 100644 index 0000000..97bfaa2 --- /dev/null +++ b/dyld/unit-tests/test-cases/rpath-no-trailing-slash/main.c @@ -0,0 +1,14 @@ +#include +#include +#include + +#include "test.h" + +extern void foo(); + +int main() +{ + foo(); + PASS("rpath-no-trailing-slash"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/shared-cache-symlink/Makefile b/dyld/unit-tests/test-cases/shared-cache-symlink/Makefile new file mode 100644 index 0000000..b8a5145 --- /dev/null +++ b/dyld/unit-tests/test-cases/shared-cache-symlink/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/shared-cache-symlink/main.c b/dyld/unit-tests/test-cases/shared-cache-symlink/main.c new file mode 100644 index 0000000..fc038c1 --- /dev/null +++ b/dyld/unit-tests/test-cases/shared-cache-symlink/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libz.1.dylib should be in the shared cache +// libz.dylib is a symlink to libz.dylib +// we want to verify that dlopening the symlink name will use the one in the shared cache + + +int main() +{ + void* libzHandle = dlopen("/usr/lib/libz.dylib", RTLD_LAZY); + if ( libzHandle == NULL ) { + FAIL("shared-cache-symlink: dlopen(/usr/lib/libz.dylib, RTLD_LAZY) failed: %s", dlerror()); + return EXIT_SUCCESS; + } + + // get address of strcmp() + void* sym = dlsym(libzHandle, "inflate"); + if ( sym == NULL ) { + FAIL("shared-cache-symlink: dlsym(handle, \"inflate\") failed"); + return EXIT_SUCCESS; + } + + Dl_info info; + if ( dladdr(sym, &info) == 0 ) { + FAIL("shared-cache-symlink: dladdr(sym, xx) failed"); + return EXIT_SUCCESS; + } + + 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"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/shared-region-overlap/Makefile b/dyld/unit-tests/test-cases/shared-region-overlap/Makefile new file mode 100644 index 0000000..8a194a2 --- /dev/null +++ b/dyld/unit-tests/test-cases/shared-region-overlap/Makefile @@ -0,0 +1,47 @@ +## +# 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 diff --git a/dyld/unit-tests/test-cases/shared-region-overlap/main.c b/dyld/unit-tests/test-cases/shared-region-overlap/main.c new file mode 100644 index 0000000..83dfd8a --- /dev/null +++ b/dyld/unit-tests/test-cases/shared-region-overlap/main.c @@ -0,0 +1,12 @@ +#include // EXIT_SUCCESS + +#include "test.h" + + +int +main() +{ + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/suid-environ/Makefile b/dyld/unit-tests/test-cases/suid-environ/Makefile new file mode 100644 index 0000000..6356a4a --- /dev/null +++ b/dyld/unit-tests/test-cases/suid-environ/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2006-2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + +ifeq "$(OS_NAME)" "iPhoneOS" + RUN_AS_USER = login -f -l mobile +else + RUN_AS_USER = +endif + +PWD = `pwd` + +all-check: all check + +check: + ${RUN_AS_USER} $(PWD)/main-with-env 2>/dev/null + +all: main + +main: main.c + ${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 main-with-env + diff --git a/dyld/unit-tests/test-cases/suid-environ/main.c b/dyld/unit-tests/test-cases/suid-environ/main.c new file mode 100644 index 0000000..43a9749 --- /dev/null +++ b/dyld/unit-tests/test-cases/suid-environ/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// +// binaries set to run as some other user id never see any DYLD_ environment variables +// + +int main(int argc, const char* argv[], const char* envp[], const char* apple[]) +{ + // verify no DYLD_ variables + const char** p; + for(p = envp; *p != NULL; p++) { + //fprintf(stderr, "%s\n", *p); + if ( strncmp(*p, "DYLD_", 5) == 0 ) { + FAIL("suid-environ: found %s", *p); + return EXIT_SUCCESS; + } + } + // verify same as apple parameter + ++p; + if ( apple != p ) { + FAIL("suid-environ: apple parameter not at end of envp"); + return EXIT_SUCCESS; + } + + // verify apple parameter is not NULL and ends in main + if ( *apple == NULL ) { + FAIL("suid-environ: apple parameter is empty"); + return EXIT_SUCCESS; + } + if ( strstr(*apple, "/main") == NULL ) { + FAIL("suid-environ: apple parameter is not path to main"); + return EXIT_SUCCESS; + } + + PASS("suid-environ"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/suid-executable_path/Makefile b/dyld/unit-tests/test-cases/suid-executable_path/Makefile new file mode 100644 index 0000000..aab4ca2 --- /dev/null +++ b/dyld/unit-tests/test-cases/suid-executable_path/Makefile @@ -0,0 +1,90 @@ +## +# 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@ +## +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 +# Use of @loader_path in setuid binaries is not allowed +# Use of relative paths in setuid binaries is not allowed +# + +all-check: all check + +check: + ./main_exe "setuid-executable_path" || echo "FAIL setuid-executable_path @executable_path not setuid" + ./main_loader "setuid-executable_path" || echo "FAIL setuid-executable_path @loader_path not setuid" + ./main_rel "setuid-executable_path" || echo "FAIL setuid-executable_path relative path not setuid" + ${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 + + + +all: main_exe main_exe-suid main_loader main_loader-suid main_rel main_rel-suid + +dir1/libfoo.dylib : foo.c + mkdir -p dir1 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir1/libfoo.dylib -install_name @executable_path/dir1/libfoo.dylib + +dir2/libbar.dylib : foo.c + mkdir -p dir2 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o dir2/libbar.dylib -install_name @loader_path/dir2/libbar.dylib + +dir3/libbaz.dylib : foo.c + mkdir -p dir3 + ${CC} ${CCFLAGS} foo.c -dynamiclib -o ./dir3/libbaz.dylib + +main_exe: main.c dir1/libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_exe main.c dir1/libfoo.dylib + cp main_exe main_exe-suid + sudo chown root main_exe-suid + sudo chmod 4755 main_exe-suid + +main_loader: main.c dir2/libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_loader main.c dir2/libbar.dylib + cp main_loader main_loader-suid + sudo chown root main_loader-suid + sudo chmod 4755 main_loader-suid + +main_rel: main.c dir3/libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main_rel main.c dir3/libbaz.dylib + cp main_rel main_rel-suid + sudo chown root main_rel-suid + sudo chmod 4755 main_rel-suid + + + + +clean: + ${RM} ${RMFLAGS} *~ main_exe main_exe-suid main_loader main_loader-suid main_rel main_rel-suid dir1 dir2 dir3 + diff --git a/dyld/unit-tests/test-cases/suid-executable_path/foo.c b/dyld/unit-tests/test-cases/suid-executable_path/foo.c new file mode 100644 index 0000000..d2b44c4 --- /dev/null +++ b/dyld/unit-tests/test-cases/suid-executable_path/foo.c @@ -0,0 +1,5 @@ + + +int foo() { return 1; } + + diff --git a/dyld/unit-tests/test-cases/suid-executable_path/main.c b/dyld/unit-tests/test-cases/suid-executable_path/main.c new file mode 100644 index 0000000..aa63e85 --- /dev/null +++ b/dyld/unit-tests/test-cases/suid-executable_path/main.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // strcmp(), strncmp() +#include // issetugid +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern int foo(); + +int main(int argc, const char* argv[]) +{ + foo(); + if ( issetugid() ) + FAIL(argv[1]); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/sym-link-load/Makefile b/dyld/unit-tests/test-cases/sym-link-load/Makefile new file mode 100644 index 0000000..a0feb3a --- /dev/null +++ b/dyld/unit-tests/test-cases/sym-link-load/Makefile @@ -0,0 +1,85 @@ +## +# 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 + +### +### This test case is to verify that dyld cannot be tricked into loading +### the "same" library twice. +### +### The tricky scenario is: +### 1) a library dyld is to load is specified with a symlink path +### 2) the library the that is the target of the symlink is already loaded +### via a different path. In this case becuase of DYLD_LIBRARY_PATH. +### +### +### +### + +PWD = $(shell pwd) + +all-check: all check + +check: + ./main + export DYLD_LIBRARY_PATH="${PWD}/fake" && ./main + + +all: main real/liblink.dylib real/libtest.dylib fake/libtest.dylib + + +main: main.c stub/libtest.dylib stub/liblink.dylib real/libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c stub/libtest.dylib stub/liblink.dylib real/libbase.dylib + +stub/libtest.dylib: test.c + mkdir -p stub + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c -DDO_NOTHING -o stub/libtest.dylib -install_name "${PWD}/real/libtest.dylib" + +stub/liblink.dylib: link.c + mkdir -p stub + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib link.c -o stub/liblink.dylib -install_name "${PWD}/real/liblink.dylib" + + +real/libbase.dylib: base.c + mkdir -p real + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib base.c -o "${PWD}/real/libbase.dylib" + +real/libtest.dylib: test.c real/libbase.dylib + mkdir -p real + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "${PWD}/real/libtest.dylib" + +real/liblink.dylib: link.c + mkdir -p real + cd real && ln -s libtest.dylib liblink.dylib + + +fake/libtest.dylib: test.c real/libbase.dylib + mkdir -p fake + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib test.c real/libbase.dylib -o "${PWD}/fake/libtest.dylib" -install_name "${PWD}/real/libtest.dylib" + + + + +clean: + ${RM} ${RMFLAGS} *~ main real stub fake + diff --git a/dyld/unit-tests/test-cases/sym-link-load/base.c b/dyld/unit-tests/test-cases/sym-link-load/base.c new file mode 100644 index 0000000..663274e --- /dev/null +++ b/dyld/unit-tests/test-cases/sym-link-load/base.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static int initCount = 0; + +void setState(int x) +{ + ++initCount; +} + + +void baseCheck() +{ + switch ( initCount ) { + case 0: + FAIL("sym-link-load no initializers run"); + break; + case 1: + PASS("sym-link-load"); + break; + case 2: + default: + FAIL("sym-link-load initializers double run (library double loaded)"); + break; + } +} + diff --git a/dyld/unit-tests/test-cases/sym-link-load/base.h b/dyld/unit-tests/test-cases/sym-link-load/base.h new file mode 100644 index 0000000..1dbf697 --- /dev/null +++ b/dyld/unit-tests/test-cases/sym-link-load/base.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +extern void setState(int); +extern void baseCheck(); + diff --git a/dyld/unit-tests/test-cases/sym-link-load/link.c b/dyld/unit-tests/test-cases/sym-link-load/link.c new file mode 100644 index 0000000..42c87ba --- /dev/null +++ b/dyld/unit-tests/test-cases/sym-link-load/link.c @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + diff --git a/dyld/unit-tests/test-cases/sym-link-load/main.c b/dyld/unit-tests/test-cases/sym-link-load/main.c new file mode 100644 index 0000000..b210384 --- /dev/null +++ b/dyld/unit-tests/test-cases/sym-link-load/main.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/sym-link-load/test.c b/dyld/unit-tests/test-cases/sym-link-load/test.c new file mode 100644 index 0000000..5b1e221 --- /dev/null +++ b/dyld/unit-tests/test-cases/sym-link-load/test.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + +#ifndef DO_NOTHING + +// should run second because first initiallzer in first .o file +static __attribute__((constructor)) void test_init() +{ + //fprintf(stderr, "test_init()\n"); + setState(1); +} + +#endif diff --git a/dyld/unit-tests/test-cases/symbol-resolver-basic/Makefile b/dyld/unit-tests/test-cases/symbol-resolver-basic/Makefile new file mode 100644 index 0000000..f38f287 --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-basic/Makefile @@ -0,0 +1,55 @@ +## +# 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_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 + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-basic/foo.c b/dyld/unit-tests/test-cases/symbol-resolver-basic/foo.c new file mode 100644 index 0000000..f7b6b4b --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-basic/foo.c @@ -0,0 +1,47 @@ +/* + * 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 + + +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; +} + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-basic/foo2.c b/dyld/unit-tests/test-cases/symbol-resolver-basic/foo2.c new file mode 100644 index 0000000..3c80d3f --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-basic/foo2.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +extern int foo(); + +// test that calls to resolver based function in same dylib work +int fooPlusOne() +{ + return foo() + 1; +} + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-basic/main.c b/dyld/unit-tests/test-cases/symbol-resolver-basic/main.c new file mode 100644 index 0000000..670984e --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-basic/main.c @@ -0,0 +1,53 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/symbol-resolver-interposed/Makefile b/dyld/unit-tests/test-cases/symbol-resolver-interposed/Makefile new file mode 100644 index 0000000..2d0c380 --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-interposed/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Test that resolver based functions can be interposed +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + export DYLD_INSERT_LIBRARIES=myfoo.interposelib && ./main + +all_1: + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + ${CC} ${CCFLAGS} -dynamiclib myfoo.c libfoo.dylib -o myfoo.interposelib + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib myfoo.interposelib + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-interposed/foo.c b/dyld/unit-tests/test-cases/symbol-resolver-interposed/foo.c new file mode 100644 index 0000000..8e9ff7e --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-interposed/foo.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + +static int which_foo = 0; + +void set_foo(int x) +{ + which_foo = x; +} + +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 ( which_foo == 0 ) + return &foo_zero; + else + return &foo_ten; +} + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-interposed/foo.h b/dyld/unit-tests/test-cases/symbol-resolver-interposed/foo.h new file mode 100644 index 0000000..2c1d57b --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-interposed/foo.h @@ -0,0 +1,3 @@ + +extern int foo(); +extern void set_foo(int); diff --git a/dyld/unit-tests/test-cases/symbol-resolver-interposed/main.c b/dyld/unit-tests/test-cases/symbol-resolver-interposed/main.c new file mode 100644 index 0000000..32c1ccb --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-interposed/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#include "foo.h" + +int main(int argc, const char* argv[]) +{ + if ( getenv("DYLD_INSERT_LIBRARIES") == NULL ) { + if ( foo() != 0 ) { + FAIL("symbol-resolver-interposed: foo() != 0"); + return EXIT_SUCCESS; + } + } + else { + if ( foo() != 20 ) { + FAIL("symbol-resolver-interposed: foo() != 20"); + return EXIT_SUCCESS; + } + } + + PASS("symbol-resolver-interposed"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c b/dyld/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c new file mode 100644 index 0000000..6212331 --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-interposed/myfoo.c @@ -0,0 +1,11 @@ + +#include +#include "foo.h" + +int myfoo() +{ + foo(); + return 20; +} + +DYLD_INTERPOSE(myfoo, foo) diff --git a/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile new file mode 100644 index 0000000..dc1021c --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/Makefile @@ -0,0 +1,56 @@ +## +# Copyright (c) 2010 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +## +## Test that resolver function is not run before initializer +## even when lazy pointers are bound early. +## + +all-check: all_$(OS_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -dynamiclib bar.c -o libbar.dylib -I${TESTROOT}/include + ${CC} ${CCFLAGS} -dynamiclib foo.c libbar.dylib -o libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + + +clean: + ${RM} ${RMFLAGS} main libfoo.dylib libbar.dylib + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c new file mode 100644 index 0000000..870c431 --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool hasBeenInited = false; + +__attribute__((constructor)) +void myInit() +{ + hasBeenInited = true; +} + + +int barGood() +{ + return 1; +} + +int barBad() +{ + return 0; +} + + +// This bar is a "resolver" function that return the actual address of "bar" +void* bar() +{ + __asm__(".symbol_resolver _bar"); // magic until we have compiler support + // Resolver function run before initializers + if ( hasBeenInited ) + return &barGood; + else + return &barBad; +} + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o new file mode 100644 index 0000000..9257269 Binary files /dev/null and b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/bar.o differ diff --git a/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c new file mode 100644 index 0000000..bb9054c --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include + + +extern int bar(); + +int foo() +{ + return bar(); +} + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c new file mode 100644 index 0000000..212d02f --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-lazy-prebound/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +typedef int (*Func_t)(void); + +int main() +{ + // RTLD_NOW force lazy pointers to be bound early + void* handle = dlopen("libfoo.dylib", RTLD_NOW); + Func_t pFoo = (Func_t)dlsym(handle, "foo"); + int result = (*pFoo)(); + if ( result == 0 ) + FAIL("symbol-resolver-lazy-prebound: resolver ran before initializer"); + else + PASS("symbol-resolver-lazy-prebound"); + + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/symbol-resolver-pointer/Makefile b/dyld/unit-tests/test-cases/symbol-resolver-pointer/Makefile new file mode 100644 index 0000000..bf8e689 --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-pointer/Makefile @@ -0,0 +1,54 @@ +## +# 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -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 + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-pointer/foo.c b/dyld/unit-tests/test-cases/symbol-resolver-pointer/foo.c new file mode 100644 index 0000000..71d67bc --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-pointer/foo.c @@ -0,0 +1,64 @@ +/* + * 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 + +#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"); +} + + + + diff --git a/dyld/unit-tests/test-cases/symbol-resolver-pointer/main.c b/dyld/unit-tests/test-cases/symbol-resolver-pointer/main.c new file mode 100644 index 0000000..9e7a73b --- /dev/null +++ b/dyld/unit-tests/test-cases/symbol-resolver-pointer/main.c @@ -0,0 +1,35 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void check(); + + +int main() +{ + check(); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/template/Makefile b/dyld/unit-tests/test-cases/template/Makefile new file mode 100644 index 0000000..ec6d1cb --- /dev/null +++ b/dyld/unit-tests/test-cases/template/Makefile @@ -0,0 +1,37 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/template/main.c b/dyld/unit-tests/test-cases/template/main.c new file mode 100644 index 0000000..083736b --- /dev/null +++ b/dyld/unit-tests/test-cases/template/main.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + PASS("template"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/terminator-bounds-check/Makefile b/dyld/unit-tests/test-cases/terminator-bounds-check/Makefile new file mode 100644 index 0000000..71d7002 --- /dev/null +++ b/dyld/unit-tests/test-cases/terminator-bounds-check/Makefile @@ -0,0 +1,36 @@ +## +# 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: + ${PASS_IFF_FAILURE} ./main + +all: + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ main + diff --git a/dyld/unit-tests/test-cases/terminator-bounds-check/main.c b/dyld/unit-tests/test-cases/terminator-bounds-check/main.c new file mode 100644 index 0000000..50177bb --- /dev/null +++ b/dyld/unit-tests/test-cases/terminator-bounds-check/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern void* terminators[] __asm__("section$start$__DATA$__mod_term_func"); + + +__attribute__((destructor)) +void myterm() +{ + FAIL("terminator-bounds-check, terminator called"); + exit(0); +} + + + +int +main() +{ + // stomp on terminator routine to point into libSystem (_rand) + terminators[0] = &rand; + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/text-perm-alt-segment/Makefile b/dyld/unit-tests/test-cases/text-perm-alt-segment/Makefile new file mode 100644 index 0000000..163719d --- /dev/null +++ b/dyld/unit-tests/test-cases/text-perm-alt-segment/Makefile @@ -0,0 +1,54 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +### +### This test case is to verify __TEXT relocations work in dylibs +### +### + + +ifeq "i386" "$(ARCH)" + EXTRA_DEP = libfoo.dylib +else + +endif + +all-check: all check + +check: + ./main + +all: ${EXTRA_DEP} + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c ${EXTRA_DEP} + +libfoo.dylib: + ${CC} ${CCFLAGS} foo.c -c + ${LD} -r -arch ${ARCH} foo.o -rename_section __TEXT __text MYTEXT mytext -o foo2.o + ${CC} ${CCFLAGS} -dynamiclib foo2.o -o libfoo.dylib -Wl,-segprot,MYTEXT,rwx,r-x -read_only_relocs suppress + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib foo.o foo2.o + diff --git a/dyld/unit-tests/test-cases/text-perm-alt-segment/foo.c b/dyld/unit-tests/test-cases/text-perm-alt-segment/foo.c new file mode 100644 index 0000000..9f41b6b --- /dev/null +++ b/dyld/unit-tests/test-cases/text-perm-alt-segment/foo.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL + +int x = 5; + +__attribute__((constructor)) +void myInit() { + x = 10; +} + + +__attribute__((section("MYTEXT,myconst,regular,pure_instructions"))) +const void* p = &myInit; + + diff --git a/dyld/unit-tests/test-cases/text-perm-alt-segment/main.c b/dyld/unit-tests/test-cases/text-perm-alt-segment/main.c new file mode 100644 index 0000000..e6f9eed --- /dev/null +++ b/dyld/unit-tests/test-cases/text-perm-alt-segment/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +int main(int argc, const char* argv[]) +{ + PASS("text-reloc-alt-segment"); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/text-relocs-perms/Makefile b/dyld/unit-tests/test-cases/text-relocs-perms/Makefile new file mode 100644 index 0000000..2e1ec0f --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs-perms/Makefile @@ -0,0 +1,44 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/text-relocs-perms/foo.c b/dyld/unit-tests/test-cases/text-relocs-perms/foo.c new file mode 100644 index 0000000..b644c3d --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs-perms/foo.c @@ -0,0 +1,111 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +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); +} + +#else + +void foo() +{ + // iOS does not have text relocs +} + +#endif + diff --git a/dyld/unit-tests/test-cases/text-relocs-perms/main.c b/dyld/unit-tests/test-cases/text-relocs-perms/main.c new file mode 100644 index 0000000..fc07f17 --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs-perms/main.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#if __MAC_OS_X_VERSION_MIN_REQUIRED + +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; +} + +#else + +int main() +{ + // iOS does not have text relocs + PASS("text-relocs-perm"); + return EXIT_SUCCESS; +} + +#endif + diff --git a/dyld/unit-tests/test-cases/text-relocs/Makefile b/dyld/unit-tests/test-cases/text-relocs/Makefile new file mode 100644 index 0000000..1048e84 --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs/Makefile @@ -0,0 +1,55 @@ +## +# Copyright (c) 2005-2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +### +### This test case is to verify __TEXT relocations work in dylibs +### +### + + +TEXT_RELOC_FLAGS = -mdynamic-no-pic -read_only_relocs suppress -Wl,-w +TEXT_STAT_FLAGS = -static + +ifeq "iPhoneOS" "$(OS_NAME)" + # iOS does not support text relocs + TEXT_RELOC_FLAGS = + TEXT_STAT_FLAGS = +endif + + +all-check: all check + +check: + ./main + +all: + ${CC} ${CCFLAGS} -dynamiclib bar.c space.s -Os -o libbar.dylib ${TEXT_RELOC_FLAGS} + ${CC} ${CCFLAGS} bind.c $(TEXT_STAT_FLAGS) -Os -c -o bind.o + ${CC} -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 libbind.dylib bind.o + diff --git a/dyld/unit-tests/test-cases/text-relocs/bar.c b/dyld/unit-tests/test-cases/text-relocs/bar.c new file mode 100644 index 0000000..a87566e --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs/bar.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +int y = 0; + +static int x = 0; + +int getx() { return x; } +void setx(int a) { x = a; } + +void bar() +{ + printf("hello\n"); +} diff --git a/dyld/unit-tests/test-cases/text-relocs/bind.c b/dyld/unit-tests/test-cases/text-relocs/bind.c new file mode 100644 index 0000000..fa0dd81 --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs/bind.c @@ -0,0 +1,7 @@ + +extern int y; + +int test() +{ + return y; +} diff --git a/dyld/unit-tests/test-cases/text-relocs/main.c b/dyld/unit-tests/test-cases/text-relocs/main.c new file mode 100644 index 0000000..453b17b --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs/main.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern int getx(); +extern void setx(int a); + +int main(int argc, const char* argv[]) +{ + setx(20); + if ( getx() != 20 ) + FAIL("text-reloc"); + else { + setx(99); + if ( getx() == 99 ) + PASS("text-reloc"); + else + FAIL("text-reloc"); + } + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/text-relocs/space.s b/dyld/unit-tests/test-cases/text-relocs/space.s new file mode 100644 index 0000000..0593e1e --- /dev/null +++ b/dyld/unit-tests/test-cases/text-relocs/space.s @@ -0,0 +1,3 @@ + .text + .align 2 +_junk: .space 1024*1024 diff --git a/dyld/unit-tests/test-cases/threaded-flat-lookup/Makefile b/dyld/unit-tests/test-cases/threaded-flat-lookup/Makefile new file mode 100644 index 0000000..1c8f897 --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-flat-lookup/Makefile @@ -0,0 +1,41 @@ +## +# 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 + diff --git a/dyld/unit-tests/test-cases/threaded-flat-lookup/client.c b/dyld/unit-tests/test-cases/threaded-flat-lookup/client.c new file mode 100644 index 0000000..72ba218 --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-flat-lookup/client.c @@ -0,0 +1,11 @@ + +extern void foo(); +extern void bar(); +extern void baz(); + +void doit() +{ + foo(); + bar(); + baz(); +} diff --git a/dyld/unit-tests/test-cases/threaded-flat-lookup/foo.c b/dyld/unit-tests/test-cases/threaded-flat-lookup/foo.c new file mode 100644 index 0000000..3f2cbaf --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-flat-lookup/foo.c @@ -0,0 +1,4 @@ + +void foo() {} +void bar() {} +void baz() {} diff --git a/dyld/unit-tests/test-cases/threaded-flat-lookup/main.c b/dyld/unit-tests/test-cases/threaded-flat-lookup/main.c new file mode 100644 index 0000000..09ee55f --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-flat-lookup/main.c @@ -0,0 +1,95 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/threaded-lazy-bind/Makefile b/dyld/unit-tests/test-cases/threaded-lazy-bind/Makefile new file mode 100644 index 0000000..7b38cee --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-lazy-bind/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/threaded-lazy-bind/foo.c b/dyld/unit-tests/test-cases/threaded-lazy-bind/foo.c new file mode 100644 index 0000000..d647bd5 --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-lazy-bind/foo.c @@ -0,0 +1,1001 @@ + +int do_000() { return 0; } +int do_001() { return 1; } +int do_002() { return 2; } +int do_003() { return 3; } +int do_004() { return 4; } +int do_005() { return 5; } +int do_006() { return 6; } +int do_007() { return 7; } +int do_008() { return 8; } +int do_009() { return 9; } +int do_010() { return 10; } +int do_011() { return 11; } +int do_012() { return 12; } +int do_013() { return 13; } +int do_014() { return 14; } +int do_015() { return 15; } +int do_016() { return 16; } +int do_017() { return 17; } +int do_018() { return 18; } +int do_019() { return 19; } +int do_020() { return 20; } +int do_021() { return 21; } +int do_022() { return 22; } +int do_023() { return 23; } +int do_024() { return 24; } +int do_025() { return 25; } +int do_026() { return 26; } +int do_027() { return 27; } +int do_028() { return 28; } +int do_029() { return 29; } +int do_030() { return 30; } +int do_031() { return 31; } +int do_032() { return 32; } +int do_033() { return 33; } +int do_034() { return 34; } +int do_035() { return 35; } +int do_036() { return 36; } +int do_037() { return 37; } +int do_038() { return 38; } +int do_039() { return 39; } +int do_040() { return 40; } +int do_041() { return 41; } +int do_042() { return 42; } +int do_043() { return 43; } +int do_044() { return 44; } +int do_045() { return 45; } +int do_046() { return 46; } +int do_047() { return 47; } +int do_048() { return 48; } +int do_049() { return 49; } +int do_050() { return 50; } +int do_051() { return 51; } +int do_052() { return 52; } +int do_053() { return 53; } +int do_054() { return 54; } +int do_055() { return 55; } +int do_056() { return 56; } +int do_057() { return 57; } +int do_058() { return 58; } +int do_059() { return 59; } +int do_060() { return 60; } +int do_061() { return 61; } +int do_062() { return 62; } +int do_063() { return 63; } +int do_064() { return 64; } +int do_065() { return 65; } +int do_066() { return 66; } +int do_067() { return 67; } +int do_068() { return 68; } +int do_069() { return 69; } +int do_070() { return 70; } +int do_071() { return 71; } +int do_072() { return 72; } +int do_073() { return 73; } +int do_074() { return 74; } +int do_075() { return 75; } +int do_076() { return 76; } +int do_077() { return 77; } +int do_078() { return 78; } +int do_079() { return 79; } +int do_080() { return 80; } +int do_081() { return 81; } +int do_082() { return 82; } +int do_083() { return 83; } +int do_084() { return 84; } +int do_085() { return 85; } +int do_086() { return 86; } +int do_087() { return 87; } +int do_088() { return 88; } +int do_089() { return 89; } +int do_090() { return 90; } +int do_091() { return 91; } +int do_092() { return 92; } +int do_093() { return 93; } +int do_094() { return 94; } +int do_095() { return 95; } +int do_096() { return 96; } +int do_097() { return 97; } +int do_098() { return 98; } +int do_099() { return 99; } +int do_100() { return 100; } +int do_101() { return 101; } +int do_102() { return 102; } +int do_103() { return 103; } +int do_104() { return 104; } +int do_105() { return 105; } +int do_106() { return 106; } +int do_107() { return 107; } +int do_108() { return 108; } +int do_109() { return 109; } +int do_110() { return 110; } +int do_111() { return 111; } +int do_112() { return 112; } +int do_113() { return 113; } +int do_114() { return 114; } +int do_115() { return 115; } +int do_116() { return 116; } +int do_117() { return 117; } +int do_118() { return 118; } +int do_119() { return 119; } +int do_120() { return 120; } +int do_121() { return 121; } +int do_122() { return 122; } +int do_123() { return 123; } +int do_124() { return 124; } +int do_125() { return 125; } +int do_126() { return 126; } +int do_127() { return 127; } +int do_128() { return 128; } +int do_129() { return 129; } +int do_130() { return 130; } +int do_131() { return 131; } +int do_132() { return 132; } +int do_133() { return 133; } +int do_134() { return 134; } +int do_135() { return 135; } +int do_136() { return 136; } +int do_137() { return 137; } +int do_138() { return 138; } +int do_139() { return 139; } +int do_140() { return 140; } +int do_141() { return 141; } +int do_142() { return 142; } +int do_143() { return 143; } +int do_144() { return 144; } +int do_145() { return 145; } +int do_146() { return 146; } +int do_147() { return 147; } +int do_148() { return 148; } +int do_149() { return 149; } +int do_150() { return 150; } +int do_151() { return 151; } +int do_152() { return 152; } +int do_153() { return 153; } +int do_154() { return 154; } +int do_155() { return 155; } +int do_156() { return 156; } +int do_157() { return 157; } +int do_158() { return 158; } +int do_159() { return 159; } +int do_160() { return 160; } +int do_161() { return 161; } +int do_162() { return 162; } +int do_163() { return 163; } +int do_164() { return 164; } +int do_165() { return 165; } +int do_166() { return 166; } +int do_167() { return 167; } +int do_168() { return 168; } +int do_169() { return 169; } +int do_170() { return 170; } +int do_171() { return 171; } +int do_172() { return 172; } +int do_173() { return 173; } +int do_174() { return 174; } +int do_175() { return 175; } +int do_176() { return 176; } +int do_177() { return 177; } +int do_178() { return 178; } +int do_179() { return 179; } +int do_180() { return 180; } +int do_181() { return 181; } +int do_182() { return 182; } +int do_183() { return 183; } +int do_184() { return 184; } +int do_185() { return 185; } +int do_186() { return 186; } +int do_187() { return 187; } +int do_188() { return 188; } +int do_189() { return 189; } +int do_190() { return 190; } +int do_191() { return 191; } +int do_192() { return 192; } +int do_193() { return 193; } +int do_194() { return 194; } +int do_195() { return 195; } +int do_196() { return 196; } +int do_197() { return 197; } +int do_198() { return 198; } +int do_199() { return 199; } +int do_200() { return 200; } +int do_201() { return 201; } +int do_202() { return 202; } +int do_203() { return 203; } +int do_204() { return 204; } +int do_205() { return 205; } +int do_206() { return 206; } +int do_207() { return 207; } +int do_208() { return 208; } +int do_209() { return 209; } +int do_210() { return 210; } +int do_211() { return 211; } +int do_212() { return 212; } +int do_213() { return 213; } +int do_214() { return 214; } +int do_215() { return 215; } +int do_216() { return 216; } +int do_217() { return 217; } +int do_218() { return 218; } +int do_219() { return 219; } +int do_220() { return 220; } +int do_221() { return 221; } +int do_222() { return 222; } +int do_223() { return 223; } +int do_224() { return 224; } +int do_225() { return 225; } +int do_226() { return 226; } +int do_227() { return 227; } +int do_228() { return 228; } +int do_229() { return 229; } +int do_230() { return 230; } +int do_231() { return 231; } +int do_232() { return 232; } +int do_233() { return 233; } +int do_234() { return 234; } +int do_235() { return 235; } +int do_236() { return 236; } +int do_237() { return 237; } +int do_238() { return 238; } +int do_239() { return 239; } +int do_240() { return 240; } +int do_241() { return 241; } +int do_242() { return 242; } +int do_243() { return 243; } +int do_244() { return 244; } +int do_245() { return 245; } +int do_246() { return 246; } +int do_247() { return 247; } +int do_248() { return 248; } +int do_249() { return 249; } +int do_250() { return 250; } +int do_251() { return 251; } +int do_252() { return 252; } +int do_253() { return 253; } +int do_254() { return 254; } +int do_255() { return 255; } +int do_256() { return 256; } +int do_257() { return 257; } +int do_258() { return 258; } +int do_259() { return 259; } +int do_260() { return 260; } +int do_261() { return 261; } +int do_262() { return 262; } +int do_263() { return 263; } +int do_264() { return 264; } +int do_265() { return 265; } +int do_266() { return 266; } +int do_267() { return 267; } +int do_268() { return 268; } +int do_269() { return 269; } +int do_270() { return 270; } +int do_271() { return 271; } +int do_272() { return 272; } +int do_273() { return 273; } +int do_274() { return 274; } +int do_275() { return 275; } +int do_276() { return 276; } +int do_277() { return 277; } +int do_278() { return 278; } +int do_279() { return 279; } +int do_280() { return 280; } +int do_281() { return 281; } +int do_282() { return 282; } +int do_283() { return 283; } +int do_284() { return 284; } +int do_285() { return 285; } +int do_286() { return 286; } +int do_287() { return 287; } +int do_288() { return 288; } +int do_289() { return 289; } +int do_290() { return 290; } +int do_291() { return 291; } +int do_292() { return 292; } +int do_293() { return 293; } +int do_294() { return 294; } +int do_295() { return 295; } +int do_296() { return 296; } +int do_297() { return 297; } +int do_298() { return 298; } +int do_299() { return 299; } +int do_300() { return 300; } +int do_301() { return 301; } +int do_302() { return 302; } +int do_303() { return 303; } +int do_304() { return 304; } +int do_305() { return 305; } +int do_306() { return 306; } +int do_307() { return 307; } +int do_308() { return 308; } +int do_309() { return 309; } +int do_310() { return 310; } +int do_311() { return 311; } +int do_312() { return 312; } +int do_313() { return 313; } +int do_314() { return 314; } +int do_315() { return 315; } +int do_316() { return 316; } +int do_317() { return 317; } +int do_318() { return 318; } +int do_319() { return 319; } +int do_320() { return 320; } +int do_321() { return 321; } +int do_322() { return 322; } +int do_323() { return 323; } +int do_324() { return 324; } +int do_325() { return 325; } +int do_326() { return 326; } +int do_327() { return 327; } +int do_328() { return 328; } +int do_329() { return 329; } +int do_330() { return 330; } +int do_331() { return 331; } +int do_332() { return 332; } +int do_333() { return 333; } +int do_334() { return 334; } +int do_335() { return 335; } +int do_336() { return 336; } +int do_337() { return 337; } +int do_338() { return 338; } +int do_339() { return 339; } +int do_340() { return 340; } +int do_341() { return 341; } +int do_342() { return 342; } +int do_343() { return 343; } +int do_344() { return 344; } +int do_345() { return 345; } +int do_346() { return 346; } +int do_347() { return 347; } +int do_348() { return 348; } +int do_349() { return 349; } +int do_350() { return 350; } +int do_351() { return 351; } +int do_352() { return 352; } +int do_353() { return 353; } +int do_354() { return 354; } +int do_355() { return 355; } +int do_356() { return 356; } +int do_357() { return 357; } +int do_358() { return 358; } +int do_359() { return 359; } +int do_360() { return 360; } +int do_361() { return 361; } +int do_362() { return 362; } +int do_363() { return 363; } +int do_364() { return 364; } +int do_365() { return 365; } +int do_366() { return 366; } +int do_367() { return 367; } +int do_368() { return 368; } +int do_369() { return 369; } +int do_370() { return 370; } +int do_371() { return 371; } +int do_372() { return 372; } +int do_373() { return 373; } +int do_374() { return 374; } +int do_375() { return 375; } +int do_376() { return 376; } +int do_377() { return 377; } +int do_378() { return 378; } +int do_379() { return 379; } +int do_380() { return 380; } +int do_381() { return 381; } +int do_382() { return 382; } +int do_383() { return 383; } +int do_384() { return 384; } +int do_385() { return 385; } +int do_386() { return 386; } +int do_387() { return 387; } +int do_388() { return 388; } +int do_389() { return 389; } +int do_390() { return 390; } +int do_391() { return 391; } +int do_392() { return 392; } +int do_393() { return 393; } +int do_394() { return 394; } +int do_395() { return 395; } +int do_396() { return 396; } +int do_397() { return 397; } +int do_398() { return 398; } +int do_399() { return 399; } +int do_400() { return 400; } +int do_401() { return 401; } +int do_402() { return 402; } +int do_403() { return 403; } +int do_404() { return 404; } +int do_405() { return 405; } +int do_406() { return 406; } +int do_407() { return 407; } +int do_408() { return 408; } +int do_409() { return 409; } +int do_410() { return 410; } +int do_411() { return 411; } +int do_412() { return 412; } +int do_413() { return 413; } +int do_414() { return 414; } +int do_415() { return 415; } +int do_416() { return 416; } +int do_417() { return 417; } +int do_418() { return 418; } +int do_419() { return 419; } +int do_420() { return 420; } +int do_421() { return 421; } +int do_422() { return 422; } +int do_423() { return 423; } +int do_424() { return 424; } +int do_425() { return 425; } +int do_426() { return 426; } +int do_427() { return 427; } +int do_428() { return 428; } +int do_429() { return 429; } +int do_430() { return 430; } +int do_431() { return 431; } +int do_432() { return 432; } +int do_433() { return 433; } +int do_434() { return 434; } +int do_435() { return 435; } +int do_436() { return 436; } +int do_437() { return 437; } +int do_438() { return 438; } +int do_439() { return 439; } +int do_440() { return 440; } +int do_441() { return 441; } +int do_442() { return 442; } +int do_443() { return 443; } +int do_444() { return 444; } +int do_445() { return 445; } +int do_446() { return 446; } +int do_447() { return 447; } +int do_448() { return 448; } +int do_449() { return 449; } +int do_450() { return 450; } +int do_451() { return 451; } +int do_452() { return 452; } +int do_453() { return 453; } +int do_454() { return 454; } +int do_455() { return 455; } +int do_456() { return 456; } +int do_457() { return 457; } +int do_458() { return 458; } +int do_459() { return 459; } +int do_460() { return 460; } +int do_461() { return 461; } +int do_462() { return 462; } +int do_463() { return 463; } +int do_464() { return 464; } +int do_465() { return 465; } +int do_466() { return 466; } +int do_467() { return 467; } +int do_468() { return 468; } +int do_469() { return 469; } +int do_470() { return 470; } +int do_471() { return 471; } +int do_472() { return 472; } +int do_473() { return 473; } +int do_474() { return 474; } +int do_475() { return 475; } +int do_476() { return 476; } +int do_477() { return 477; } +int do_478() { return 478; } +int do_479() { return 479; } +int do_480() { return 480; } +int do_481() { return 481; } +int do_482() { return 482; } +int do_483() { return 483; } +int do_484() { return 484; } +int do_485() { return 485; } +int do_486() { return 486; } +int do_487() { return 487; } +int do_488() { return 488; } +int do_489() { return 489; } +int do_490() { return 490; } +int do_491() { return 491; } +int do_492() { return 492; } +int do_493() { return 493; } +int do_494() { return 494; } +int do_495() { return 495; } +int do_496() { return 496; } +int do_497() { return 497; } +int do_498() { return 498; } +int do_499() { return 499; } +int do_500() { return 500; } +int do_501() { return 501; } +int do_502() { return 502; } +int do_503() { return 503; } +int do_504() { return 504; } +int do_505() { return 505; } +int do_506() { return 506; } +int do_507() { return 507; } +int do_508() { return 508; } +int do_509() { return 509; } +int do_510() { return 510; } +int do_511() { return 511; } +int do_512() { return 512; } +int do_513() { return 513; } +int do_514() { return 514; } +int do_515() { return 515; } +int do_516() { return 516; } +int do_517() { return 517; } +int do_518() { return 518; } +int do_519() { return 519; } +int do_520() { return 520; } +int do_521() { return 521; } +int do_522() { return 522; } +int do_523() { return 523; } +int do_524() { return 524; } +int do_525() { return 525; } +int do_526() { return 526; } +int do_527() { return 527; } +int do_528() { return 528; } +int do_529() { return 529; } +int do_530() { return 530; } +int do_531() { return 531; } +int do_532() { return 532; } +int do_533() { return 533; } +int do_534() { return 534; } +int do_535() { return 535; } +int do_536() { return 536; } +int do_537() { return 537; } +int do_538() { return 538; } +int do_539() { return 539; } +int do_540() { return 540; } +int do_541() { return 541; } +int do_542() { return 542; } +int do_543() { return 543; } +int do_544() { return 544; } +int do_545() { return 545; } +int do_546() { return 546; } +int do_547() { return 547; } +int do_548() { return 548; } +int do_549() { return 549; } +int do_550() { return 550; } +int do_551() { return 551; } +int do_552() { return 552; } +int do_553() { return 553; } +int do_554() { return 554; } +int do_555() { return 555; } +int do_556() { return 556; } +int do_557() { return 557; } +int do_558() { return 558; } +int do_559() { return 559; } +int do_560() { return 560; } +int do_561() { return 561; } +int do_562() { return 562; } +int do_563() { return 563; } +int do_564() { return 564; } +int do_565() { return 565; } +int do_566() { return 566; } +int do_567() { return 567; } +int do_568() { return 568; } +int do_569() { return 569; } +int do_570() { return 570; } +int do_571() { return 571; } +int do_572() { return 572; } +int do_573() { return 573; } +int do_574() { return 574; } +int do_575() { return 575; } +int do_576() { return 576; } +int do_577() { return 577; } +int do_578() { return 578; } +int do_579() { return 579; } +int do_580() { return 580; } +int do_581() { return 581; } +int do_582() { return 582; } +int do_583() { return 583; } +int do_584() { return 584; } +int do_585() { return 585; } +int do_586() { return 586; } +int do_587() { return 587; } +int do_588() { return 588; } +int do_589() { return 589; } +int do_590() { return 590; } +int do_591() { return 591; } +int do_592() { return 592; } +int do_593() { return 593; } +int do_594() { return 594; } +int do_595() { return 595; } +int do_596() { return 596; } +int do_597() { return 597; } +int do_598() { return 598; } +int do_599() { return 599; } +int do_600() { return 600; } +int do_601() { return 601; } +int do_602() { return 602; } +int do_603() { return 603; } +int do_604() { return 604; } +int do_605() { return 605; } +int do_606() { return 606; } +int do_607() { return 607; } +int do_608() { return 608; } +int do_609() { return 609; } +int do_610() { return 610; } +int do_611() { return 611; } +int do_612() { return 612; } +int do_613() { return 613; } +int do_614() { return 614; } +int do_615() { return 615; } +int do_616() { return 616; } +int do_617() { return 617; } +int do_618() { return 618; } +int do_619() { return 619; } +int do_620() { return 620; } +int do_621() { return 621; } +int do_622() { return 622; } +int do_623() { return 623; } +int do_624() { return 624; } +int do_625() { return 625; } +int do_626() { return 626; } +int do_627() { return 627; } +int do_628() { return 628; } +int do_629() { return 629; } +int do_630() { return 630; } +int do_631() { return 631; } +int do_632() { return 632; } +int do_633() { return 633; } +int do_634() { return 634; } +int do_635() { return 635; } +int do_636() { return 636; } +int do_637() { return 637; } +int do_638() { return 638; } +int do_639() { return 639; } +int do_640() { return 640; } +int do_641() { return 641; } +int do_642() { return 642; } +int do_643() { return 643; } +int do_644() { return 644; } +int do_645() { return 645; } +int do_646() { return 646; } +int do_647() { return 647; } +int do_648() { return 648; } +int do_649() { return 649; } +int do_650() { return 650; } +int do_651() { return 651; } +int do_652() { return 652; } +int do_653() { return 653; } +int do_654() { return 654; } +int do_655() { return 655; } +int do_656() { return 656; } +int do_657() { return 657; } +int do_658() { return 658; } +int do_659() { return 659; } +int do_660() { return 660; } +int do_661() { return 661; } +int do_662() { return 662; } +int do_663() { return 663; } +int do_664() { return 664; } +int do_665() { return 665; } +int do_666() { return 666; } +int do_667() { return 667; } +int do_668() { return 668; } +int do_669() { return 669; } +int do_670() { return 670; } +int do_671() { return 671; } +int do_672() { return 672; } +int do_673() { return 673; } +int do_674() { return 674; } +int do_675() { return 675; } +int do_676() { return 676; } +int do_677() { return 677; } +int do_678() { return 678; } +int do_679() { return 679; } +int do_680() { return 680; } +int do_681() { return 681; } +int do_682() { return 682; } +int do_683() { return 683; } +int do_684() { return 684; } +int do_685() { return 685; } +int do_686() { return 686; } +int do_687() { return 687; } +int do_688() { return 688; } +int do_689() { return 689; } +int do_690() { return 690; } +int do_691() { return 691; } +int do_692() { return 692; } +int do_693() { return 693; } +int do_694() { return 694; } +int do_695() { return 695; } +int do_696() { return 696; } +int do_697() { return 697; } +int do_698() { return 698; } +int do_699() { return 699; } +int do_700() { return 700; } +int do_701() { return 701; } +int do_702() { return 702; } +int do_703() { return 703; } +int do_704() { return 704; } +int do_705() { return 705; } +int do_706() { return 706; } +int do_707() { return 707; } +int do_708() { return 708; } +int do_709() { return 709; } +int do_710() { return 710; } +int do_711() { return 711; } +int do_712() { return 712; } +int do_713() { return 713; } +int do_714() { return 714; } +int do_715() { return 715; } +int do_716() { return 716; } +int do_717() { return 717; } +int do_718() { return 718; } +int do_719() { return 719; } +int do_720() { return 720; } +int do_721() { return 721; } +int do_722() { return 722; } +int do_723() { return 723; } +int do_724() { return 724; } +int do_725() { return 725; } +int do_726() { return 726; } +int do_727() { return 727; } +int do_728() { return 728; } +int do_729() { return 729; } +int do_730() { return 730; } +int do_731() { return 731; } +int do_732() { return 732; } +int do_733() { return 733; } +int do_734() { return 734; } +int do_735() { return 735; } +int do_736() { return 736; } +int do_737() { return 737; } +int do_738() { return 738; } +int do_739() { return 739; } +int do_740() { return 740; } +int do_741() { return 741; } +int do_742() { return 742; } +int do_743() { return 743; } +int do_744() { return 744; } +int do_745() { return 745; } +int do_746() { return 746; } +int do_747() { return 747; } +int do_748() { return 748; } +int do_749() { return 749; } +int do_750() { return 750; } +int do_751() { return 751; } +int do_752() { return 752; } +int do_753() { return 753; } +int do_754() { return 754; } +int do_755() { return 755; } +int do_756() { return 756; } +int do_757() { return 757; } +int do_758() { return 758; } +int do_759() { return 759; } +int do_760() { return 760; } +int do_761() { return 761; } +int do_762() { return 762; } +int do_763() { return 763; } +int do_764() { return 764; } +int do_765() { return 765; } +int do_766() { return 766; } +int do_767() { return 767; } +int do_768() { return 768; } +int do_769() { return 769; } +int do_770() { return 770; } +int do_771() { return 771; } +int do_772() { return 772; } +int do_773() { return 773; } +int do_774() { return 774; } +int do_775() { return 775; } +int do_776() { return 776; } +int do_777() { return 777; } +int do_778() { return 778; } +int do_779() { return 779; } +int do_780() { return 780; } +int do_781() { return 781; } +int do_782() { return 782; } +int do_783() { return 783; } +int do_784() { return 784; } +int do_785() { return 785; } +int do_786() { return 786; } +int do_787() { return 787; } +int do_788() { return 788; } +int do_789() { return 789; } +int do_790() { return 790; } +int do_791() { return 791; } +int do_792() { return 792; } +int do_793() { return 793; } +int do_794() { return 794; } +int do_795() { return 795; } +int do_796() { return 796; } +int do_797() { return 797; } +int do_798() { return 798; } +int do_799() { return 799; } +int do_800() { return 800; } +int do_801() { return 801; } +int do_802() { return 802; } +int do_803() { return 803; } +int do_804() { return 804; } +int do_805() { return 805; } +int do_806() { return 806; } +int do_807() { return 807; } +int do_808() { return 808; } +int do_809() { return 809; } +int do_810() { return 810; } +int do_811() { return 811; } +int do_812() { return 812; } +int do_813() { return 813; } +int do_814() { return 814; } +int do_815() { return 815; } +int do_816() { return 816; } +int do_817() { return 817; } +int do_818() { return 818; } +int do_819() { return 819; } +int do_820() { return 820; } +int do_821() { return 821; } +int do_822() { return 822; } +int do_823() { return 823; } +int do_824() { return 824; } +int do_825() { return 825; } +int do_826() { return 826; } +int do_827() { return 827; } +int do_828() { return 828; } +int do_829() { return 829; } +int do_830() { return 830; } +int do_831() { return 831; } +int do_832() { return 832; } +int do_833() { return 833; } +int do_834() { return 834; } +int do_835() { return 835; } +int do_836() { return 836; } +int do_837() { return 837; } +int do_838() { return 838; } +int do_839() { return 839; } +int do_840() { return 840; } +int do_841() { return 841; } +int do_842() { return 842; } +int do_843() { return 843; } +int do_844() { return 844; } +int do_845() { return 845; } +int do_846() { return 846; } +int do_847() { return 847; } +int do_848() { return 848; } +int do_849() { return 849; } +int do_850() { return 850; } +int do_851() { return 851; } +int do_852() { return 852; } +int do_853() { return 853; } +int do_854() { return 854; } +int do_855() { return 855; } +int do_856() { return 856; } +int do_857() { return 857; } +int do_858() { return 858; } +int do_859() { return 859; } +int do_860() { return 860; } +int do_861() { return 861; } +int do_862() { return 862; } +int do_863() { return 863; } +int do_864() { return 864; } +int do_865() { return 865; } +int do_866() { return 866; } +int do_867() { return 867; } +int do_868() { return 868; } +int do_869() { return 869; } +int do_870() { return 870; } +int do_871() { return 871; } +int do_872() { return 872; } +int do_873() { return 873; } +int do_874() { return 874; } +int do_875() { return 875; } +int do_876() { return 876; } +int do_877() { return 877; } +int do_878() { return 878; } +int do_879() { return 879; } +int do_880() { return 880; } +int do_881() { return 881; } +int do_882() { return 882; } +int do_883() { return 883; } +int do_884() { return 884; } +int do_885() { return 885; } +int do_886() { return 886; } +int do_887() { return 887; } +int do_888() { return 888; } +int do_889() { return 889; } +int do_890() { return 890; } +int do_891() { return 891; } +int do_892() { return 892; } +int do_893() { return 893; } +int do_894() { return 894; } +int do_895() { return 895; } +int do_896() { return 896; } +int do_897() { return 897; } +int do_898() { return 898; } +int do_899() { return 899; } +int do_900() { return 900; } +int do_901() { return 901; } +int do_902() { return 902; } +int do_903() { return 903; } +int do_904() { return 904; } +int do_905() { return 905; } +int do_906() { return 906; } +int do_907() { return 907; } +int do_908() { return 908; } +int do_909() { return 909; } +int do_910() { return 910; } +int do_911() { return 911; } +int do_912() { return 912; } +int do_913() { return 913; } +int do_914() { return 914; } +int do_915() { return 915; } +int do_916() { return 916; } +int do_917() { return 917; } +int do_918() { return 918; } +int do_919() { return 919; } +int do_920() { return 920; } +int do_921() { return 921; } +int do_922() { return 922; } +int do_923() { return 923; } +int do_924() { return 924; } +int do_925() { return 925; } +int do_926() { return 926; } +int do_927() { return 927; } +int do_928() { return 928; } +int do_929() { return 929; } +int do_930() { return 930; } +int do_931() { return 931; } +int do_932() { return 932; } +int do_933() { return 933; } +int do_934() { return 934; } +int do_935() { return 935; } +int do_936() { return 936; } +int do_937() { return 937; } +int do_938() { return 938; } +int do_939() { return 939; } +int do_940() { return 940; } +int do_941() { return 941; } +int do_942() { return 942; } +int do_943() { return 943; } +int do_944() { return 944; } +int do_945() { return 945; } +int do_946() { return 946; } +int do_947() { return 947; } +int do_948() { return 948; } +int do_949() { return 949; } +int do_950() { return 950; } +int do_951() { return 951; } +int do_952() { return 952; } +int do_953() { return 953; } +int do_954() { return 954; } +int do_955() { return 955; } +int do_956() { return 956; } +int do_957() { return 957; } +int do_958() { return 958; } +int do_959() { return 959; } +int do_960() { return 960; } +int do_961() { return 961; } +int do_962() { return 962; } +int do_963() { return 963; } +int do_964() { return 964; } +int do_965() { return 965; } +int do_966() { return 966; } +int do_967() { return 967; } +int do_968() { return 968; } +int do_969() { return 969; } +int do_970() { return 970; } +int do_971() { return 971; } +int do_972() { return 972; } +int do_973() { return 973; } +int do_974() { return 974; } +int do_975() { return 975; } +int do_976() { return 976; } +int do_977() { return 977; } +int do_978() { return 978; } +int do_979() { return 979; } +int do_980() { return 980; } +int do_981() { return 981; } +int do_982() { return 982; } +int do_983() { return 983; } +int do_984() { return 984; } +int do_985() { return 985; } +int do_986() { return 986; } +int do_987() { return 987; } +int do_988() { return 988; } +int do_989() { return 989; } +int do_990() { return 990; } +int do_991() { return 991; } +int do_992() { return 992; } +int do_993() { return 993; } +int do_994() { return 994; } +int do_995() { return 995; } +int do_996() { return 996; } +int do_997() { return 997; } +int do_998() { return 998; } +int do_999() { return 999; } diff --git a/dyld/unit-tests/test-cases/threaded-lazy-bind/gen.c b/dyld/unit-tests/test-cases/threaded-lazy-bind/gen.c new file mode 100644 index 0000000..03bd744 --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-lazy-bind/gen.c @@ -0,0 +1,19 @@ + +#include + +int main() +{ + int i; + for (i = 0; i < 1000; ++i) { + printf("int do_%03d() { return %d; }\n", i, i); + } + + for (i = 0; i < 1000; ++i) { + printf("extern int do_%03d();\n", i); + } + for (i = 0; i < 1000; ++i) { + printf("if ( do_%03d() != %d ) { FAIL(\"iteration %d\"); exit(0); }\n", i, i, i); + } + + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/threaded-lazy-bind/main.c b/dyld/unit-tests/test-cases/threaded-lazy-bind/main.c new file mode 100644 index 0000000..ebb0e0d --- /dev/null +++ b/dyld/unit-tests/test-cases/threaded-lazy-bind/main.c @@ -0,0 +1,2060 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +/// +/// Test that lazy binding is thread safe +/// + +extern int do_000(); +extern int do_001(); +extern int do_002(); +extern int do_003(); +extern int do_004(); +extern int do_005(); +extern int do_006(); +extern int do_007(); +extern int do_008(); +extern int do_009(); +extern int do_010(); +extern int do_011(); +extern int do_012(); +extern int do_013(); +extern int do_014(); +extern int do_015(); +extern int do_016(); +extern int do_017(); +extern int do_018(); +extern int do_019(); +extern int do_020(); +extern int do_021(); +extern int do_022(); +extern int do_023(); +extern int do_024(); +extern int do_025(); +extern int do_026(); +extern int do_027(); +extern int do_028(); +extern int do_029(); +extern int do_030(); +extern int do_031(); +extern int do_032(); +extern int do_033(); +extern int do_034(); +extern int do_035(); +extern int do_036(); +extern int do_037(); +extern int do_038(); +extern int do_039(); +extern int do_040(); +extern int do_041(); +extern int do_042(); +extern int do_043(); +extern int do_044(); +extern int do_045(); +extern int do_046(); +extern int do_047(); +extern int do_048(); +extern int do_049(); +extern int do_050(); +extern int do_051(); +extern int do_052(); +extern int do_053(); +extern int do_054(); +extern int do_055(); +extern int do_056(); +extern int do_057(); +extern int do_058(); +extern int do_059(); +extern int do_060(); +extern int do_061(); +extern int do_062(); +extern int do_063(); +extern int do_064(); +extern int do_065(); +extern int do_066(); +extern int do_067(); +extern int do_068(); +extern int do_069(); +extern int do_070(); +extern int do_071(); +extern int do_072(); +extern int do_073(); +extern int do_074(); +extern int do_075(); +extern int do_076(); +extern int do_077(); +extern int do_078(); +extern int do_079(); +extern int do_080(); +extern int do_081(); +extern int do_082(); +extern int do_083(); +extern int do_084(); +extern int do_085(); +extern int do_086(); +extern int do_087(); +extern int do_088(); +extern int do_089(); +extern int do_090(); +extern int do_091(); +extern int do_092(); +extern int do_093(); +extern int do_094(); +extern int do_095(); +extern int do_096(); +extern int do_097(); +extern int do_098(); +extern int do_099(); +extern int do_100(); +extern int do_101(); +extern int do_102(); +extern int do_103(); +extern int do_104(); +extern int do_105(); +extern int do_106(); +extern int do_107(); +extern int do_108(); +extern int do_109(); +extern int do_110(); +extern int do_111(); +extern int do_112(); +extern int do_113(); +extern int do_114(); +extern int do_115(); +extern int do_116(); +extern int do_117(); +extern int do_118(); +extern int do_119(); +extern int do_120(); +extern int do_121(); +extern int do_122(); +extern int do_123(); +extern int do_124(); +extern int do_125(); +extern int do_126(); +extern int do_127(); +extern int do_128(); +extern int do_129(); +extern int do_130(); +extern int do_131(); +extern int do_132(); +extern int do_133(); +extern int do_134(); +extern int do_135(); +extern int do_136(); +extern int do_137(); +extern int do_138(); +extern int do_139(); +extern int do_140(); +extern int do_141(); +extern int do_142(); +extern int do_143(); +extern int do_144(); +extern int do_145(); +extern int do_146(); +extern int do_147(); +extern int do_148(); +extern int do_149(); +extern int do_150(); +extern int do_151(); +extern int do_152(); +extern int do_153(); +extern int do_154(); +extern int do_155(); +extern int do_156(); +extern int do_157(); +extern int do_158(); +extern int do_159(); +extern int do_160(); +extern int do_161(); +extern int do_162(); +extern int do_163(); +extern int do_164(); +extern int do_165(); +extern int do_166(); +extern int do_167(); +extern int do_168(); +extern int do_169(); +extern int do_170(); +extern int do_171(); +extern int do_172(); +extern int do_173(); +extern int do_174(); +extern int do_175(); +extern int do_176(); +extern int do_177(); +extern int do_178(); +extern int do_179(); +extern int do_180(); +extern int do_181(); +extern int do_182(); +extern int do_183(); +extern int do_184(); +extern int do_185(); +extern int do_186(); +extern int do_187(); +extern int do_188(); +extern int do_189(); +extern int do_190(); +extern int do_191(); +extern int do_192(); +extern int do_193(); +extern int do_194(); +extern int do_195(); +extern int do_196(); +extern int do_197(); +extern int do_198(); +extern int do_199(); +extern int do_200(); +extern int do_201(); +extern int do_202(); +extern int do_203(); +extern int do_204(); +extern int do_205(); +extern int do_206(); +extern int do_207(); +extern int do_208(); +extern int do_209(); +extern int do_210(); +extern int do_211(); +extern int do_212(); +extern int do_213(); +extern int do_214(); +extern int do_215(); +extern int do_216(); +extern int do_217(); +extern int do_218(); +extern int do_219(); +extern int do_220(); +extern int do_221(); +extern int do_222(); +extern int do_223(); +extern int do_224(); +extern int do_225(); +extern int do_226(); +extern int do_227(); +extern int do_228(); +extern int do_229(); +extern int do_230(); +extern int do_231(); +extern int do_232(); +extern int do_233(); +extern int do_234(); +extern int do_235(); +extern int do_236(); +extern int do_237(); +extern int do_238(); +extern int do_239(); +extern int do_240(); +extern int do_241(); +extern int do_242(); +extern int do_243(); +extern int do_244(); +extern int do_245(); +extern int do_246(); +extern int do_247(); +extern int do_248(); +extern int do_249(); +extern int do_250(); +extern int do_251(); +extern int do_252(); +extern int do_253(); +extern int do_254(); +extern int do_255(); +extern int do_256(); +extern int do_257(); +extern int do_258(); +extern int do_259(); +extern int do_260(); +extern int do_261(); +extern int do_262(); +extern int do_263(); +extern int do_264(); +extern int do_265(); +extern int do_266(); +extern int do_267(); +extern int do_268(); +extern int do_269(); +extern int do_270(); +extern int do_271(); +extern int do_272(); +extern int do_273(); +extern int do_274(); +extern int do_275(); +extern int do_276(); +extern int do_277(); +extern int do_278(); +extern int do_279(); +extern int do_280(); +extern int do_281(); +extern int do_282(); +extern int do_283(); +extern int do_284(); +extern int do_285(); +extern int do_286(); +extern int do_287(); +extern int do_288(); +extern int do_289(); +extern int do_290(); +extern int do_291(); +extern int do_292(); +extern int do_293(); +extern int do_294(); +extern int do_295(); +extern int do_296(); +extern int do_297(); +extern int do_298(); +extern int do_299(); +extern int do_300(); +extern int do_301(); +extern int do_302(); +extern int do_303(); +extern int do_304(); +extern int do_305(); +extern int do_306(); +extern int do_307(); +extern int do_308(); +extern int do_309(); +extern int do_310(); +extern int do_311(); +extern int do_312(); +extern int do_313(); +extern int do_314(); +extern int do_315(); +extern int do_316(); +extern int do_317(); +extern int do_318(); +extern int do_319(); +extern int do_320(); +extern int do_321(); +extern int do_322(); +extern int do_323(); +extern int do_324(); +extern int do_325(); +extern int do_326(); +extern int do_327(); +extern int do_328(); +extern int do_329(); +extern int do_330(); +extern int do_331(); +extern int do_332(); +extern int do_333(); +extern int do_334(); +extern int do_335(); +extern int do_336(); +extern int do_337(); +extern int do_338(); +extern int do_339(); +extern int do_340(); +extern int do_341(); +extern int do_342(); +extern int do_343(); +extern int do_344(); +extern int do_345(); +extern int do_346(); +extern int do_347(); +extern int do_348(); +extern int do_349(); +extern int do_350(); +extern int do_351(); +extern int do_352(); +extern int do_353(); +extern int do_354(); +extern int do_355(); +extern int do_356(); +extern int do_357(); +extern int do_358(); +extern int do_359(); +extern int do_360(); +extern int do_361(); +extern int do_362(); +extern int do_363(); +extern int do_364(); +extern int do_365(); +extern int do_366(); +extern int do_367(); +extern int do_368(); +extern int do_369(); +extern int do_370(); +extern int do_371(); +extern int do_372(); +extern int do_373(); +extern int do_374(); +extern int do_375(); +extern int do_376(); +extern int do_377(); +extern int do_378(); +extern int do_379(); +extern int do_380(); +extern int do_381(); +extern int do_382(); +extern int do_383(); +extern int do_384(); +extern int do_385(); +extern int do_386(); +extern int do_387(); +extern int do_388(); +extern int do_389(); +extern int do_390(); +extern int do_391(); +extern int do_392(); +extern int do_393(); +extern int do_394(); +extern int do_395(); +extern int do_396(); +extern int do_397(); +extern int do_398(); +extern int do_399(); +extern int do_400(); +extern int do_401(); +extern int do_402(); +extern int do_403(); +extern int do_404(); +extern int do_405(); +extern int do_406(); +extern int do_407(); +extern int do_408(); +extern int do_409(); +extern int do_410(); +extern int do_411(); +extern int do_412(); +extern int do_413(); +extern int do_414(); +extern int do_415(); +extern int do_416(); +extern int do_417(); +extern int do_418(); +extern int do_419(); +extern int do_420(); +extern int do_421(); +extern int do_422(); +extern int do_423(); +extern int do_424(); +extern int do_425(); +extern int do_426(); +extern int do_427(); +extern int do_428(); +extern int do_429(); +extern int do_430(); +extern int do_431(); +extern int do_432(); +extern int do_433(); +extern int do_434(); +extern int do_435(); +extern int do_436(); +extern int do_437(); +extern int do_438(); +extern int do_439(); +extern int do_440(); +extern int do_441(); +extern int do_442(); +extern int do_443(); +extern int do_444(); +extern int do_445(); +extern int do_446(); +extern int do_447(); +extern int do_448(); +extern int do_449(); +extern int do_450(); +extern int do_451(); +extern int do_452(); +extern int do_453(); +extern int do_454(); +extern int do_455(); +extern int do_456(); +extern int do_457(); +extern int do_458(); +extern int do_459(); +extern int do_460(); +extern int do_461(); +extern int do_462(); +extern int do_463(); +extern int do_464(); +extern int do_465(); +extern int do_466(); +extern int do_467(); +extern int do_468(); +extern int do_469(); +extern int do_470(); +extern int do_471(); +extern int do_472(); +extern int do_473(); +extern int do_474(); +extern int do_475(); +extern int do_476(); +extern int do_477(); +extern int do_478(); +extern int do_479(); +extern int do_480(); +extern int do_481(); +extern int do_482(); +extern int do_483(); +extern int do_484(); +extern int do_485(); +extern int do_486(); +extern int do_487(); +extern int do_488(); +extern int do_489(); +extern int do_490(); +extern int do_491(); +extern int do_492(); +extern int do_493(); +extern int do_494(); +extern int do_495(); +extern int do_496(); +extern int do_497(); +extern int do_498(); +extern int do_499(); +extern int do_500(); +extern int do_501(); +extern int do_502(); +extern int do_503(); +extern int do_504(); +extern int do_505(); +extern int do_506(); +extern int do_507(); +extern int do_508(); +extern int do_509(); +extern int do_510(); +extern int do_511(); +extern int do_512(); +extern int do_513(); +extern int do_514(); +extern int do_515(); +extern int do_516(); +extern int do_517(); +extern int do_518(); +extern int do_519(); +extern int do_520(); +extern int do_521(); +extern int do_522(); +extern int do_523(); +extern int do_524(); +extern int do_525(); +extern int do_526(); +extern int do_527(); +extern int do_528(); +extern int do_529(); +extern int do_530(); +extern int do_531(); +extern int do_532(); +extern int do_533(); +extern int do_534(); +extern int do_535(); +extern int do_536(); +extern int do_537(); +extern int do_538(); +extern int do_539(); +extern int do_540(); +extern int do_541(); +extern int do_542(); +extern int do_543(); +extern int do_544(); +extern int do_545(); +extern int do_546(); +extern int do_547(); +extern int do_548(); +extern int do_549(); +extern int do_550(); +extern int do_551(); +extern int do_552(); +extern int do_553(); +extern int do_554(); +extern int do_555(); +extern int do_556(); +extern int do_557(); +extern int do_558(); +extern int do_559(); +extern int do_560(); +extern int do_561(); +extern int do_562(); +extern int do_563(); +extern int do_564(); +extern int do_565(); +extern int do_566(); +extern int do_567(); +extern int do_568(); +extern int do_569(); +extern int do_570(); +extern int do_571(); +extern int do_572(); +extern int do_573(); +extern int do_574(); +extern int do_575(); +extern int do_576(); +extern int do_577(); +extern int do_578(); +extern int do_579(); +extern int do_580(); +extern int do_581(); +extern int do_582(); +extern int do_583(); +extern int do_584(); +extern int do_585(); +extern int do_586(); +extern int do_587(); +extern int do_588(); +extern int do_589(); +extern int do_590(); +extern int do_591(); +extern int do_592(); +extern int do_593(); +extern int do_594(); +extern int do_595(); +extern int do_596(); +extern int do_597(); +extern int do_598(); +extern int do_599(); +extern int do_600(); +extern int do_601(); +extern int do_602(); +extern int do_603(); +extern int do_604(); +extern int do_605(); +extern int do_606(); +extern int do_607(); +extern int do_608(); +extern int do_609(); +extern int do_610(); +extern int do_611(); +extern int do_612(); +extern int do_613(); +extern int do_614(); +extern int do_615(); +extern int do_616(); +extern int do_617(); +extern int do_618(); +extern int do_619(); +extern int do_620(); +extern int do_621(); +extern int do_622(); +extern int do_623(); +extern int do_624(); +extern int do_625(); +extern int do_626(); +extern int do_627(); +extern int do_628(); +extern int do_629(); +extern int do_630(); +extern int do_631(); +extern int do_632(); +extern int do_633(); +extern int do_634(); +extern int do_635(); +extern int do_636(); +extern int do_637(); +extern int do_638(); +extern int do_639(); +extern int do_640(); +extern int do_641(); +extern int do_642(); +extern int do_643(); +extern int do_644(); +extern int do_645(); +extern int do_646(); +extern int do_647(); +extern int do_648(); +extern int do_649(); +extern int do_650(); +extern int do_651(); +extern int do_652(); +extern int do_653(); +extern int do_654(); +extern int do_655(); +extern int do_656(); +extern int do_657(); +extern int do_658(); +extern int do_659(); +extern int do_660(); +extern int do_661(); +extern int do_662(); +extern int do_663(); +extern int do_664(); +extern int do_665(); +extern int do_666(); +extern int do_667(); +extern int do_668(); +extern int do_669(); +extern int do_670(); +extern int do_671(); +extern int do_672(); +extern int do_673(); +extern int do_674(); +extern int do_675(); +extern int do_676(); +extern int do_677(); +extern int do_678(); +extern int do_679(); +extern int do_680(); +extern int do_681(); +extern int do_682(); +extern int do_683(); +extern int do_684(); +extern int do_685(); +extern int do_686(); +extern int do_687(); +extern int do_688(); +extern int do_689(); +extern int do_690(); +extern int do_691(); +extern int do_692(); +extern int do_693(); +extern int do_694(); +extern int do_695(); +extern int do_696(); +extern int do_697(); +extern int do_698(); +extern int do_699(); +extern int do_700(); +extern int do_701(); +extern int do_702(); +extern int do_703(); +extern int do_704(); +extern int do_705(); +extern int do_706(); +extern int do_707(); +extern int do_708(); +extern int do_709(); +extern int do_710(); +extern int do_711(); +extern int do_712(); +extern int do_713(); +extern int do_714(); +extern int do_715(); +extern int do_716(); +extern int do_717(); +extern int do_718(); +extern int do_719(); +extern int do_720(); +extern int do_721(); +extern int do_722(); +extern int do_723(); +extern int do_724(); +extern int do_725(); +extern int do_726(); +extern int do_727(); +extern int do_728(); +extern int do_729(); +extern int do_730(); +extern int do_731(); +extern int do_732(); +extern int do_733(); +extern int do_734(); +extern int do_735(); +extern int do_736(); +extern int do_737(); +extern int do_738(); +extern int do_739(); +extern int do_740(); +extern int do_741(); +extern int do_742(); +extern int do_743(); +extern int do_744(); +extern int do_745(); +extern int do_746(); +extern int do_747(); +extern int do_748(); +extern int do_749(); +extern int do_750(); +extern int do_751(); +extern int do_752(); +extern int do_753(); +extern int do_754(); +extern int do_755(); +extern int do_756(); +extern int do_757(); +extern int do_758(); +extern int do_759(); +extern int do_760(); +extern int do_761(); +extern int do_762(); +extern int do_763(); +extern int do_764(); +extern int do_765(); +extern int do_766(); +extern int do_767(); +extern int do_768(); +extern int do_769(); +extern int do_770(); +extern int do_771(); +extern int do_772(); +extern int do_773(); +extern int do_774(); +extern int do_775(); +extern int do_776(); +extern int do_777(); +extern int do_778(); +extern int do_779(); +extern int do_780(); +extern int do_781(); +extern int do_782(); +extern int do_783(); +extern int do_784(); +extern int do_785(); +extern int do_786(); +extern int do_787(); +extern int do_788(); +extern int do_789(); +extern int do_790(); +extern int do_791(); +extern int do_792(); +extern int do_793(); +extern int do_794(); +extern int do_795(); +extern int do_796(); +extern int do_797(); +extern int do_798(); +extern int do_799(); +extern int do_800(); +extern int do_801(); +extern int do_802(); +extern int do_803(); +extern int do_804(); +extern int do_805(); +extern int do_806(); +extern int do_807(); +extern int do_808(); +extern int do_809(); +extern int do_810(); +extern int do_811(); +extern int do_812(); +extern int do_813(); +extern int do_814(); +extern int do_815(); +extern int do_816(); +extern int do_817(); +extern int do_818(); +extern int do_819(); +extern int do_820(); +extern int do_821(); +extern int do_822(); +extern int do_823(); +extern int do_824(); +extern int do_825(); +extern int do_826(); +extern int do_827(); +extern int do_828(); +extern int do_829(); +extern int do_830(); +extern int do_831(); +extern int do_832(); +extern int do_833(); +extern int do_834(); +extern int do_835(); +extern int do_836(); +extern int do_837(); +extern int do_838(); +extern int do_839(); +extern int do_840(); +extern int do_841(); +extern int do_842(); +extern int do_843(); +extern int do_844(); +extern int do_845(); +extern int do_846(); +extern int do_847(); +extern int do_848(); +extern int do_849(); +extern int do_850(); +extern int do_851(); +extern int do_852(); +extern int do_853(); +extern int do_854(); +extern int do_855(); +extern int do_856(); +extern int do_857(); +extern int do_858(); +extern int do_859(); +extern int do_860(); +extern int do_861(); +extern int do_862(); +extern int do_863(); +extern int do_864(); +extern int do_865(); +extern int do_866(); +extern int do_867(); +extern int do_868(); +extern int do_869(); +extern int do_870(); +extern int do_871(); +extern int do_872(); +extern int do_873(); +extern int do_874(); +extern int do_875(); +extern int do_876(); +extern int do_877(); +extern int do_878(); +extern int do_879(); +extern int do_880(); +extern int do_881(); +extern int do_882(); +extern int do_883(); +extern int do_884(); +extern int do_885(); +extern int do_886(); +extern int do_887(); +extern int do_888(); +extern int do_889(); +extern int do_890(); +extern int do_891(); +extern int do_892(); +extern int do_893(); +extern int do_894(); +extern int do_895(); +extern int do_896(); +extern int do_897(); +extern int do_898(); +extern int do_899(); +extern int do_900(); +extern int do_901(); +extern int do_902(); +extern int do_903(); +extern int do_904(); +extern int do_905(); +extern int do_906(); +extern int do_907(); +extern int do_908(); +extern int do_909(); +extern int do_910(); +extern int do_911(); +extern int do_912(); +extern int do_913(); +extern int do_914(); +extern int do_915(); +extern int do_916(); +extern int do_917(); +extern int do_918(); +extern int do_919(); +extern int do_920(); +extern int do_921(); +extern int do_922(); +extern int do_923(); +extern int do_924(); +extern int do_925(); +extern int do_926(); +extern int do_927(); +extern int do_928(); +extern int do_929(); +extern int do_930(); +extern int do_931(); +extern int do_932(); +extern int do_933(); +extern int do_934(); +extern int do_935(); +extern int do_936(); +extern int do_937(); +extern int do_938(); +extern int do_939(); +extern int do_940(); +extern int do_941(); +extern int do_942(); +extern int do_943(); +extern int do_944(); +extern int do_945(); +extern int do_946(); +extern int do_947(); +extern int do_948(); +extern int do_949(); +extern int do_950(); +extern int do_951(); +extern int do_952(); +extern int do_953(); +extern int do_954(); +extern int do_955(); +extern int do_956(); +extern int do_957(); +extern int do_958(); +extern int do_959(); +extern int do_960(); +extern int do_961(); +extern int do_962(); +extern int do_963(); +extern int do_964(); +extern int do_965(); +extern int do_966(); +extern int do_967(); +extern int do_968(); +extern int do_969(); +extern int do_970(); +extern int do_971(); +extern int do_972(); +extern int do_973(); +extern int do_974(); +extern int do_975(); +extern int do_976(); +extern int do_977(); +extern int do_978(); +extern int do_979(); +extern int do_980(); +extern int do_981(); +extern int do_982(); +extern int do_983(); +extern int do_984(); +extern int do_985(); +extern int do_986(); +extern int do_987(); +extern int do_988(); +extern int do_989(); +extern int do_990(); +extern int do_991(); +extern int do_992(); +extern int do_993(); +extern int do_994(); +extern int do_995(); +extern int do_996(); +extern int do_997(); +extern int do_998(); +extern int do_999(); + + +static void* work(void* ignore) +{ + if ( do_000() != 0 ) { FAIL("iteration 0"); exit(0); } + if ( do_001() != 1 ) { FAIL("iteration 1"); exit(0); } + if ( do_002() != 2 ) { FAIL("iteration 2"); exit(0); } + if ( do_003() != 3 ) { FAIL("iteration 3"); exit(0); } + if ( do_004() != 4 ) { FAIL("iteration 4"); exit(0); } + if ( do_005() != 5 ) { FAIL("iteration 5"); exit(0); } + if ( do_006() != 6 ) { FAIL("iteration 6"); exit(0); } + if ( do_007() != 7 ) { FAIL("iteration 7"); exit(0); } + if ( do_008() != 8 ) { FAIL("iteration 8"); exit(0); } + if ( do_009() != 9 ) { FAIL("iteration 9"); exit(0); } + if ( do_010() != 10 ) { FAIL("iteration 10"); exit(0); } + if ( do_011() != 11 ) { FAIL("iteration 11"); exit(0); } + if ( do_012() != 12 ) { FAIL("iteration 12"); exit(0); } + if ( do_013() != 13 ) { FAIL("iteration 13"); exit(0); } + if ( do_014() != 14 ) { FAIL("iteration 14"); exit(0); } + if ( do_015() != 15 ) { FAIL("iteration 15"); exit(0); } + if ( do_016() != 16 ) { FAIL("iteration 16"); exit(0); } + if ( do_017() != 17 ) { FAIL("iteration 17"); exit(0); } + if ( do_018() != 18 ) { FAIL("iteration 18"); exit(0); } + if ( do_019() != 19 ) { FAIL("iteration 19"); exit(0); } + if ( do_020() != 20 ) { FAIL("iteration 20"); exit(0); } + if ( do_021() != 21 ) { FAIL("iteration 21"); exit(0); } + if ( do_022() != 22 ) { FAIL("iteration 22"); exit(0); } + if ( do_023() != 23 ) { FAIL("iteration 23"); exit(0); } + if ( do_024() != 24 ) { FAIL("iteration 24"); exit(0); } + if ( do_025() != 25 ) { FAIL("iteration 25"); exit(0); } + if ( do_026() != 26 ) { FAIL("iteration 26"); exit(0); } + if ( do_027() != 27 ) { FAIL("iteration 27"); exit(0); } + if ( do_028() != 28 ) { FAIL("iteration 28"); exit(0); } + if ( do_029() != 29 ) { FAIL("iteration 29"); exit(0); } + if ( do_030() != 30 ) { FAIL("iteration 30"); exit(0); } + if ( do_031() != 31 ) { FAIL("iteration 31"); exit(0); } + if ( do_032() != 32 ) { FAIL("iteration 32"); exit(0); } + if ( do_033() != 33 ) { FAIL("iteration 33"); exit(0); } + if ( do_034() != 34 ) { FAIL("iteration 34"); exit(0); } + if ( do_035() != 35 ) { FAIL("iteration 35"); exit(0); } + if ( do_036() != 36 ) { FAIL("iteration 36"); exit(0); } + if ( do_037() != 37 ) { FAIL("iteration 37"); exit(0); } + if ( do_038() != 38 ) { FAIL("iteration 38"); exit(0); } + if ( do_039() != 39 ) { FAIL("iteration 39"); exit(0); } + if ( do_040() != 40 ) { FAIL("iteration 40"); exit(0); } + if ( do_041() != 41 ) { FAIL("iteration 41"); exit(0); } + if ( do_042() != 42 ) { FAIL("iteration 42"); exit(0); } + if ( do_043() != 43 ) { FAIL("iteration 43"); exit(0); } + if ( do_044() != 44 ) { FAIL("iteration 44"); exit(0); } + if ( do_045() != 45 ) { FAIL("iteration 45"); exit(0); } + if ( do_046() != 46 ) { FAIL("iteration 46"); exit(0); } + if ( do_047() != 47 ) { FAIL("iteration 47"); exit(0); } + if ( do_048() != 48 ) { FAIL("iteration 48"); exit(0); } + if ( do_049() != 49 ) { FAIL("iteration 49"); exit(0); } + if ( do_050() != 50 ) { FAIL("iteration 50"); exit(0); } + if ( do_051() != 51 ) { FAIL("iteration 51"); exit(0); } + if ( do_052() != 52 ) { FAIL("iteration 52"); exit(0); } + if ( do_053() != 53 ) { FAIL("iteration 53"); exit(0); } + if ( do_054() != 54 ) { FAIL("iteration 54"); exit(0); } + if ( do_055() != 55 ) { FAIL("iteration 55"); exit(0); } + if ( do_056() != 56 ) { FAIL("iteration 56"); exit(0); } + if ( do_057() != 57 ) { FAIL("iteration 57"); exit(0); } + if ( do_058() != 58 ) { FAIL("iteration 58"); exit(0); } + if ( do_059() != 59 ) { FAIL("iteration 59"); exit(0); } + if ( do_060() != 60 ) { FAIL("iteration 60"); exit(0); } + if ( do_061() != 61 ) { FAIL("iteration 61"); exit(0); } + if ( do_062() != 62 ) { FAIL("iteration 62"); exit(0); } + if ( do_063() != 63 ) { FAIL("iteration 63"); exit(0); } + if ( do_064() != 64 ) { FAIL("iteration 64"); exit(0); } + if ( do_065() != 65 ) { FAIL("iteration 65"); exit(0); } + if ( do_066() != 66 ) { FAIL("iteration 66"); exit(0); } + if ( do_067() != 67 ) { FAIL("iteration 67"); exit(0); } + if ( do_068() != 68 ) { FAIL("iteration 68"); exit(0); } + if ( do_069() != 69 ) { FAIL("iteration 69"); exit(0); } + if ( do_070() != 70 ) { FAIL("iteration 70"); exit(0); } + if ( do_071() != 71 ) { FAIL("iteration 71"); exit(0); } + if ( do_072() != 72 ) { FAIL("iteration 72"); exit(0); } + if ( do_073() != 73 ) { FAIL("iteration 73"); exit(0); } + if ( do_074() != 74 ) { FAIL("iteration 74"); exit(0); } + if ( do_075() != 75 ) { FAIL("iteration 75"); exit(0); } + if ( do_076() != 76 ) { FAIL("iteration 76"); exit(0); } + if ( do_077() != 77 ) { FAIL("iteration 77"); exit(0); } + if ( do_078() != 78 ) { FAIL("iteration 78"); exit(0); } + if ( do_079() != 79 ) { FAIL("iteration 79"); exit(0); } + if ( do_080() != 80 ) { FAIL("iteration 80"); exit(0); } + if ( do_081() != 81 ) { FAIL("iteration 81"); exit(0); } + if ( do_082() != 82 ) { FAIL("iteration 82"); exit(0); } + if ( do_083() != 83 ) { FAIL("iteration 83"); exit(0); } + if ( do_084() != 84 ) { FAIL("iteration 84"); exit(0); } + if ( do_085() != 85 ) { FAIL("iteration 85"); exit(0); } + if ( do_086() != 86 ) { FAIL("iteration 86"); exit(0); } + if ( do_087() != 87 ) { FAIL("iteration 87"); exit(0); } + if ( do_088() != 88 ) { FAIL("iteration 88"); exit(0); } + if ( do_089() != 89 ) { FAIL("iteration 89"); exit(0); } + if ( do_090() != 90 ) { FAIL("iteration 90"); exit(0); } + if ( do_091() != 91 ) { FAIL("iteration 91"); exit(0); } + if ( do_092() != 92 ) { FAIL("iteration 92"); exit(0); } + if ( do_093() != 93 ) { FAIL("iteration 93"); exit(0); } + if ( do_094() != 94 ) { FAIL("iteration 94"); exit(0); } + if ( do_095() != 95 ) { FAIL("iteration 95"); exit(0); } + if ( do_096() != 96 ) { FAIL("iteration 96"); exit(0); } + if ( do_097() != 97 ) { FAIL("iteration 97"); exit(0); } + if ( do_098() != 98 ) { FAIL("iteration 98"); exit(0); } + if ( do_099() != 99 ) { FAIL("iteration 99"); exit(0); } + if ( do_100() != 100 ) { FAIL("iteration 100"); exit(0); } + if ( do_101() != 101 ) { FAIL("iteration 101"); exit(0); } + if ( do_102() != 102 ) { FAIL("iteration 102"); exit(0); } + if ( do_103() != 103 ) { FAIL("iteration 103"); exit(0); } + if ( do_104() != 104 ) { FAIL("iteration 104"); exit(0); } + if ( do_105() != 105 ) { FAIL("iteration 105"); exit(0); } + if ( do_106() != 106 ) { FAIL("iteration 106"); exit(0); } + if ( do_107() != 107 ) { FAIL("iteration 107"); exit(0); } + if ( do_108() != 108 ) { FAIL("iteration 108"); exit(0); } + if ( do_109() != 109 ) { FAIL("iteration 109"); exit(0); } + if ( do_110() != 110 ) { FAIL("iteration 110"); exit(0); } + if ( do_111() != 111 ) { FAIL("iteration 111"); exit(0); } + if ( do_112() != 112 ) { FAIL("iteration 112"); exit(0); } + if ( do_113() != 113 ) { FAIL("iteration 113"); exit(0); } + if ( do_114() != 114 ) { FAIL("iteration 114"); exit(0); } + if ( do_115() != 115 ) { FAIL("iteration 115"); exit(0); } + if ( do_116() != 116 ) { FAIL("iteration 116"); exit(0); } + if ( do_117() != 117 ) { FAIL("iteration 117"); exit(0); } + if ( do_118() != 118 ) { FAIL("iteration 118"); exit(0); } + if ( do_119() != 119 ) { FAIL("iteration 119"); exit(0); } + if ( do_120() != 120 ) { FAIL("iteration 120"); exit(0); } + if ( do_121() != 121 ) { FAIL("iteration 121"); exit(0); } + if ( do_122() != 122 ) { FAIL("iteration 122"); exit(0); } + if ( do_123() != 123 ) { FAIL("iteration 123"); exit(0); } + if ( do_124() != 124 ) { FAIL("iteration 124"); exit(0); } + if ( do_125() != 125 ) { FAIL("iteration 125"); exit(0); } + if ( do_126() != 126 ) { FAIL("iteration 126"); exit(0); } + if ( do_127() != 127 ) { FAIL("iteration 127"); exit(0); } + if ( do_128() != 128 ) { FAIL("iteration 128"); exit(0); } + if ( do_129() != 129 ) { FAIL("iteration 129"); exit(0); } + if ( do_130() != 130 ) { FAIL("iteration 130"); exit(0); } + if ( do_131() != 131 ) { FAIL("iteration 131"); exit(0); } + if ( do_132() != 132 ) { FAIL("iteration 132"); exit(0); } + if ( do_133() != 133 ) { FAIL("iteration 133"); exit(0); } + if ( do_134() != 134 ) { FAIL("iteration 134"); exit(0); } + if ( do_135() != 135 ) { FAIL("iteration 135"); exit(0); } + if ( do_136() != 136 ) { FAIL("iteration 136"); exit(0); } + if ( do_137() != 137 ) { FAIL("iteration 137"); exit(0); } + if ( do_138() != 138 ) { FAIL("iteration 138"); exit(0); } + if ( do_139() != 139 ) { FAIL("iteration 139"); exit(0); } + if ( do_140() != 140 ) { FAIL("iteration 140"); exit(0); } + if ( do_141() != 141 ) { FAIL("iteration 141"); exit(0); } + if ( do_142() != 142 ) { FAIL("iteration 142"); exit(0); } + if ( do_143() != 143 ) { FAIL("iteration 143"); exit(0); } + if ( do_144() != 144 ) { FAIL("iteration 144"); exit(0); } + if ( do_145() != 145 ) { FAIL("iteration 145"); exit(0); } + if ( do_146() != 146 ) { FAIL("iteration 146"); exit(0); } + if ( do_147() != 147 ) { FAIL("iteration 147"); exit(0); } + if ( do_148() != 148 ) { FAIL("iteration 148"); exit(0); } + if ( do_149() != 149 ) { FAIL("iteration 149"); exit(0); } + if ( do_150() != 150 ) { FAIL("iteration 150"); exit(0); } + if ( do_151() != 151 ) { FAIL("iteration 151"); exit(0); } + if ( do_152() != 152 ) { FAIL("iteration 152"); exit(0); } + if ( do_153() != 153 ) { FAIL("iteration 153"); exit(0); } + if ( do_154() != 154 ) { FAIL("iteration 154"); exit(0); } + if ( do_155() != 155 ) { FAIL("iteration 155"); exit(0); } + if ( do_156() != 156 ) { FAIL("iteration 156"); exit(0); } + if ( do_157() != 157 ) { FAIL("iteration 157"); exit(0); } + if ( do_158() != 158 ) { FAIL("iteration 158"); exit(0); } + if ( do_159() != 159 ) { FAIL("iteration 159"); exit(0); } + if ( do_160() != 160 ) { FAIL("iteration 160"); exit(0); } + if ( do_161() != 161 ) { FAIL("iteration 161"); exit(0); } + if ( do_162() != 162 ) { FAIL("iteration 162"); exit(0); } + if ( do_163() != 163 ) { FAIL("iteration 163"); exit(0); } + if ( do_164() != 164 ) { FAIL("iteration 164"); exit(0); } + if ( do_165() != 165 ) { FAIL("iteration 165"); exit(0); } + if ( do_166() != 166 ) { FAIL("iteration 166"); exit(0); } + if ( do_167() != 167 ) { FAIL("iteration 167"); exit(0); } + if ( do_168() != 168 ) { FAIL("iteration 168"); exit(0); } + if ( do_169() != 169 ) { FAIL("iteration 169"); exit(0); } + if ( do_170() != 170 ) { FAIL("iteration 170"); exit(0); } + if ( do_171() != 171 ) { FAIL("iteration 171"); exit(0); } + if ( do_172() != 172 ) { FAIL("iteration 172"); exit(0); } + if ( do_173() != 173 ) { FAIL("iteration 173"); exit(0); } + if ( do_174() != 174 ) { FAIL("iteration 174"); exit(0); } + if ( do_175() != 175 ) { FAIL("iteration 175"); exit(0); } + if ( do_176() != 176 ) { FAIL("iteration 176"); exit(0); } + if ( do_177() != 177 ) { FAIL("iteration 177"); exit(0); } + if ( do_178() != 178 ) { FAIL("iteration 178"); exit(0); } + if ( do_179() != 179 ) { FAIL("iteration 179"); exit(0); } + if ( do_180() != 180 ) { FAIL("iteration 180"); exit(0); } + if ( do_181() != 181 ) { FAIL("iteration 181"); exit(0); } + if ( do_182() != 182 ) { FAIL("iteration 182"); exit(0); } + if ( do_183() != 183 ) { FAIL("iteration 183"); exit(0); } + if ( do_184() != 184 ) { FAIL("iteration 184"); exit(0); } + if ( do_185() != 185 ) { FAIL("iteration 185"); exit(0); } + if ( do_186() != 186 ) { FAIL("iteration 186"); exit(0); } + if ( do_187() != 187 ) { FAIL("iteration 187"); exit(0); } + if ( do_188() != 188 ) { FAIL("iteration 188"); exit(0); } + if ( do_189() != 189 ) { FAIL("iteration 189"); exit(0); } + if ( do_190() != 190 ) { FAIL("iteration 190"); exit(0); } + if ( do_191() != 191 ) { FAIL("iteration 191"); exit(0); } + if ( do_192() != 192 ) { FAIL("iteration 192"); exit(0); } + if ( do_193() != 193 ) { FAIL("iteration 193"); exit(0); } + if ( do_194() != 194 ) { FAIL("iteration 194"); exit(0); } + if ( do_195() != 195 ) { FAIL("iteration 195"); exit(0); } + if ( do_196() != 196 ) { FAIL("iteration 196"); exit(0); } + if ( do_197() != 197 ) { FAIL("iteration 197"); exit(0); } + if ( do_198() != 198 ) { FAIL("iteration 198"); exit(0); } + if ( do_199() != 199 ) { FAIL("iteration 199"); exit(0); } + if ( do_200() != 200 ) { FAIL("iteration 200"); exit(0); } + if ( do_201() != 201 ) { FAIL("iteration 201"); exit(0); } + if ( do_202() != 202 ) { FAIL("iteration 202"); exit(0); } + if ( do_203() != 203 ) { FAIL("iteration 203"); exit(0); } + if ( do_204() != 204 ) { FAIL("iteration 204"); exit(0); } + if ( do_205() != 205 ) { FAIL("iteration 205"); exit(0); } + if ( do_206() != 206 ) { FAIL("iteration 206"); exit(0); } + if ( do_207() != 207 ) { FAIL("iteration 207"); exit(0); } + if ( do_208() != 208 ) { FAIL("iteration 208"); exit(0); } + if ( do_209() != 209 ) { FAIL("iteration 209"); exit(0); } + if ( do_210() != 210 ) { FAIL("iteration 210"); exit(0); } + if ( do_211() != 211 ) { FAIL("iteration 211"); exit(0); } + if ( do_212() != 212 ) { FAIL("iteration 212"); exit(0); } + if ( do_213() != 213 ) { FAIL("iteration 213"); exit(0); } + if ( do_214() != 214 ) { FAIL("iteration 214"); exit(0); } + if ( do_215() != 215 ) { FAIL("iteration 215"); exit(0); } + if ( do_216() != 216 ) { FAIL("iteration 216"); exit(0); } + if ( do_217() != 217 ) { FAIL("iteration 217"); exit(0); } + if ( do_218() != 218 ) { FAIL("iteration 218"); exit(0); } + if ( do_219() != 219 ) { FAIL("iteration 219"); exit(0); } + if ( do_220() != 220 ) { FAIL("iteration 220"); exit(0); } + if ( do_221() != 221 ) { FAIL("iteration 221"); exit(0); } + if ( do_222() != 222 ) { FAIL("iteration 222"); exit(0); } + if ( do_223() != 223 ) { FAIL("iteration 223"); exit(0); } + if ( do_224() != 224 ) { FAIL("iteration 224"); exit(0); } + if ( do_225() != 225 ) { FAIL("iteration 225"); exit(0); } + if ( do_226() != 226 ) { FAIL("iteration 226"); exit(0); } + if ( do_227() != 227 ) { FAIL("iteration 227"); exit(0); } + if ( do_228() != 228 ) { FAIL("iteration 228"); exit(0); } + if ( do_229() != 229 ) { FAIL("iteration 229"); exit(0); } + if ( do_230() != 230 ) { FAIL("iteration 230"); exit(0); } + if ( do_231() != 231 ) { FAIL("iteration 231"); exit(0); } + if ( do_232() != 232 ) { FAIL("iteration 232"); exit(0); } + if ( do_233() != 233 ) { FAIL("iteration 233"); exit(0); } + if ( do_234() != 234 ) { FAIL("iteration 234"); exit(0); } + if ( do_235() != 235 ) { FAIL("iteration 235"); exit(0); } + if ( do_236() != 236 ) { FAIL("iteration 236"); exit(0); } + if ( do_237() != 237 ) { FAIL("iteration 237"); exit(0); } + if ( do_238() != 238 ) { FAIL("iteration 238"); exit(0); } + if ( do_239() != 239 ) { FAIL("iteration 239"); exit(0); } + if ( do_240() != 240 ) { FAIL("iteration 240"); exit(0); } + if ( do_241() != 241 ) { FAIL("iteration 241"); exit(0); } + if ( do_242() != 242 ) { FAIL("iteration 242"); exit(0); } + if ( do_243() != 243 ) { FAIL("iteration 243"); exit(0); } + if ( do_244() != 244 ) { FAIL("iteration 244"); exit(0); } + if ( do_245() != 245 ) { FAIL("iteration 245"); exit(0); } + if ( do_246() != 246 ) { FAIL("iteration 246"); exit(0); } + if ( do_247() != 247 ) { FAIL("iteration 247"); exit(0); } + if ( do_248() != 248 ) { FAIL("iteration 248"); exit(0); } + if ( do_249() != 249 ) { FAIL("iteration 249"); exit(0); } + if ( do_250() != 250 ) { FAIL("iteration 250"); exit(0); } + if ( do_251() != 251 ) { FAIL("iteration 251"); exit(0); } + if ( do_252() != 252 ) { FAIL("iteration 252"); exit(0); } + if ( do_253() != 253 ) { FAIL("iteration 253"); exit(0); } + if ( do_254() != 254 ) { FAIL("iteration 254"); exit(0); } + if ( do_255() != 255 ) { FAIL("iteration 255"); exit(0); } + if ( do_256() != 256 ) { FAIL("iteration 256"); exit(0); } + if ( do_257() != 257 ) { FAIL("iteration 257"); exit(0); } + if ( do_258() != 258 ) { FAIL("iteration 258"); exit(0); } + if ( do_259() != 259 ) { FAIL("iteration 259"); exit(0); } + if ( do_260() != 260 ) { FAIL("iteration 260"); exit(0); } + if ( do_261() != 261 ) { FAIL("iteration 261"); exit(0); } + if ( do_262() != 262 ) { FAIL("iteration 262"); exit(0); } + if ( do_263() != 263 ) { FAIL("iteration 263"); exit(0); } + if ( do_264() != 264 ) { FAIL("iteration 264"); exit(0); } + if ( do_265() != 265 ) { FAIL("iteration 265"); exit(0); } + if ( do_266() != 266 ) { FAIL("iteration 266"); exit(0); } + if ( do_267() != 267 ) { FAIL("iteration 267"); exit(0); } + if ( do_268() != 268 ) { FAIL("iteration 268"); exit(0); } + if ( do_269() != 269 ) { FAIL("iteration 269"); exit(0); } + if ( do_270() != 270 ) { FAIL("iteration 270"); exit(0); } + if ( do_271() != 271 ) { FAIL("iteration 271"); exit(0); } + if ( do_272() != 272 ) { FAIL("iteration 272"); exit(0); } + if ( do_273() != 273 ) { FAIL("iteration 273"); exit(0); } + if ( do_274() != 274 ) { FAIL("iteration 274"); exit(0); } + if ( do_275() != 275 ) { FAIL("iteration 275"); exit(0); } + if ( do_276() != 276 ) { FAIL("iteration 276"); exit(0); } + if ( do_277() != 277 ) { FAIL("iteration 277"); exit(0); } + if ( do_278() != 278 ) { FAIL("iteration 278"); exit(0); } + if ( do_279() != 279 ) { FAIL("iteration 279"); exit(0); } + if ( do_280() != 280 ) { FAIL("iteration 280"); exit(0); } + if ( do_281() != 281 ) { FAIL("iteration 281"); exit(0); } + if ( do_282() != 282 ) { FAIL("iteration 282"); exit(0); } + if ( do_283() != 283 ) { FAIL("iteration 283"); exit(0); } + if ( do_284() != 284 ) { FAIL("iteration 284"); exit(0); } + if ( do_285() != 285 ) { FAIL("iteration 285"); exit(0); } + if ( do_286() != 286 ) { FAIL("iteration 286"); exit(0); } + if ( do_287() != 287 ) { FAIL("iteration 287"); exit(0); } + if ( do_288() != 288 ) { FAIL("iteration 288"); exit(0); } + if ( do_289() != 289 ) { FAIL("iteration 289"); exit(0); } + if ( do_290() != 290 ) { FAIL("iteration 290"); exit(0); } + if ( do_291() != 291 ) { FAIL("iteration 291"); exit(0); } + if ( do_292() != 292 ) { FAIL("iteration 292"); exit(0); } + if ( do_293() != 293 ) { FAIL("iteration 293"); exit(0); } + if ( do_294() != 294 ) { FAIL("iteration 294"); exit(0); } + if ( do_295() != 295 ) { FAIL("iteration 295"); exit(0); } + if ( do_296() != 296 ) { FAIL("iteration 296"); exit(0); } + if ( do_297() != 297 ) { FAIL("iteration 297"); exit(0); } + if ( do_298() != 298 ) { FAIL("iteration 298"); exit(0); } + if ( do_299() != 299 ) { FAIL("iteration 299"); exit(0); } + if ( do_300() != 300 ) { FAIL("iteration 300"); exit(0); } + if ( do_301() != 301 ) { FAIL("iteration 301"); exit(0); } + if ( do_302() != 302 ) { FAIL("iteration 302"); exit(0); } + if ( do_303() != 303 ) { FAIL("iteration 303"); exit(0); } + if ( do_304() != 304 ) { FAIL("iteration 304"); exit(0); } + if ( do_305() != 305 ) { FAIL("iteration 305"); exit(0); } + if ( do_306() != 306 ) { FAIL("iteration 306"); exit(0); } + if ( do_307() != 307 ) { FAIL("iteration 307"); exit(0); } + if ( do_308() != 308 ) { FAIL("iteration 308"); exit(0); } + if ( do_309() != 309 ) { FAIL("iteration 309"); exit(0); } + if ( do_310() != 310 ) { FAIL("iteration 310"); exit(0); } + if ( do_311() != 311 ) { FAIL("iteration 311"); exit(0); } + if ( do_312() != 312 ) { FAIL("iteration 312"); exit(0); } + if ( do_313() != 313 ) { FAIL("iteration 313"); exit(0); } + if ( do_314() != 314 ) { FAIL("iteration 314"); exit(0); } + if ( do_315() != 315 ) { FAIL("iteration 315"); exit(0); } + if ( do_316() != 316 ) { FAIL("iteration 316"); exit(0); } + if ( do_317() != 317 ) { FAIL("iteration 317"); exit(0); } + if ( do_318() != 318 ) { FAIL("iteration 318"); exit(0); } + if ( do_319() != 319 ) { FAIL("iteration 319"); exit(0); } + if ( do_320() != 320 ) { FAIL("iteration 320"); exit(0); } + if ( do_321() != 321 ) { FAIL("iteration 321"); exit(0); } + if ( do_322() != 322 ) { FAIL("iteration 322"); exit(0); } + if ( do_323() != 323 ) { FAIL("iteration 323"); exit(0); } + if ( do_324() != 324 ) { FAIL("iteration 324"); exit(0); } + if ( do_325() != 325 ) { FAIL("iteration 325"); exit(0); } + if ( do_326() != 326 ) { FAIL("iteration 326"); exit(0); } + if ( do_327() != 327 ) { FAIL("iteration 327"); exit(0); } + if ( do_328() != 328 ) { FAIL("iteration 328"); exit(0); } + if ( do_329() != 329 ) { FAIL("iteration 329"); exit(0); } + if ( do_330() != 330 ) { FAIL("iteration 330"); exit(0); } + if ( do_331() != 331 ) { FAIL("iteration 331"); exit(0); } + if ( do_332() != 332 ) { FAIL("iteration 332"); exit(0); } + if ( do_333() != 333 ) { FAIL("iteration 333"); exit(0); } + if ( do_334() != 334 ) { FAIL("iteration 334"); exit(0); } + if ( do_335() != 335 ) { FAIL("iteration 335"); exit(0); } + if ( do_336() != 336 ) { FAIL("iteration 336"); exit(0); } + if ( do_337() != 337 ) { FAIL("iteration 337"); exit(0); } + if ( do_338() != 338 ) { FAIL("iteration 338"); exit(0); } + if ( do_339() != 339 ) { FAIL("iteration 339"); exit(0); } + if ( do_340() != 340 ) { FAIL("iteration 340"); exit(0); } + if ( do_341() != 341 ) { FAIL("iteration 341"); exit(0); } + if ( do_342() != 342 ) { FAIL("iteration 342"); exit(0); } + if ( do_343() != 343 ) { FAIL("iteration 343"); exit(0); } + if ( do_344() != 344 ) { FAIL("iteration 344"); exit(0); } + if ( do_345() != 345 ) { FAIL("iteration 345"); exit(0); } + if ( do_346() != 346 ) { FAIL("iteration 346"); exit(0); } + if ( do_347() != 347 ) { FAIL("iteration 347"); exit(0); } + if ( do_348() != 348 ) { FAIL("iteration 348"); exit(0); } + if ( do_349() != 349 ) { FAIL("iteration 349"); exit(0); } + if ( do_350() != 350 ) { FAIL("iteration 350"); exit(0); } + if ( do_351() != 351 ) { FAIL("iteration 351"); exit(0); } + if ( do_352() != 352 ) { FAIL("iteration 352"); exit(0); } + if ( do_353() != 353 ) { FAIL("iteration 353"); exit(0); } + if ( do_354() != 354 ) { FAIL("iteration 354"); exit(0); } + if ( do_355() != 355 ) { FAIL("iteration 355"); exit(0); } + if ( do_356() != 356 ) { FAIL("iteration 356"); exit(0); } + if ( do_357() != 357 ) { FAIL("iteration 357"); exit(0); } + if ( do_358() != 358 ) { FAIL("iteration 358"); exit(0); } + if ( do_359() != 359 ) { FAIL("iteration 359"); exit(0); } + if ( do_360() != 360 ) { FAIL("iteration 360"); exit(0); } + if ( do_361() != 361 ) { FAIL("iteration 361"); exit(0); } + if ( do_362() != 362 ) { FAIL("iteration 362"); exit(0); } + if ( do_363() != 363 ) { FAIL("iteration 363"); exit(0); } + if ( do_364() != 364 ) { FAIL("iteration 364"); exit(0); } + if ( do_365() != 365 ) { FAIL("iteration 365"); exit(0); } + if ( do_366() != 366 ) { FAIL("iteration 366"); exit(0); } + if ( do_367() != 367 ) { FAIL("iteration 367"); exit(0); } + if ( do_368() != 368 ) { FAIL("iteration 368"); exit(0); } + if ( do_369() != 369 ) { FAIL("iteration 369"); exit(0); } + if ( do_370() != 370 ) { FAIL("iteration 370"); exit(0); } + if ( do_371() != 371 ) { FAIL("iteration 371"); exit(0); } + if ( do_372() != 372 ) { FAIL("iteration 372"); exit(0); } + if ( do_373() != 373 ) { FAIL("iteration 373"); exit(0); } + if ( do_374() != 374 ) { FAIL("iteration 374"); exit(0); } + if ( do_375() != 375 ) { FAIL("iteration 375"); exit(0); } + if ( do_376() != 376 ) { FAIL("iteration 376"); exit(0); } + if ( do_377() != 377 ) { FAIL("iteration 377"); exit(0); } + if ( do_378() != 378 ) { FAIL("iteration 378"); exit(0); } + if ( do_379() != 379 ) { FAIL("iteration 379"); exit(0); } + if ( do_380() != 380 ) { FAIL("iteration 380"); exit(0); } + if ( do_381() != 381 ) { FAIL("iteration 381"); exit(0); } + if ( do_382() != 382 ) { FAIL("iteration 382"); exit(0); } + if ( do_383() != 383 ) { FAIL("iteration 383"); exit(0); } + if ( do_384() != 384 ) { FAIL("iteration 384"); exit(0); } + if ( do_385() != 385 ) { FAIL("iteration 385"); exit(0); } + if ( do_386() != 386 ) { FAIL("iteration 386"); exit(0); } + if ( do_387() != 387 ) { FAIL("iteration 387"); exit(0); } + if ( do_388() != 388 ) { FAIL("iteration 388"); exit(0); } + if ( do_389() != 389 ) { FAIL("iteration 389"); exit(0); } + if ( do_390() != 390 ) { FAIL("iteration 390"); exit(0); } + if ( do_391() != 391 ) { FAIL("iteration 391"); exit(0); } + if ( do_392() != 392 ) { FAIL("iteration 392"); exit(0); } + if ( do_393() != 393 ) { FAIL("iteration 393"); exit(0); } + if ( do_394() != 394 ) { FAIL("iteration 394"); exit(0); } + if ( do_395() != 395 ) { FAIL("iteration 395"); exit(0); } + if ( do_396() != 396 ) { FAIL("iteration 396"); exit(0); } + if ( do_397() != 397 ) { FAIL("iteration 397"); exit(0); } + if ( do_398() != 398 ) { FAIL("iteration 398"); exit(0); } + if ( do_399() != 399 ) { FAIL("iteration 399"); exit(0); } + if ( do_400() != 400 ) { FAIL("iteration 400"); exit(0); } + if ( do_401() != 401 ) { FAIL("iteration 401"); exit(0); } + if ( do_402() != 402 ) { FAIL("iteration 402"); exit(0); } + if ( do_403() != 403 ) { FAIL("iteration 403"); exit(0); } + if ( do_404() != 404 ) { FAIL("iteration 404"); exit(0); } + if ( do_405() != 405 ) { FAIL("iteration 405"); exit(0); } + if ( do_406() != 406 ) { FAIL("iteration 406"); exit(0); } + if ( do_407() != 407 ) { FAIL("iteration 407"); exit(0); } + if ( do_408() != 408 ) { FAIL("iteration 408"); exit(0); } + if ( do_409() != 409 ) { FAIL("iteration 409"); exit(0); } + if ( do_410() != 410 ) { FAIL("iteration 410"); exit(0); } + if ( do_411() != 411 ) { FAIL("iteration 411"); exit(0); } + if ( do_412() != 412 ) { FAIL("iteration 412"); exit(0); } + if ( do_413() != 413 ) { FAIL("iteration 413"); exit(0); } + if ( do_414() != 414 ) { FAIL("iteration 414"); exit(0); } + if ( do_415() != 415 ) { FAIL("iteration 415"); exit(0); } + if ( do_416() != 416 ) { FAIL("iteration 416"); exit(0); } + if ( do_417() != 417 ) { FAIL("iteration 417"); exit(0); } + if ( do_418() != 418 ) { FAIL("iteration 418"); exit(0); } + if ( do_419() != 419 ) { FAIL("iteration 419"); exit(0); } + if ( do_420() != 420 ) { FAIL("iteration 420"); exit(0); } + if ( do_421() != 421 ) { FAIL("iteration 421"); exit(0); } + if ( do_422() != 422 ) { FAIL("iteration 422"); exit(0); } + if ( do_423() != 423 ) { FAIL("iteration 423"); exit(0); } + if ( do_424() != 424 ) { FAIL("iteration 424"); exit(0); } + if ( do_425() != 425 ) { FAIL("iteration 425"); exit(0); } + if ( do_426() != 426 ) { FAIL("iteration 426"); exit(0); } + if ( do_427() != 427 ) { FAIL("iteration 427"); exit(0); } + if ( do_428() != 428 ) { FAIL("iteration 428"); exit(0); } + if ( do_429() != 429 ) { FAIL("iteration 429"); exit(0); } + if ( do_430() != 430 ) { FAIL("iteration 430"); exit(0); } + if ( do_431() != 431 ) { FAIL("iteration 431"); exit(0); } + if ( do_432() != 432 ) { FAIL("iteration 432"); exit(0); } + if ( do_433() != 433 ) { FAIL("iteration 433"); exit(0); } + if ( do_434() != 434 ) { FAIL("iteration 434"); exit(0); } + if ( do_435() != 435 ) { FAIL("iteration 435"); exit(0); } + if ( do_436() != 436 ) { FAIL("iteration 436"); exit(0); } + if ( do_437() != 437 ) { FAIL("iteration 437"); exit(0); } + if ( do_438() != 438 ) { FAIL("iteration 438"); exit(0); } + if ( do_439() != 439 ) { FAIL("iteration 439"); exit(0); } + if ( do_440() != 440 ) { FAIL("iteration 440"); exit(0); } + if ( do_441() != 441 ) { FAIL("iteration 441"); exit(0); } + if ( do_442() != 442 ) { FAIL("iteration 442"); exit(0); } + if ( do_443() != 443 ) { FAIL("iteration 443"); exit(0); } + if ( do_444() != 444 ) { FAIL("iteration 444"); exit(0); } + if ( do_445() != 445 ) { FAIL("iteration 445"); exit(0); } + if ( do_446() != 446 ) { FAIL("iteration 446"); exit(0); } + if ( do_447() != 447 ) { FAIL("iteration 447"); exit(0); } + if ( do_448() != 448 ) { FAIL("iteration 448"); exit(0); } + if ( do_449() != 449 ) { FAIL("iteration 449"); exit(0); } + if ( do_450() != 450 ) { FAIL("iteration 450"); exit(0); } + if ( do_451() != 451 ) { FAIL("iteration 451"); exit(0); } + if ( do_452() != 452 ) { FAIL("iteration 452"); exit(0); } + if ( do_453() != 453 ) { FAIL("iteration 453"); exit(0); } + if ( do_454() != 454 ) { FAIL("iteration 454"); exit(0); } + if ( do_455() != 455 ) { FAIL("iteration 455"); exit(0); } + if ( do_456() != 456 ) { FAIL("iteration 456"); exit(0); } + if ( do_457() != 457 ) { FAIL("iteration 457"); exit(0); } + if ( do_458() != 458 ) { FAIL("iteration 458"); exit(0); } + if ( do_459() != 459 ) { FAIL("iteration 459"); exit(0); } + if ( do_460() != 460 ) { FAIL("iteration 460"); exit(0); } + if ( do_461() != 461 ) { FAIL("iteration 461"); exit(0); } + if ( do_462() != 462 ) { FAIL("iteration 462"); exit(0); } + if ( do_463() != 463 ) { FAIL("iteration 463"); exit(0); } + if ( do_464() != 464 ) { FAIL("iteration 464"); exit(0); } + if ( do_465() != 465 ) { FAIL("iteration 465"); exit(0); } + if ( do_466() != 466 ) { FAIL("iteration 466"); exit(0); } + if ( do_467() != 467 ) { FAIL("iteration 467"); exit(0); } + if ( do_468() != 468 ) { FAIL("iteration 468"); exit(0); } + if ( do_469() != 469 ) { FAIL("iteration 469"); exit(0); } + if ( do_470() != 470 ) { FAIL("iteration 470"); exit(0); } + if ( do_471() != 471 ) { FAIL("iteration 471"); exit(0); } + if ( do_472() != 472 ) { FAIL("iteration 472"); exit(0); } + if ( do_473() != 473 ) { FAIL("iteration 473"); exit(0); } + if ( do_474() != 474 ) { FAIL("iteration 474"); exit(0); } + if ( do_475() != 475 ) { FAIL("iteration 475"); exit(0); } + if ( do_476() != 476 ) { FAIL("iteration 476"); exit(0); } + if ( do_477() != 477 ) { FAIL("iteration 477"); exit(0); } + if ( do_478() != 478 ) { FAIL("iteration 478"); exit(0); } + if ( do_479() != 479 ) { FAIL("iteration 479"); exit(0); } + if ( do_480() != 480 ) { FAIL("iteration 480"); exit(0); } + if ( do_481() != 481 ) { FAIL("iteration 481"); exit(0); } + if ( do_482() != 482 ) { FAIL("iteration 482"); exit(0); } + if ( do_483() != 483 ) { FAIL("iteration 483"); exit(0); } + if ( do_484() != 484 ) { FAIL("iteration 484"); exit(0); } + if ( do_485() != 485 ) { FAIL("iteration 485"); exit(0); } + if ( do_486() != 486 ) { FAIL("iteration 486"); exit(0); } + if ( do_487() != 487 ) { FAIL("iteration 487"); exit(0); } + if ( do_488() != 488 ) { FAIL("iteration 488"); exit(0); } + if ( do_489() != 489 ) { FAIL("iteration 489"); exit(0); } + if ( do_490() != 490 ) { FAIL("iteration 490"); exit(0); } + if ( do_491() != 491 ) { FAIL("iteration 491"); exit(0); } + if ( do_492() != 492 ) { FAIL("iteration 492"); exit(0); } + if ( do_493() != 493 ) { FAIL("iteration 493"); exit(0); } + if ( do_494() != 494 ) { FAIL("iteration 494"); exit(0); } + if ( do_495() != 495 ) { FAIL("iteration 495"); exit(0); } + if ( do_496() != 496 ) { FAIL("iteration 496"); exit(0); } + if ( do_497() != 497 ) { FAIL("iteration 497"); exit(0); } + if ( do_498() != 498 ) { FAIL("iteration 498"); exit(0); } + if ( do_499() != 499 ) { FAIL("iteration 499"); exit(0); } + if ( do_500() != 500 ) { FAIL("iteration 500"); exit(0); } + if ( do_501() != 501 ) { FAIL("iteration 501"); exit(0); } + if ( do_502() != 502 ) { FAIL("iteration 502"); exit(0); } + if ( do_503() != 503 ) { FAIL("iteration 503"); exit(0); } + if ( do_504() != 504 ) { FAIL("iteration 504"); exit(0); } + if ( do_505() != 505 ) { FAIL("iteration 505"); exit(0); } + if ( do_506() != 506 ) { FAIL("iteration 506"); exit(0); } + if ( do_507() != 507 ) { FAIL("iteration 507"); exit(0); } + if ( do_508() != 508 ) { FAIL("iteration 508"); exit(0); } + if ( do_509() != 509 ) { FAIL("iteration 509"); exit(0); } + if ( do_510() != 510 ) { FAIL("iteration 510"); exit(0); } + if ( do_511() != 511 ) { FAIL("iteration 511"); exit(0); } + if ( do_512() != 512 ) { FAIL("iteration 512"); exit(0); } + if ( do_513() != 513 ) { FAIL("iteration 513"); exit(0); } + if ( do_514() != 514 ) { FAIL("iteration 514"); exit(0); } + if ( do_515() != 515 ) { FAIL("iteration 515"); exit(0); } + if ( do_516() != 516 ) { FAIL("iteration 516"); exit(0); } + if ( do_517() != 517 ) { FAIL("iteration 517"); exit(0); } + if ( do_518() != 518 ) { FAIL("iteration 518"); exit(0); } + if ( do_519() != 519 ) { FAIL("iteration 519"); exit(0); } + if ( do_520() != 520 ) { FAIL("iteration 520"); exit(0); } + if ( do_521() != 521 ) { FAIL("iteration 521"); exit(0); } + if ( do_522() != 522 ) { FAIL("iteration 522"); exit(0); } + if ( do_523() != 523 ) { FAIL("iteration 523"); exit(0); } + if ( do_524() != 524 ) { FAIL("iteration 524"); exit(0); } + if ( do_525() != 525 ) { FAIL("iteration 525"); exit(0); } + if ( do_526() != 526 ) { FAIL("iteration 526"); exit(0); } + if ( do_527() != 527 ) { FAIL("iteration 527"); exit(0); } + if ( do_528() != 528 ) { FAIL("iteration 528"); exit(0); } + if ( do_529() != 529 ) { FAIL("iteration 529"); exit(0); } + if ( do_530() != 530 ) { FAIL("iteration 530"); exit(0); } + if ( do_531() != 531 ) { FAIL("iteration 531"); exit(0); } + if ( do_532() != 532 ) { FAIL("iteration 532"); exit(0); } + if ( do_533() != 533 ) { FAIL("iteration 533"); exit(0); } + if ( do_534() != 534 ) { FAIL("iteration 534"); exit(0); } + if ( do_535() != 535 ) { FAIL("iteration 535"); exit(0); } + if ( do_536() != 536 ) { FAIL("iteration 536"); exit(0); } + if ( do_537() != 537 ) { FAIL("iteration 537"); exit(0); } + if ( do_538() != 538 ) { FAIL("iteration 538"); exit(0); } + if ( do_539() != 539 ) { FAIL("iteration 539"); exit(0); } + if ( do_540() != 540 ) { FAIL("iteration 540"); exit(0); } + if ( do_541() != 541 ) { FAIL("iteration 541"); exit(0); } + if ( do_542() != 542 ) { FAIL("iteration 542"); exit(0); } + if ( do_543() != 543 ) { FAIL("iteration 543"); exit(0); } + if ( do_544() != 544 ) { FAIL("iteration 544"); exit(0); } + if ( do_545() != 545 ) { FAIL("iteration 545"); exit(0); } + if ( do_546() != 546 ) { FAIL("iteration 546"); exit(0); } + if ( do_547() != 547 ) { FAIL("iteration 547"); exit(0); } + if ( do_548() != 548 ) { FAIL("iteration 548"); exit(0); } + if ( do_549() != 549 ) { FAIL("iteration 549"); exit(0); } + if ( do_550() != 550 ) { FAIL("iteration 550"); exit(0); } + if ( do_551() != 551 ) { FAIL("iteration 551"); exit(0); } + if ( do_552() != 552 ) { FAIL("iteration 552"); exit(0); } + if ( do_553() != 553 ) { FAIL("iteration 553"); exit(0); } + if ( do_554() != 554 ) { FAIL("iteration 554"); exit(0); } + if ( do_555() != 555 ) { FAIL("iteration 555"); exit(0); } + if ( do_556() != 556 ) { FAIL("iteration 556"); exit(0); } + if ( do_557() != 557 ) { FAIL("iteration 557"); exit(0); } + if ( do_558() != 558 ) { FAIL("iteration 558"); exit(0); } + if ( do_559() != 559 ) { FAIL("iteration 559"); exit(0); } + if ( do_560() != 560 ) { FAIL("iteration 560"); exit(0); } + if ( do_561() != 561 ) { FAIL("iteration 561"); exit(0); } + if ( do_562() != 562 ) { FAIL("iteration 562"); exit(0); } + if ( do_563() != 563 ) { FAIL("iteration 563"); exit(0); } + if ( do_564() != 564 ) { FAIL("iteration 564"); exit(0); } + if ( do_565() != 565 ) { FAIL("iteration 565"); exit(0); } + if ( do_566() != 566 ) { FAIL("iteration 566"); exit(0); } + if ( do_567() != 567 ) { FAIL("iteration 567"); exit(0); } + if ( do_568() != 568 ) { FAIL("iteration 568"); exit(0); } + if ( do_569() != 569 ) { FAIL("iteration 569"); exit(0); } + if ( do_570() != 570 ) { FAIL("iteration 570"); exit(0); } + if ( do_571() != 571 ) { FAIL("iteration 571"); exit(0); } + if ( do_572() != 572 ) { FAIL("iteration 572"); exit(0); } + if ( do_573() != 573 ) { FAIL("iteration 573"); exit(0); } + if ( do_574() != 574 ) { FAIL("iteration 574"); exit(0); } + if ( do_575() != 575 ) { FAIL("iteration 575"); exit(0); } + if ( do_576() != 576 ) { FAIL("iteration 576"); exit(0); } + if ( do_577() != 577 ) { FAIL("iteration 577"); exit(0); } + if ( do_578() != 578 ) { FAIL("iteration 578"); exit(0); } + if ( do_579() != 579 ) { FAIL("iteration 579"); exit(0); } + if ( do_580() != 580 ) { FAIL("iteration 580"); exit(0); } + if ( do_581() != 581 ) { FAIL("iteration 581"); exit(0); } + if ( do_582() != 582 ) { FAIL("iteration 582"); exit(0); } + if ( do_583() != 583 ) { FAIL("iteration 583"); exit(0); } + if ( do_584() != 584 ) { FAIL("iteration 584"); exit(0); } + if ( do_585() != 585 ) { FAIL("iteration 585"); exit(0); } + if ( do_586() != 586 ) { FAIL("iteration 586"); exit(0); } + if ( do_587() != 587 ) { FAIL("iteration 587"); exit(0); } + if ( do_588() != 588 ) { FAIL("iteration 588"); exit(0); } + if ( do_589() != 589 ) { FAIL("iteration 589"); exit(0); } + if ( do_590() != 590 ) { FAIL("iteration 590"); exit(0); } + if ( do_591() != 591 ) { FAIL("iteration 591"); exit(0); } + if ( do_592() != 592 ) { FAIL("iteration 592"); exit(0); } + if ( do_593() != 593 ) { FAIL("iteration 593"); exit(0); } + if ( do_594() != 594 ) { FAIL("iteration 594"); exit(0); } + if ( do_595() != 595 ) { FAIL("iteration 595"); exit(0); } + if ( do_596() != 596 ) { FAIL("iteration 596"); exit(0); } + if ( do_597() != 597 ) { FAIL("iteration 597"); exit(0); } + if ( do_598() != 598 ) { FAIL("iteration 598"); exit(0); } + if ( do_599() != 599 ) { FAIL("iteration 599"); exit(0); } + if ( do_600() != 600 ) { FAIL("iteration 600"); exit(0); } + if ( do_601() != 601 ) { FAIL("iteration 601"); exit(0); } + if ( do_602() != 602 ) { FAIL("iteration 602"); exit(0); } + if ( do_603() != 603 ) { FAIL("iteration 603"); exit(0); } + if ( do_604() != 604 ) { FAIL("iteration 604"); exit(0); } + if ( do_605() != 605 ) { FAIL("iteration 605"); exit(0); } + if ( do_606() != 606 ) { FAIL("iteration 606"); exit(0); } + if ( do_607() != 607 ) { FAIL("iteration 607"); exit(0); } + if ( do_608() != 608 ) { FAIL("iteration 608"); exit(0); } + if ( do_609() != 609 ) { FAIL("iteration 609"); exit(0); } + if ( do_610() != 610 ) { FAIL("iteration 610"); exit(0); } + if ( do_611() != 611 ) { FAIL("iteration 611"); exit(0); } + if ( do_612() != 612 ) { FAIL("iteration 612"); exit(0); } + if ( do_613() != 613 ) { FAIL("iteration 613"); exit(0); } + if ( do_614() != 614 ) { FAIL("iteration 614"); exit(0); } + if ( do_615() != 615 ) { FAIL("iteration 615"); exit(0); } + if ( do_616() != 616 ) { FAIL("iteration 616"); exit(0); } + if ( do_617() != 617 ) { FAIL("iteration 617"); exit(0); } + if ( do_618() != 618 ) { FAIL("iteration 618"); exit(0); } + if ( do_619() != 619 ) { FAIL("iteration 619"); exit(0); } + if ( do_620() != 620 ) { FAIL("iteration 620"); exit(0); } + if ( do_621() != 621 ) { FAIL("iteration 621"); exit(0); } + if ( do_622() != 622 ) { FAIL("iteration 622"); exit(0); } + if ( do_623() != 623 ) { FAIL("iteration 623"); exit(0); } + if ( do_624() != 624 ) { FAIL("iteration 624"); exit(0); } + if ( do_625() != 625 ) { FAIL("iteration 625"); exit(0); } + if ( do_626() != 626 ) { FAIL("iteration 626"); exit(0); } + if ( do_627() != 627 ) { FAIL("iteration 627"); exit(0); } + if ( do_628() != 628 ) { FAIL("iteration 628"); exit(0); } + if ( do_629() != 629 ) { FAIL("iteration 629"); exit(0); } + if ( do_630() != 630 ) { FAIL("iteration 630"); exit(0); } + if ( do_631() != 631 ) { FAIL("iteration 631"); exit(0); } + if ( do_632() != 632 ) { FAIL("iteration 632"); exit(0); } + if ( do_633() != 633 ) { FAIL("iteration 633"); exit(0); } + if ( do_634() != 634 ) { FAIL("iteration 634"); exit(0); } + if ( do_635() != 635 ) { FAIL("iteration 635"); exit(0); } + if ( do_636() != 636 ) { FAIL("iteration 636"); exit(0); } + if ( do_637() != 637 ) { FAIL("iteration 637"); exit(0); } + if ( do_638() != 638 ) { FAIL("iteration 638"); exit(0); } + if ( do_639() != 639 ) { FAIL("iteration 639"); exit(0); } + if ( do_640() != 640 ) { FAIL("iteration 640"); exit(0); } + if ( do_641() != 641 ) { FAIL("iteration 641"); exit(0); } + if ( do_642() != 642 ) { FAIL("iteration 642"); exit(0); } + if ( do_643() != 643 ) { FAIL("iteration 643"); exit(0); } + if ( do_644() != 644 ) { FAIL("iteration 644"); exit(0); } + if ( do_645() != 645 ) { FAIL("iteration 645"); exit(0); } + if ( do_646() != 646 ) { FAIL("iteration 646"); exit(0); } + if ( do_647() != 647 ) { FAIL("iteration 647"); exit(0); } + if ( do_648() != 648 ) { FAIL("iteration 648"); exit(0); } + if ( do_649() != 649 ) { FAIL("iteration 649"); exit(0); } + if ( do_650() != 650 ) { FAIL("iteration 650"); exit(0); } + if ( do_651() != 651 ) { FAIL("iteration 651"); exit(0); } + if ( do_652() != 652 ) { FAIL("iteration 652"); exit(0); } + if ( do_653() != 653 ) { FAIL("iteration 653"); exit(0); } + if ( do_654() != 654 ) { FAIL("iteration 654"); exit(0); } + if ( do_655() != 655 ) { FAIL("iteration 655"); exit(0); } + if ( do_656() != 656 ) { FAIL("iteration 656"); exit(0); } + if ( do_657() != 657 ) { FAIL("iteration 657"); exit(0); } + if ( do_658() != 658 ) { FAIL("iteration 658"); exit(0); } + if ( do_659() != 659 ) { FAIL("iteration 659"); exit(0); } + if ( do_660() != 660 ) { FAIL("iteration 660"); exit(0); } + if ( do_661() != 661 ) { FAIL("iteration 661"); exit(0); } + if ( do_662() != 662 ) { FAIL("iteration 662"); exit(0); } + if ( do_663() != 663 ) { FAIL("iteration 663"); exit(0); } + if ( do_664() != 664 ) { FAIL("iteration 664"); exit(0); } + if ( do_665() != 665 ) { FAIL("iteration 665"); exit(0); } + if ( do_666() != 666 ) { FAIL("iteration 666"); exit(0); } + if ( do_667() != 667 ) { FAIL("iteration 667"); exit(0); } + if ( do_668() != 668 ) { FAIL("iteration 668"); exit(0); } + if ( do_669() != 669 ) { FAIL("iteration 669"); exit(0); } + if ( do_670() != 670 ) { FAIL("iteration 670"); exit(0); } + if ( do_671() != 671 ) { FAIL("iteration 671"); exit(0); } + if ( do_672() != 672 ) { FAIL("iteration 672"); exit(0); } + if ( do_673() != 673 ) { FAIL("iteration 673"); exit(0); } + if ( do_674() != 674 ) { FAIL("iteration 674"); exit(0); } + if ( do_675() != 675 ) { FAIL("iteration 675"); exit(0); } + if ( do_676() != 676 ) { FAIL("iteration 676"); exit(0); } + if ( do_677() != 677 ) { FAIL("iteration 677"); exit(0); } + if ( do_678() != 678 ) { FAIL("iteration 678"); exit(0); } + if ( do_679() != 679 ) { FAIL("iteration 679"); exit(0); } + if ( do_680() != 680 ) { FAIL("iteration 680"); exit(0); } + if ( do_681() != 681 ) { FAIL("iteration 681"); exit(0); } + if ( do_682() != 682 ) { FAIL("iteration 682"); exit(0); } + if ( do_683() != 683 ) { FAIL("iteration 683"); exit(0); } + if ( do_684() != 684 ) { FAIL("iteration 684"); exit(0); } + if ( do_685() != 685 ) { FAIL("iteration 685"); exit(0); } + if ( do_686() != 686 ) { FAIL("iteration 686"); exit(0); } + if ( do_687() != 687 ) { FAIL("iteration 687"); exit(0); } + if ( do_688() != 688 ) { FAIL("iteration 688"); exit(0); } + if ( do_689() != 689 ) { FAIL("iteration 689"); exit(0); } + if ( do_690() != 690 ) { FAIL("iteration 690"); exit(0); } + if ( do_691() != 691 ) { FAIL("iteration 691"); exit(0); } + if ( do_692() != 692 ) { FAIL("iteration 692"); exit(0); } + if ( do_693() != 693 ) { FAIL("iteration 693"); exit(0); } + if ( do_694() != 694 ) { FAIL("iteration 694"); exit(0); } + if ( do_695() != 695 ) { FAIL("iteration 695"); exit(0); } + if ( do_696() != 696 ) { FAIL("iteration 696"); exit(0); } + if ( do_697() != 697 ) { FAIL("iteration 697"); exit(0); } + if ( do_698() != 698 ) { FAIL("iteration 698"); exit(0); } + if ( do_699() != 699 ) { FAIL("iteration 699"); exit(0); } + if ( do_700() != 700 ) { FAIL("iteration 700"); exit(0); } + if ( do_701() != 701 ) { FAIL("iteration 701"); exit(0); } + if ( do_702() != 702 ) { FAIL("iteration 702"); exit(0); } + if ( do_703() != 703 ) { FAIL("iteration 703"); exit(0); } + if ( do_704() != 704 ) { FAIL("iteration 704"); exit(0); } + if ( do_705() != 705 ) { FAIL("iteration 705"); exit(0); } + if ( do_706() != 706 ) { FAIL("iteration 706"); exit(0); } + if ( do_707() != 707 ) { FAIL("iteration 707"); exit(0); } + if ( do_708() != 708 ) { FAIL("iteration 708"); exit(0); } + if ( do_709() != 709 ) { FAIL("iteration 709"); exit(0); } + if ( do_710() != 710 ) { FAIL("iteration 710"); exit(0); } + if ( do_711() != 711 ) { FAIL("iteration 711"); exit(0); } + if ( do_712() != 712 ) { FAIL("iteration 712"); exit(0); } + if ( do_713() != 713 ) { FAIL("iteration 713"); exit(0); } + if ( do_714() != 714 ) { FAIL("iteration 714"); exit(0); } + if ( do_715() != 715 ) { FAIL("iteration 715"); exit(0); } + if ( do_716() != 716 ) { FAIL("iteration 716"); exit(0); } + if ( do_717() != 717 ) { FAIL("iteration 717"); exit(0); } + if ( do_718() != 718 ) { FAIL("iteration 718"); exit(0); } + if ( do_719() != 719 ) { FAIL("iteration 719"); exit(0); } + if ( do_720() != 720 ) { FAIL("iteration 720"); exit(0); } + if ( do_721() != 721 ) { FAIL("iteration 721"); exit(0); } + if ( do_722() != 722 ) { FAIL("iteration 722"); exit(0); } + if ( do_723() != 723 ) { FAIL("iteration 723"); exit(0); } + if ( do_724() != 724 ) { FAIL("iteration 724"); exit(0); } + if ( do_725() != 725 ) { FAIL("iteration 725"); exit(0); } + if ( do_726() != 726 ) { FAIL("iteration 726"); exit(0); } + if ( do_727() != 727 ) { FAIL("iteration 727"); exit(0); } + if ( do_728() != 728 ) { FAIL("iteration 728"); exit(0); } + if ( do_729() != 729 ) { FAIL("iteration 729"); exit(0); } + if ( do_730() != 730 ) { FAIL("iteration 730"); exit(0); } + if ( do_731() != 731 ) { FAIL("iteration 731"); exit(0); } + if ( do_732() != 732 ) { FAIL("iteration 732"); exit(0); } + if ( do_733() != 733 ) { FAIL("iteration 733"); exit(0); } + if ( do_734() != 734 ) { FAIL("iteration 734"); exit(0); } + if ( do_735() != 735 ) { FAIL("iteration 735"); exit(0); } + if ( do_736() != 736 ) { FAIL("iteration 736"); exit(0); } + if ( do_737() != 737 ) { FAIL("iteration 737"); exit(0); } + if ( do_738() != 738 ) { FAIL("iteration 738"); exit(0); } + if ( do_739() != 739 ) { FAIL("iteration 739"); exit(0); } + if ( do_740() != 740 ) { FAIL("iteration 740"); exit(0); } + if ( do_741() != 741 ) { FAIL("iteration 741"); exit(0); } + if ( do_742() != 742 ) { FAIL("iteration 742"); exit(0); } + if ( do_743() != 743 ) { FAIL("iteration 743"); exit(0); } + if ( do_744() != 744 ) { FAIL("iteration 744"); exit(0); } + if ( do_745() != 745 ) { FAIL("iteration 745"); exit(0); } + if ( do_746() != 746 ) { FAIL("iteration 746"); exit(0); } + if ( do_747() != 747 ) { FAIL("iteration 747"); exit(0); } + if ( do_748() != 748 ) { FAIL("iteration 748"); exit(0); } + if ( do_749() != 749 ) { FAIL("iteration 749"); exit(0); } + if ( do_750() != 750 ) { FAIL("iteration 750"); exit(0); } + if ( do_751() != 751 ) { FAIL("iteration 751"); exit(0); } + if ( do_752() != 752 ) { FAIL("iteration 752"); exit(0); } + if ( do_753() != 753 ) { FAIL("iteration 753"); exit(0); } + if ( do_754() != 754 ) { FAIL("iteration 754"); exit(0); } + if ( do_755() != 755 ) { FAIL("iteration 755"); exit(0); } + if ( do_756() != 756 ) { FAIL("iteration 756"); exit(0); } + if ( do_757() != 757 ) { FAIL("iteration 757"); exit(0); } + if ( do_758() != 758 ) { FAIL("iteration 758"); exit(0); } + if ( do_759() != 759 ) { FAIL("iteration 759"); exit(0); } + if ( do_760() != 760 ) { FAIL("iteration 760"); exit(0); } + if ( do_761() != 761 ) { FAIL("iteration 761"); exit(0); } + if ( do_762() != 762 ) { FAIL("iteration 762"); exit(0); } + if ( do_763() != 763 ) { FAIL("iteration 763"); exit(0); } + if ( do_764() != 764 ) { FAIL("iteration 764"); exit(0); } + if ( do_765() != 765 ) { FAIL("iteration 765"); exit(0); } + if ( do_766() != 766 ) { FAIL("iteration 766"); exit(0); } + if ( do_767() != 767 ) { FAIL("iteration 767"); exit(0); } + if ( do_768() != 768 ) { FAIL("iteration 768"); exit(0); } + if ( do_769() != 769 ) { FAIL("iteration 769"); exit(0); } + if ( do_770() != 770 ) { FAIL("iteration 770"); exit(0); } + if ( do_771() != 771 ) { FAIL("iteration 771"); exit(0); } + if ( do_772() != 772 ) { FAIL("iteration 772"); exit(0); } + if ( do_773() != 773 ) { FAIL("iteration 773"); exit(0); } + if ( do_774() != 774 ) { FAIL("iteration 774"); exit(0); } + if ( do_775() != 775 ) { FAIL("iteration 775"); exit(0); } + if ( do_776() != 776 ) { FAIL("iteration 776"); exit(0); } + if ( do_777() != 777 ) { FAIL("iteration 777"); exit(0); } + if ( do_778() != 778 ) { FAIL("iteration 778"); exit(0); } + if ( do_779() != 779 ) { FAIL("iteration 779"); exit(0); } + if ( do_780() != 780 ) { FAIL("iteration 780"); exit(0); } + if ( do_781() != 781 ) { FAIL("iteration 781"); exit(0); } + if ( do_782() != 782 ) { FAIL("iteration 782"); exit(0); } + if ( do_783() != 783 ) { FAIL("iteration 783"); exit(0); } + if ( do_784() != 784 ) { FAIL("iteration 784"); exit(0); } + if ( do_785() != 785 ) { FAIL("iteration 785"); exit(0); } + if ( do_786() != 786 ) { FAIL("iteration 786"); exit(0); } + if ( do_787() != 787 ) { FAIL("iteration 787"); exit(0); } + if ( do_788() != 788 ) { FAIL("iteration 788"); exit(0); } + if ( do_789() != 789 ) { FAIL("iteration 789"); exit(0); } + if ( do_790() != 790 ) { FAIL("iteration 790"); exit(0); } + if ( do_791() != 791 ) { FAIL("iteration 791"); exit(0); } + if ( do_792() != 792 ) { FAIL("iteration 792"); exit(0); } + if ( do_793() != 793 ) { FAIL("iteration 793"); exit(0); } + if ( do_794() != 794 ) { FAIL("iteration 794"); exit(0); } + if ( do_795() != 795 ) { FAIL("iteration 795"); exit(0); } + if ( do_796() != 796 ) { FAIL("iteration 796"); exit(0); } + if ( do_797() != 797 ) { FAIL("iteration 797"); exit(0); } + if ( do_798() != 798 ) { FAIL("iteration 798"); exit(0); } + if ( do_799() != 799 ) { FAIL("iteration 799"); exit(0); } + if ( do_800() != 800 ) { FAIL("iteration 800"); exit(0); } + if ( do_801() != 801 ) { FAIL("iteration 801"); exit(0); } + if ( do_802() != 802 ) { FAIL("iteration 802"); exit(0); } + if ( do_803() != 803 ) { FAIL("iteration 803"); exit(0); } + if ( do_804() != 804 ) { FAIL("iteration 804"); exit(0); } + if ( do_805() != 805 ) { FAIL("iteration 805"); exit(0); } + if ( do_806() != 806 ) { FAIL("iteration 806"); exit(0); } + if ( do_807() != 807 ) { FAIL("iteration 807"); exit(0); } + if ( do_808() != 808 ) { FAIL("iteration 808"); exit(0); } + if ( do_809() != 809 ) { FAIL("iteration 809"); exit(0); } + if ( do_810() != 810 ) { FAIL("iteration 810"); exit(0); } + if ( do_811() != 811 ) { FAIL("iteration 811"); exit(0); } + if ( do_812() != 812 ) { FAIL("iteration 812"); exit(0); } + if ( do_813() != 813 ) { FAIL("iteration 813"); exit(0); } + if ( do_814() != 814 ) { FAIL("iteration 814"); exit(0); } + if ( do_815() != 815 ) { FAIL("iteration 815"); exit(0); } + if ( do_816() != 816 ) { FAIL("iteration 816"); exit(0); } + if ( do_817() != 817 ) { FAIL("iteration 817"); exit(0); } + if ( do_818() != 818 ) { FAIL("iteration 818"); exit(0); } + if ( do_819() != 819 ) { FAIL("iteration 819"); exit(0); } + if ( do_820() != 820 ) { FAIL("iteration 820"); exit(0); } + if ( do_821() != 821 ) { FAIL("iteration 821"); exit(0); } + if ( do_822() != 822 ) { FAIL("iteration 822"); exit(0); } + if ( do_823() != 823 ) { FAIL("iteration 823"); exit(0); } + if ( do_824() != 824 ) { FAIL("iteration 824"); exit(0); } + if ( do_825() != 825 ) { FAIL("iteration 825"); exit(0); } + if ( do_826() != 826 ) { FAIL("iteration 826"); exit(0); } + if ( do_827() != 827 ) { FAIL("iteration 827"); exit(0); } + if ( do_828() != 828 ) { FAIL("iteration 828"); exit(0); } + if ( do_829() != 829 ) { FAIL("iteration 829"); exit(0); } + if ( do_830() != 830 ) { FAIL("iteration 830"); exit(0); } + if ( do_831() != 831 ) { FAIL("iteration 831"); exit(0); } + if ( do_832() != 832 ) { FAIL("iteration 832"); exit(0); } + if ( do_833() != 833 ) { FAIL("iteration 833"); exit(0); } + if ( do_834() != 834 ) { FAIL("iteration 834"); exit(0); } + if ( do_835() != 835 ) { FAIL("iteration 835"); exit(0); } + if ( do_836() != 836 ) { FAIL("iteration 836"); exit(0); } + if ( do_837() != 837 ) { FAIL("iteration 837"); exit(0); } + if ( do_838() != 838 ) { FAIL("iteration 838"); exit(0); } + if ( do_839() != 839 ) { FAIL("iteration 839"); exit(0); } + if ( do_840() != 840 ) { FAIL("iteration 840"); exit(0); } + if ( do_841() != 841 ) { FAIL("iteration 841"); exit(0); } + if ( do_842() != 842 ) { FAIL("iteration 842"); exit(0); } + if ( do_843() != 843 ) { FAIL("iteration 843"); exit(0); } + if ( do_844() != 844 ) { FAIL("iteration 844"); exit(0); } + if ( do_845() != 845 ) { FAIL("iteration 845"); exit(0); } + if ( do_846() != 846 ) { FAIL("iteration 846"); exit(0); } + if ( do_847() != 847 ) { FAIL("iteration 847"); exit(0); } + if ( do_848() != 848 ) { FAIL("iteration 848"); exit(0); } + if ( do_849() != 849 ) { FAIL("iteration 849"); exit(0); } + if ( do_850() != 850 ) { FAIL("iteration 850"); exit(0); } + if ( do_851() != 851 ) { FAIL("iteration 851"); exit(0); } + if ( do_852() != 852 ) { FAIL("iteration 852"); exit(0); } + if ( do_853() != 853 ) { FAIL("iteration 853"); exit(0); } + if ( do_854() != 854 ) { FAIL("iteration 854"); exit(0); } + if ( do_855() != 855 ) { FAIL("iteration 855"); exit(0); } + if ( do_856() != 856 ) { FAIL("iteration 856"); exit(0); } + if ( do_857() != 857 ) { FAIL("iteration 857"); exit(0); } + if ( do_858() != 858 ) { FAIL("iteration 858"); exit(0); } + if ( do_859() != 859 ) { FAIL("iteration 859"); exit(0); } + if ( do_860() != 860 ) { FAIL("iteration 860"); exit(0); } + if ( do_861() != 861 ) { FAIL("iteration 861"); exit(0); } + if ( do_862() != 862 ) { FAIL("iteration 862"); exit(0); } + if ( do_863() != 863 ) { FAIL("iteration 863"); exit(0); } + if ( do_864() != 864 ) { FAIL("iteration 864"); exit(0); } + if ( do_865() != 865 ) { FAIL("iteration 865"); exit(0); } + if ( do_866() != 866 ) { FAIL("iteration 866"); exit(0); } + if ( do_867() != 867 ) { FAIL("iteration 867"); exit(0); } + if ( do_868() != 868 ) { FAIL("iteration 868"); exit(0); } + if ( do_869() != 869 ) { FAIL("iteration 869"); exit(0); } + if ( do_870() != 870 ) { FAIL("iteration 870"); exit(0); } + if ( do_871() != 871 ) { FAIL("iteration 871"); exit(0); } + if ( do_872() != 872 ) { FAIL("iteration 872"); exit(0); } + if ( do_873() != 873 ) { FAIL("iteration 873"); exit(0); } + if ( do_874() != 874 ) { FAIL("iteration 874"); exit(0); } + if ( do_875() != 875 ) { FAIL("iteration 875"); exit(0); } + if ( do_876() != 876 ) { FAIL("iteration 876"); exit(0); } + if ( do_877() != 877 ) { FAIL("iteration 877"); exit(0); } + if ( do_878() != 878 ) { FAIL("iteration 878"); exit(0); } + if ( do_879() != 879 ) { FAIL("iteration 879"); exit(0); } + if ( do_880() != 880 ) { FAIL("iteration 880"); exit(0); } + if ( do_881() != 881 ) { FAIL("iteration 881"); exit(0); } + if ( do_882() != 882 ) { FAIL("iteration 882"); exit(0); } + if ( do_883() != 883 ) { FAIL("iteration 883"); exit(0); } + if ( do_884() != 884 ) { FAIL("iteration 884"); exit(0); } + if ( do_885() != 885 ) { FAIL("iteration 885"); exit(0); } + if ( do_886() != 886 ) { FAIL("iteration 886"); exit(0); } + if ( do_887() != 887 ) { FAIL("iteration 887"); exit(0); } + if ( do_888() != 888 ) { FAIL("iteration 888"); exit(0); } + if ( do_889() != 889 ) { FAIL("iteration 889"); exit(0); } + if ( do_890() != 890 ) { FAIL("iteration 890"); exit(0); } + if ( do_891() != 891 ) { FAIL("iteration 891"); exit(0); } + if ( do_892() != 892 ) { FAIL("iteration 892"); exit(0); } + if ( do_893() != 893 ) { FAIL("iteration 893"); exit(0); } + if ( do_894() != 894 ) { FAIL("iteration 894"); exit(0); } + if ( do_895() != 895 ) { FAIL("iteration 895"); exit(0); } + if ( do_896() != 896 ) { FAIL("iteration 896"); exit(0); } + if ( do_897() != 897 ) { FAIL("iteration 897"); exit(0); } + if ( do_898() != 898 ) { FAIL("iteration 898"); exit(0); } + if ( do_899() != 899 ) { FAIL("iteration 899"); exit(0); } + if ( do_900() != 900 ) { FAIL("iteration 900"); exit(0); } + if ( do_901() != 901 ) { FAIL("iteration 901"); exit(0); } + if ( do_902() != 902 ) { FAIL("iteration 902"); exit(0); } + if ( do_903() != 903 ) { FAIL("iteration 903"); exit(0); } + if ( do_904() != 904 ) { FAIL("iteration 904"); exit(0); } + if ( do_905() != 905 ) { FAIL("iteration 905"); exit(0); } + if ( do_906() != 906 ) { FAIL("iteration 906"); exit(0); } + if ( do_907() != 907 ) { FAIL("iteration 907"); exit(0); } + if ( do_908() != 908 ) { FAIL("iteration 908"); exit(0); } + if ( do_909() != 909 ) { FAIL("iteration 909"); exit(0); } + if ( do_910() != 910 ) { FAIL("iteration 910"); exit(0); } + if ( do_911() != 911 ) { FAIL("iteration 911"); exit(0); } + if ( do_912() != 912 ) { FAIL("iteration 912"); exit(0); } + if ( do_913() != 913 ) { FAIL("iteration 913"); exit(0); } + if ( do_914() != 914 ) { FAIL("iteration 914"); exit(0); } + if ( do_915() != 915 ) { FAIL("iteration 915"); exit(0); } + if ( do_916() != 916 ) { FAIL("iteration 916"); exit(0); } + if ( do_917() != 917 ) { FAIL("iteration 917"); exit(0); } + if ( do_918() != 918 ) { FAIL("iteration 918"); exit(0); } + if ( do_919() != 919 ) { FAIL("iteration 919"); exit(0); } + if ( do_920() != 920 ) { FAIL("iteration 920"); exit(0); } + if ( do_921() != 921 ) { FAIL("iteration 921"); exit(0); } + if ( do_922() != 922 ) { FAIL("iteration 922"); exit(0); } + if ( do_923() != 923 ) { FAIL("iteration 923"); exit(0); } + if ( do_924() != 924 ) { FAIL("iteration 924"); exit(0); } + if ( do_925() != 925 ) { FAIL("iteration 925"); exit(0); } + if ( do_926() != 926 ) { FAIL("iteration 926"); exit(0); } + if ( do_927() != 927 ) { FAIL("iteration 927"); exit(0); } + if ( do_928() != 928 ) { FAIL("iteration 928"); exit(0); } + if ( do_929() != 929 ) { FAIL("iteration 929"); exit(0); } + if ( do_930() != 930 ) { FAIL("iteration 930"); exit(0); } + if ( do_931() != 931 ) { FAIL("iteration 931"); exit(0); } + if ( do_932() != 932 ) { FAIL("iteration 932"); exit(0); } + if ( do_933() != 933 ) { FAIL("iteration 933"); exit(0); } + if ( do_934() != 934 ) { FAIL("iteration 934"); exit(0); } + if ( do_935() != 935 ) { FAIL("iteration 935"); exit(0); } + if ( do_936() != 936 ) { FAIL("iteration 936"); exit(0); } + if ( do_937() != 937 ) { FAIL("iteration 937"); exit(0); } + if ( do_938() != 938 ) { FAIL("iteration 938"); exit(0); } + if ( do_939() != 939 ) { FAIL("iteration 939"); exit(0); } + if ( do_940() != 940 ) { FAIL("iteration 940"); exit(0); } + if ( do_941() != 941 ) { FAIL("iteration 941"); exit(0); } + if ( do_942() != 942 ) { FAIL("iteration 942"); exit(0); } + if ( do_943() != 943 ) { FAIL("iteration 943"); exit(0); } + if ( do_944() != 944 ) { FAIL("iteration 944"); exit(0); } + if ( do_945() != 945 ) { FAIL("iteration 945"); exit(0); } + if ( do_946() != 946 ) { FAIL("iteration 946"); exit(0); } + if ( do_947() != 947 ) { FAIL("iteration 947"); exit(0); } + if ( do_948() != 948 ) { FAIL("iteration 948"); exit(0); } + if ( do_949() != 949 ) { FAIL("iteration 949"); exit(0); } + if ( do_950() != 950 ) { FAIL("iteration 950"); exit(0); } + if ( do_951() != 951 ) { FAIL("iteration 951"); exit(0); } + if ( do_952() != 952 ) { FAIL("iteration 952"); exit(0); } + if ( do_953() != 953 ) { FAIL("iteration 953"); exit(0); } + if ( do_954() != 954 ) { FAIL("iteration 954"); exit(0); } + if ( do_955() != 955 ) { FAIL("iteration 955"); exit(0); } + if ( do_956() != 956 ) { FAIL("iteration 956"); exit(0); } + if ( do_957() != 957 ) { FAIL("iteration 957"); exit(0); } + if ( do_958() != 958 ) { FAIL("iteration 958"); exit(0); } + if ( do_959() != 959 ) { FAIL("iteration 959"); exit(0); } + if ( do_960() != 960 ) { FAIL("iteration 960"); exit(0); } + if ( do_961() != 961 ) { FAIL("iteration 961"); exit(0); } + if ( do_962() != 962 ) { FAIL("iteration 962"); exit(0); } + if ( do_963() != 963 ) { FAIL("iteration 963"); exit(0); } + if ( do_964() != 964 ) { FAIL("iteration 964"); exit(0); } + if ( do_965() != 965 ) { FAIL("iteration 965"); exit(0); } + if ( do_966() != 966 ) { FAIL("iteration 966"); exit(0); } + if ( do_967() != 967 ) { FAIL("iteration 967"); exit(0); } + if ( do_968() != 968 ) { FAIL("iteration 968"); exit(0); } + if ( do_969() != 969 ) { FAIL("iteration 969"); exit(0); } + if ( do_970() != 970 ) { FAIL("iteration 970"); exit(0); } + if ( do_971() != 971 ) { FAIL("iteration 971"); exit(0); } + if ( do_972() != 972 ) { FAIL("iteration 972"); exit(0); } + if ( do_973() != 973 ) { FAIL("iteration 973"); exit(0); } + if ( do_974() != 974 ) { FAIL("iteration 974"); exit(0); } + if ( do_975() != 975 ) { FAIL("iteration 975"); exit(0); } + if ( do_976() != 976 ) { FAIL("iteration 976"); exit(0); } + if ( do_977() != 977 ) { FAIL("iteration 977"); exit(0); } + if ( do_978() != 978 ) { FAIL("iteration 978"); exit(0); } + if ( do_979() != 979 ) { FAIL("iteration 979"); exit(0); } + if ( do_980() != 980 ) { FAIL("iteration 980"); exit(0); } + if ( do_981() != 981 ) { FAIL("iteration 981"); exit(0); } + if ( do_982() != 982 ) { FAIL("iteration 982"); exit(0); } + if ( do_983() != 983 ) { FAIL("iteration 983"); exit(0); } + if ( do_984() != 984 ) { FAIL("iteration 984"); exit(0); } + if ( do_985() != 985 ) { FAIL("iteration 985"); exit(0); } + if ( do_986() != 986 ) { FAIL("iteration 986"); exit(0); } + if ( do_987() != 987 ) { FAIL("iteration 987"); exit(0); } + if ( do_988() != 988 ) { FAIL("iteration 988"); exit(0); } + if ( do_989() != 989 ) { FAIL("iteration 989"); exit(0); } + if ( do_990() != 990 ) { FAIL("iteration 990"); exit(0); } + if ( do_991() != 991 ) { FAIL("iteration 991"); exit(0); } + if ( do_992() != 992 ) { FAIL("iteration 992"); exit(0); } + if ( do_993() != 993 ) { FAIL("iteration 993"); exit(0); } + if ( do_994() != 994 ) { FAIL("iteration 994"); exit(0); } + if ( do_995() != 995 ) { FAIL("iteration 995"); exit(0); } + if ( do_996() != 996 ) { FAIL("iteration 996"); exit(0); } + if ( do_997() != 997 ) { FAIL("iteration 997"); exit(0); } + if ( do_998() != 998 ) { FAIL("iteration 998"); exit(0); } + if ( do_999() != 999 ) { FAIL("iteration 999"); exit(0); } + return NULL; +} + + +int main() +{ + pthread_t worker[10]; + for (int i=0; i < 10; ++i) { + if ( pthread_create(&worker[i], NULL, work, NULL) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + } + + void* result; + for (int i=0; i < 10; ++i) { + pthread_join(worker[i], &result); + } + + PASS("thread-lazy-bind"); + return 0; +} diff --git a/dyld/unit-tests/test-cases/tlv-basic/Makefile b/dyld/unit-tests/test-cases/tlv-basic/Makefile new file mode 100644 index 0000000..cf25175 --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-basic/Makefile @@ -0,0 +1,44 @@ +## +# 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 + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c -o main + + +clean: + ${RM} ${RMFLAGS} main + diff --git a/dyld/unit-tests/test-cases/tlv-basic/main.c b/dyld/unit-tests/test-cases/tlv-basic/main.c new file mode 100644 index 0000000..1bbdc61 --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-basic/main.c @@ -0,0 +1,122 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +__attribute__((weak)) +void foobar() { +} + +__thread int a; +__thread int b = 5; +__thread static int c; +__thread static int d = 5; + + +static void* work(void* arg) +{ + uintptr_t offset = (uintptr_t)arg; + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + void* result = malloc(10); + if ( a != 0 ) { + FAIL("tlv-basic: a non-zero on slow-path"); + exit(0); + } + if ( b != 5 ) { + FAIL("tlv-basic: b not five"); + exit(0); + } + + if ( c != 0 ) { + FAIL("tlv-basic: c non-zero"); + exit(0); + } + + if ( d != 5 ) { + FAIL("tlv-basic: gd not five"); + exit(0); + } + + for(int i=0; i < 10000; ++i) { + a = 3 + offset + i; + b = 7 + offset + i; + c = 11 + offset + i; + d = 13 + offset + i; + foobar(); + if ( a != 3 + offset + i ) { + FAIL("tlv-basic: a not three"); + exit(0); + } + if ( b != 7 + offset + i ) { + FAIL("tlv-basic: b not seven"); + exit(0); + } + if ( c != 11 + offset + i ) { + FAIL("tlv-basic: c not eleven"); + exit(0); + } + if ( d != 13 + offset + i ) { + FAIL("tlv-basic: d not thirteen"); + exit(0); + } + } + + return result; +} + +int main() +{ + pthread_t worker1; + if ( pthread_create(&worker1, NULL, work, (void*)1) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker2; + if ( pthread_create(&worker2, NULL, work, (void*)10) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + pthread_t worker3; + if ( pthread_create(&worker3, NULL, work, (void*)100) != 0 ) { + FAIL("pthread_create failed"); + exit(0); + } + + void* result; + //fprintf(stderr, "waiting for worker 1\n"); + pthread_join(worker1, &result); + //fprintf(stderr, "waiting for worker 2\n"); + pthread_join(worker2, &result); + //fprintf(stderr, "waiting for worker 3\n"); + pthread_join(worker3, &result); + + work(NULL); + + PASS("tlv-basic"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/tlv-dylib/Makefile b/dyld/unit-tests/test-cases/tlv-dylib/Makefile new file mode 100644 index 0000000..9011041 --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-dylib/Makefile @@ -0,0 +1,43 @@ +## +# 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: + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include foo.c -dynamiclib -o libfoo.dylib + ${CC} -arch ${ARCH} ${CCFLAGS} -I${TESTROOT}/include main.c libfoo.dylib -o main + + +clean: + ${RM} ${RMFLAGS} libfoo.dylib main + diff --git a/dyld/unit-tests/test-cases/tlv-dylib/foo.c b/dyld/unit-tests/test-cases/tlv-dylib/foo.c new file mode 100644 index 0000000..4a47edb --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-dylib/foo.c @@ -0,0 +1,8 @@ + + + +__thread int a; +__thread int b = 5; + + +int getB() { return b; } diff --git a/dyld/unit-tests/test-cases/tlv-dylib/main.c b/dyld/unit-tests/test-cases/tlv-dylib/main.c new file mode 100644 index 0000000..117a646 --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-dylib/main.c @@ -0,0 +1,82 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/tlv-initializer/Makefile b/dyld/unit-tests/test-cases/tlv-initializer/Makefile new file mode 100644 index 0000000..6a6d74c --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-initializer/Makefile @@ -0,0 +1,50 @@ +## +# 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +check: check_$(OS_LION_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 + diff --git a/dyld/unit-tests/test-cases/tlv-initializer/get.s b/dyld/unit-tests/test-cases/tlv-initializer/get.s new file mode 100644 index 0000000..919667a --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-initializer/get.s @@ -0,0 +1,18 @@ + +#if __x86_64__ + + # _myinit sets up TLV content + .thread_init_func + .quad _myinit + +#endif + +#if __i386__ + + # _myinit sets up TLV content + .thread_init_func + .long _myinit + +#endif + +.subsections_via_symbols diff --git a/dyld/unit-tests/test-cases/tlv-initializer/main.c b/dyld/unit-tests/test-cases/tlv-initializer/main.c new file mode 100644 index 0000000..3f89123 --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-initializer/main.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010-2011 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +__thread int a = 0; // initially 0 +__thread int b = 5; // initially 5 + +// simulate C++ initializer on thread local variables +void myinit() +{ + a = 11; + b = 42; +} + +static void* work(void* arg) +{ + //fprintf(stderr, "self=%p, &a=%p\n", pthread_self(), get_a()); + if ( a != 11 ) { + FAIL("tlv-initializer: get_a() not initialized to 11"); + exit(0); + } + if ( 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; +} diff --git a/dyld/unit-tests/test-cases/tlv-terminators/Makefile b/dyld/unit-tests/test-cases/tlv-terminators/Makefile new file mode 100644 index 0000000..9094732 --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-terminators/Makefile @@ -0,0 +1,50 @@ +## +# 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +check: check_$(OS_LION_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 + diff --git a/dyld/unit-tests/test-cases/tlv-terminators/init.s b/dyld/unit-tests/test-cases/tlv-terminators/init.s new file mode 100644 index 0000000..9bb901c --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-terminators/init.s @@ -0,0 +1,10 @@ + + # _myinit sets up TLV content + .thread_init_func + #if __LP64__ + .quad _myinit + #else + .long _myinit + #endif + +.subsections_via_symbols diff --git a/dyld/unit-tests/test-cases/tlv-terminators/main.c b/dyld/unit-tests/test-cases/tlv-terminators/main.c new file mode 100644 index 0000000..94d1d2a --- /dev/null +++ b/dyld/unit-tests/test-cases/tlv-terminators/main.c @@ -0,0 +1,154 @@ +/* + * 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 // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include + +#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; +} diff --git a/dyld/unit-tests/test-cases/trie-symbol-overrun/Makefile b/dyld/unit-tests/test-cases/trie-symbol-overrun/Makefile new file mode 100644 index 0000000..41fe1c1 --- /dev/null +++ b/dyld/unit-tests/test-cases/trie-symbol-overrun/Makefile @@ -0,0 +1,43 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main : main.c foo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c foo.dylib -Wno-deprecated-declarations + + +foo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o foo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main foo.dylib + diff --git a/dyld/unit-tests/test-cases/trie-symbol-overrun/foo.c b/dyld/unit-tests/test-cases/trie-symbol-overrun/foo.c new file mode 100644 index 0000000..62d5f38 --- /dev/null +++ b/dyld/unit-tests/test-cases/trie-symbol-overrun/foo.c @@ -0,0 +1,12 @@ + +void abc() +{ +} + +void abcdefghi() +{ +} + +void abcdee() +{ +} diff --git a/dyld/unit-tests/test-cases/trie-symbol-overrun/main.c b/dyld/unit-tests/test-cases/trie-symbol-overrun/main.c new file mode 100644 index 0000000..e3d07f1 --- /dev/null +++ b/dyld/unit-tests/test-cases/trie-symbol-overrun/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include +#include +#include +#include +#include + + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + + +int main() +{ + // allocate two pages + vm_address_t addr = 0; + kern_return_t r = vm_allocate(mach_task_self(), &addr, 8192, VM_FLAGS_ANYWHERE); + if ( r != KERN_SUCCESS ) { + FAIL("vm_allocate returned %d", r); + exit(0); + } + // mark the second page unreadable + mprotect((char*)(addr+4096), 4096, 0); + // copy a string to the end of the first page + char* sym = (char*)(addr+4096-5); + strcpy(sym, "_abd"); + + // call a dyld API that uses the string + // if dyld reads past the end of the string, it will crash + // trie parser can read past end of input symbol name +#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; +} diff --git a/dyld/unit-tests/test-cases/unloadable-library-residue/Makefile b/dyld/unit-tests/test-cases/unloadable-library-residue/Makefile new file mode 100644 index 0000000..42459e6 --- /dev/null +++ b/dyld/unit-tests/test-cases/unloadable-library-residue/Makefile @@ -0,0 +1,55 @@ +## +# 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 + +### +### rdar://problem/3884004 Libraries can be half-baked if an error occurs during their first use +### +### We indirectly load libfoo which depends on libbar. The libbar that is loaded does not contain +### what libfoo needs, so libfoo fails to bind. The bug was that libfoo was left marked as bound, +### so the next use of libfoo seemed to succeed, when it should have failed. +### + +all-check: all check + +check: + export DYLD_IMAGE_SUFFIX="_missing" && ./main + +all: main libfoo.dylib + +main : main.c + ${CC} ${CCFLAGS} -Wno-deprecated-declarations -I${TESTROOT}/include -o main main.c + +libfoo.dylib : foo.c libbar_missing.dylib libbar.dylib + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo.dylib libbar.dylib + +libbar_missing.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar_missing.dylib -install_name libbar.dylib + +libbar.dylib : bar.c + ${CC} ${CCFLAGS} bar.c -dynamiclib -o libbar.dylib -DHAS_BAR2=1 + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbar_missing.dylib + diff --git a/dyld/unit-tests/test-cases/unloadable-library-residue/bar.c b/dyld/unit-tests/test-cases/unloadable-library-residue/bar.c new file mode 100644 index 0000000..400e1bf --- /dev/null +++ b/dyld/unit-tests/test-cases/unloadable-library-residue/bar.c @@ -0,0 +1,30 @@ +/* + * 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 bar1 = 1; +#if HAS_BAR2 +int bar2 = 2; +#endif +int bar3 = 3; + diff --git a/dyld/unit-tests/test-cases/unloadable-library-residue/foo.c b/dyld/unit-tests/test-cases/unloadable-library-residue/foo.c new file mode 100644 index 0000000..8abc708 --- /dev/null +++ b/dyld/unit-tests/test-cases/unloadable-library-residue/foo.c @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +extern int bar1; +extern int bar2; +extern int bar3; + + + +int foo() +{ + return bar1 + bar2 + bar3; +} diff --git a/dyld/unit-tests/test-cases/unloadable-library-residue/main.c b/dyld/unit-tests/test-cases/unloadable-library-residue/main.c new file mode 100644 index 0000000..d1bab84 --- /dev/null +++ b/dyld/unit-tests/test-cases/unloadable-library-residue/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005-2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include +#include +#include + +#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); + //fprintf(stderr, "%s\n", name); + if (strcmp(name, "libfoo.dylib") == 0 ) { + FAIL("library-cant-be-bound: libfoo.dylib shows up in list of images"); + return 0; + } + } + + + PASS("library-cant-be-bound"); + return 0; +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/Makefile b/dyld/unit-tests/test-cases/upward-dylib-init-order/Makefile new file mode 100644 index 0000000..bc48b5c --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/Makefile @@ -0,0 +1,54 @@ +## +# 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_FEATURES) + +check_: + ${PASS_IFF} true + +all_: + + +check_1: + ./main + +all_1: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib common.c -o libcommon.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib u2.c libcommon.dylib -o libu2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib u.c libcommon.dylib -o libu.dylib -Wl,-upward_library,libu2.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib c.c libcommon.dylib -o libc.dylib -Wl,-upward_library,libu.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib b.c libcommon.dylib -o libb.dylib libc.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include main.c libb.dylib libcommon.dylib -o main + + +clean: + ${RM} ${RMFLAGS} *~ main libu.dylib libu2.dylib libb.dylib libc.dylib libcommon.dylib + + + diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/b.c b/dyld/unit-tests/test-cases/upward-dylib-init-order/b.c new file mode 100644 index 0000000..9a146f4 --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/b.c @@ -0,0 +1,10 @@ +#include +#include "common.h" + + + +static __attribute__((constructor)) void myInit() +{ + setB(); + //fprintf(stderr, "init b\n"); +} diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/c.c b/dyld/unit-tests/test-cases/upward-dylib-init-order/c.c new file mode 100644 index 0000000..0d79e7c --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/c.c @@ -0,0 +1,8 @@ +#include + + + +static __attribute__((constructor)) void myInit() +{ + //fprintf(stderr, "init c\n"); +} diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/common.c b/dyld/unit-tests/test-cases/upward-dylib-init-order/common.c new file mode 100644 index 0000000..663e4a1 --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/common.c @@ -0,0 +1,37 @@ +#include "common.h" +#include + +static bool b = false; +static bool u = false; +static bool u2 = false; +static bool isOk = true; + +void setB() +{ + if ( u || b || u2 ) + isOk = false; + b = true; +} + +void setU() +{ + if ( u || u2 ) + isOk = false; + u = true; +} + +void setU2() +{ + if ( u2 ) + isOk = false; + u2 = true; +} + +// return true iff +// setB() was called, then setU() +bool ok() +{ + //fprintf(stderr, "isOk=%d, u=%d, b=%d\n", isOk, u, b); + return isOk && u && b && u2; +} + diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/common.h b/dyld/unit-tests/test-cases/upward-dylib-init-order/common.h new file mode 100644 index 0000000..05427dc --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/common.h @@ -0,0 +1,8 @@ +#include + +extern void setB(); +extern void setU(); +extern void setU2(); + +extern bool ok(); + diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/main.c b/dyld/unit-tests/test-cases/upward-dylib-init-order/main.c new file mode 100644 index 0000000..e7edc55 --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/main.c @@ -0,0 +1,19 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "common.h" + +int main() +{ + if ( ok() ) + PASS("upward-dylib-init-order"); + else + FAIL("upward-dylib-init-order"); + + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/u.c b/dyld/unit-tests/test-cases/upward-dylib-init-order/u.c new file mode 100644 index 0000000..81c4043 --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/u.c @@ -0,0 +1,10 @@ +#include +#include "common.h" + + +static __attribute__((constructor)) void myInit() +{ + setU(); + //fprintf(stderr, "init u\n"); +} + diff --git a/dyld/unit-tests/test-cases/upward-dylib-init-order/u2.c b/dyld/unit-tests/test-cases/upward-dylib-init-order/u2.c new file mode 100644 index 0000000..7add33f --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib-init-order/u2.c @@ -0,0 +1,10 @@ +#include +#include "common.h" + + +static __attribute__((constructor)) void myInit() +{ + setU2(); + //fprintf(stderr, "init u2\n"); +} + diff --git a/dyld/unit-tests/test-cases/upward-dylib/Makefile b/dyld/unit-tests/test-cases/upward-dylib/Makefile new file mode 100644 index 0000000..b33a842 --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib/Makefile @@ -0,0 +1,52 @@ +## +# 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_LION_FEATURES) check_$(OS_LION_FEATURES) + +all: all_$(OS_LION_FEATURES) + +check: check_$(OS_LION_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 libup.dylib libdown.dylib -o main1 + ${CC} ${CCFLAGS} -I${TESTROOT}/include main2.c libdown.dylib -o main2 + + +clean: + ${RM} ${RMFLAGS} *~ main1 main2 libup.dylib libdown.dylib libup.stub + diff --git a/dyld/unit-tests/test-cases/upward-dylib/down.c b/dyld/unit-tests/test-cases/upward-dylib/down.c new file mode 100644 index 0000000..f576ad6 --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib/down.c @@ -0,0 +1,22 @@ +#include +#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; +} + +int getdownsup() +{ + return whatsup(); +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/upward-dylib/down.h b/dyld/unit-tests/test-cases/upward-dylib/down.h new file mode 100644 index 0000000..e4eb56c --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib/down.h @@ -0,0 +1,3 @@ +extern int getdown(); +extern int getdownsup(); + diff --git a/dyld/unit-tests/test-cases/upward-dylib/main.c b/dyld/unit-tests/test-cases/upward-dylib/main.c new file mode 100644 index 0000000..302478a --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib/main.c @@ -0,0 +1,20 @@ +#include // fprintf(), NULL +#include // 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; +} + + diff --git a/dyld/unit-tests/test-cases/upward-dylib/main2.c b/dyld/unit-tests/test-cases/upward-dylib/main2.c new file mode 100644 index 0000000..25ee4eb --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib/main2.c @@ -0,0 +1,20 @@ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "up.h" +#include "down.h" + +int main() +{ + if ( getdownsup() ) + PASS("upward-dylib"); + else + FAIL("upward-dylib"); + + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/upward-dylib/up.c b/dyld/unit-tests/test-cases/upward-dylib/up.c new file mode 100644 index 0000000..2451d74 --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib/up.c @@ -0,0 +1,21 @@ +#include +#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; +} + diff --git a/dyld/unit-tests/test-cases/upward-dylib/up.h b/dyld/unit-tests/test-cases/upward-dylib/up.h new file mode 100644 index 0000000..9ed804c --- /dev/null +++ b/dyld/unit-tests/test-cases/upward-dylib/up.h @@ -0,0 +1,2 @@ + +extern int whatsup(); diff --git a/dyld/unit-tests/test-cases/weak-coalesce-c++/Makefile b/dyld/unit-tests/test-cases/weak-coalesce-c++/Makefile new file mode 100644 index 0000000..253ea75 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-c++/Makefile @@ -0,0 +1,54 @@ +## +# 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: + ./dynamic-test + +all: dynamic-test static-test + +static-test: a1.o a2.o main.o + $(CXX) -I${TESTROOT}/include a1.o a2.o main.o -o static-test + +dynamic-test: main.o a1.o liba2.dylib + $(CXX) -I${TESTROOT}/include main.o a1.o -L. -la2 -o dynamic-test + +liba2.dylib: a2.o + $(CXX) -I${TESTROOT}/include a2.o -dynamiclib -o liba2.dylib + +a1.o: a1.cc a.h + $(CXX) -I${TESTROOT}/include -c a1.cc + +a2.o: a2.cc a.h + $(CXX) -I${TESTROOT}/include -c a2.cc + +main.o: main.cc + $(CXX) -I${TESTROOT}/include -c main.cc + + +clean: + ${RM} ${RMFLAGS} *.o *.dylib static-test dynamic-test diff --git a/dyld/unit-tests/test-cases/weak-coalesce-c++/a.h b/dyld/unit-tests/test-cases/weak-coalesce-c++/a.h new file mode 100644 index 0000000..574026d --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-c++/a.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +template +struct wrapper { + static X x; +}; + +template X wrapper::x; diff --git a/dyld/unit-tests/test-cases/weak-coalesce-c++/a1.cc b/dyld/unit-tests/test-cases/weak-coalesce-c++/a1.cc new file mode 100644 index 0000000..0395912 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-c++/a1.cc @@ -0,0 +1,4 @@ +#include "a.h" + +int get_x_1 (void) { return wrapper::x; } +void set_x_1 (int x_) { wrapper::x = x_; } diff --git a/dyld/unit-tests/test-cases/weak-coalesce-c++/a2.cc b/dyld/unit-tests/test-cases/weak-coalesce-c++/a2.cc new file mode 100644 index 0000000..b3a3fed --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-c++/a2.cc @@ -0,0 +1,4 @@ +#include "a.h" + +int get_x_2 (void) { return wrapper::x; } +void set_x_2 (int x_) { wrapper::x = x_; } diff --git a/dyld/unit-tests/test-cases/weak-coalesce-c++/main.cc b/dyld/unit-tests/test-cases/weak-coalesce-c++/main.cc new file mode 100644 index 0000000..3915e94 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-c++/main.cc @@ -0,0 +1,20 @@ +#include +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +extern void set_x_1(int); +extern void set_x_2(int); +extern int get_x_1(void); +extern int get_x_2(void); + +int main() +{ + set_x_1 (17); + set_x_2 (76); + + if (get_x_1() == 76 && get_x_2() == 76) + PASS("weak-coalesce=c++"); + else + FAIL("weak-coalesce=c++"); +} diff --git a/dyld/unit-tests/test-cases/weak-coalesce-inserted/Makefile b/dyld/unit-tests/test-cases/weak-coalesce-inserted/Makefile new file mode 100644 index 0000000..2c3f872 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-inserted/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2013 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + DYLD_INSERT_LIBRARIES=libfoo1.dylib:libfoo2.dylib ./main + +all: main + +main: + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbase.dylib base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo1.dylib foo1.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo2.dylib foo2.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libbase.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libbase.dylib + diff --git a/dyld/unit-tests/test-cases/weak-coalesce-inserted/base.c b/dyld/unit-tests/test-cases/weak-coalesce-inserted/base.c new file mode 100644 index 0000000..a2d200b --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-inserted/base.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool wasProblem = false; + +static const char* coal1Where = NULL; +static int* coal1Addr = NULL; +static int checkInCountCoal1 = 0; + +void baseVerifyCoal1(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr); + ++checkInCountCoal1; + if ( coal1Where == NULL ) { + coal1Where = where; + coal1Addr = addr; + } + else { + if ( addr != coal1Addr ) { + fprintf(stderr, "coal1 resolved to different locations. %p %s and %p %s\n", + coal1Addr, coal1Where, addr, where); + wasProblem = true; + } + } +} + + +static const char* coal2Where = NULL; +static int* coal2Addr = NULL; +static int checkInCountCoal2 = 0; + +void baseVerifyCoal2(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr); + ++checkInCountCoal2; + if ( coal2Where == NULL ) { + coal2Where = where; + coal2Addr = addr; + } + else { + if ( addr != coal2Addr ) { + fprintf(stderr, "coal2 resolved to different locations. %p %s and %p %s\n", + coal2Addr, coal2Where, addr, where); + wasProblem = true; + } + } +} + + + +void baseCheck() +{ + if ( wasProblem || (checkInCountCoal1 != 3) || (checkInCountCoal2 != 2) ) + FAIL("weak-coal"); + else + PASS("weak-coal"); +} + diff --git a/dyld/unit-tests/test-cases/weak-coalesce-inserted/base.h b/dyld/unit-tests/test-cases/weak-coalesce-inserted/base.h new file mode 100644 index 0000000..482b832 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-inserted/base.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +extern void baseCheck(); + +extern int coal1; +extern int coal2; + +extern void baseVerifyCoal1(const char* where, int* addr); +extern void baseVerifyCoal2(const char* where, int* addr); diff --git a/dyld/unit-tests/test-cases/weak-coalesce-inserted/foo1.c b/dyld/unit-tests/test-cases/weak-coalesce-inserted/foo1.c new file mode 100644 index 0000000..0707e0d --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-inserted/foo1.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + + +int __attribute__((weak)) coal1 = 1; +int __attribute__((weak)) coal2 = 1; + + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo1", &coal1); + baseVerifyCoal2("in foo1", &coal2); +} + + diff --git a/dyld/unit-tests/test-cases/weak-coalesce-inserted/foo2.c b/dyld/unit-tests/test-cases/weak-coalesce-inserted/foo2.c new file mode 100644 index 0000000..13e8b07 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-inserted/foo2.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + +int __attribute__((weak)) coal1 = 2; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo2", &coal1); + baseVerifyCoal2("in foo2", &coal2); +} diff --git a/dyld/unit-tests/test-cases/weak-coalesce-inserted/main.c b/dyld/unit-tests/test-cases/weak-coalesce-inserted/main.c new file mode 100644 index 0000000..bf38e29 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-inserted/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int __attribute__((weak)) coal1 = 3; + + +int main() +{ + baseVerifyCoal1("in main", &coal1); + + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/weak-coalesce-stubs/Makefile b/dyld/unit-tests/test-cases/weak-coalesce-stubs/Makefile new file mode 100644 index 0000000..d55a5c5 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-stubs/Makefile @@ -0,0 +1,18 @@ + +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 diff --git a/dyld/unit-tests/test-cases/weak-coalesce-stubs/bar.c b/dyld/unit-tests/test-cases/weak-coalesce-stubs/bar.c new file mode 100644 index 0000000..344a82c --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-stubs/bar.c @@ -0,0 +1,6 @@ +void bar() {} + +void foo() __attribute__((weak)); +void foo() +{ +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/weak-coalesce-stubs/foo.c b/dyld/unit-tests/test-cases/weak-coalesce-stubs/foo.c new file mode 100644 index 0000000..348cf65 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-stubs/foo.c @@ -0,0 +1,61 @@ + + +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() {} diff --git a/dyld/unit-tests/test-cases/weak-coalesce-stubs/main.c b/dyld/unit-tests/test-cases/weak-coalesce-stubs/main.c new file mode 100644 index 0000000..11a4c89 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce-stubs/main.c @@ -0,0 +1,22 @@ +#include +#include +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// 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; +} diff --git a/dyld/unit-tests/test-cases/weak-coalesce/Makefile b/dyld/unit-tests/test-cases/weak-coalesce/Makefile new file mode 100644 index 0000000..543a5be --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce/Makefile @@ -0,0 +1,52 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo1.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo1.dylib libbase.dylib + +libfoo1.dylib: foo1.c libfoo2.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo1.dylib foo1.c libfoo2.dylib libbase.dylib + +libfoo2.dylib: foo2.c libfoo3.dylib libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo2.dylib foo2.c libfoo3.dylib libbase.dylib + +libfoo3.dylib: foo3.c libbase.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo3.dylib foo3.c libbase.dylib -prebind -seg1addr 20000 + +libbase.dylib: base.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libbase.dylib base.c -prebind -seg1addr 10000 + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo1.dylib libfoo2.dylib libfoo3.dylib libbase.dylib + diff --git a/dyld/unit-tests/test-cases/weak-coalesce/base.c b/dyld/unit-tests/test-cases/weak-coalesce/base.c new file mode 100644 index 0000000..0deff52 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce/base.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "base.h" +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +static bool wasProblem = false; + +static const char* coal1Where = NULL; +static int* coal1Addr = NULL; +static int checkInCountCoal1 = 0; + +void baseVerifyCoal1(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal1(%s, %p)\n", where, addr); + ++checkInCountCoal1; + if ( coal1Where == NULL ) { + coal1Where = where; + coal1Addr = addr; + } + else { + if ( addr != coal1Addr ) { + fprintf(stderr, "coal1 resolved to different locations. %p in %s and %p in %s\n", + coal1Addr, coal1Where, addr, where); + wasProblem = true; + } + } +} + + +static const char* coal2Where = NULL; +static int* coal2Addr = NULL; +static int checkInCountCoal2 = 0; + +void baseVerifyCoal2(const char* where, int* addr) +{ + //fprintf(stderr, "baseVerifyCoal2(%s, %p)\n", where, addr); + ++checkInCountCoal2; + if ( coal2Where == NULL ) { + coal2Where = where; + coal2Addr = addr; + } + else { + if ( addr != coal2Addr ) { + fprintf(stderr, "coal2 resolved to different locations. %p in %s and %p in %s\n", + coal2Addr, coal2Where, addr, where); + wasProblem = true; + } + } +} + + + +void baseCheck() +{ + if ( wasProblem || (checkInCountCoal1 != 4) || (checkInCountCoal2 != 4) ) + FAIL("weak-coal"); + else + PASS("weak-coal"); +} + diff --git a/dyld/unit-tests/test-cases/weak-coalesce/base.h b/dyld/unit-tests/test-cases/weak-coalesce/base.h new file mode 100644 index 0000000..482b832 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce/base.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +extern void baseCheck(); + +extern int coal1; +extern int coal2; + +extern void baseVerifyCoal1(const char* where, int* addr); +extern void baseVerifyCoal2(const char* where, int* addr); diff --git a/dyld/unit-tests/test-cases/weak-coalesce/foo1.c b/dyld/unit-tests/test-cases/weak-coalesce/foo1.c new file mode 100644 index 0000000..0707e0d --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce/foo1.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + + +int __attribute__((weak)) coal1 = 1; +int __attribute__((weak)) coal2 = 1; + + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo1", &coal1); + baseVerifyCoal2("in foo1", &coal2); +} + + diff --git a/dyld/unit-tests/test-cases/weak-coalesce/foo2.c b/dyld/unit-tests/test-cases/weak-coalesce/foo2.c new file mode 100644 index 0000000..a5770d4 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce/foo2.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + + +int coal1 = 2; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo2", &coal1); + baseVerifyCoal2("in foo2", &coal2); +} diff --git a/dyld/unit-tests/test-cases/weak-coalesce/foo3.c b/dyld/unit-tests/test-cases/weak-coalesce/foo3.c new file mode 100644 index 0000000..c92a444 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce/foo3.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include "base.h" + +int __attribute__((weak)) coal1 = 3; +int __attribute__((weak)) coal2 = 2; + +static __attribute__((constructor)) void myinit() +{ + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in foo3", &coal1); + baseVerifyCoal2("in foo3", &coal2); +} + diff --git a/dyld/unit-tests/test-cases/weak-coalesce/main.c b/dyld/unit-tests/test-cases/weak-coalesce/main.c new file mode 100644 index 0000000..6f4f9e1 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-coalesce/main.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +#include "base.h" + +int main() +{ + + //fprintf(stderr, "myinit() in foo1.c\n"); + baseVerifyCoal1("in main", &coal1); + baseVerifyCoal2("in main", &coal2); + + baseCheck(); + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc-flat/Makefile b/dyld/unit-tests/test-cases/weak-external-reloc-flat/Makefile new file mode 100644 index 0000000..b1e95b2 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc-flat/Makefile @@ -0,0 +1,34 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +# +# flat_namespace and weak binding conflict +# +# Note that libfoo.dylib is built flat-namespace +# + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib -flat_namespace + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc-flat/bar.c b/dyld/unit-tests/test-cases/weak-external-reloc-flat/bar.c new file mode 100644 index 0000000..4787d5b --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc-flat/bar.c @@ -0,0 +1,8 @@ + + +int bar[] = { 20, 21, 22, 23 }; + + +int __attribute__((weak)) frob[] = { 30, 31, 32, 33 }; + +int bar_getfrob(int x) { return frob[x]; } diff --git a/dyld/unit-tests/test-cases/weak-external-reloc-flat/baz.c b/dyld/unit-tests/test-cases/weak-external-reloc-flat/baz.c new file mode 100644 index 0000000..f35d05c --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc-flat/baz.c @@ -0,0 +1,8 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc-flat/foo.c b/dyld/unit-tests/test-cases/weak-external-reloc-flat/foo.c new file mode 100644 index 0000000..d2d73ef --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc-flat/foo.c @@ -0,0 +1,14 @@ + + +extern int bar_getfrob(int x); + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; +int __attribute__((weak)) frob[] = { 20, 21, 22, 23 }; + +int* pfoo = &foo[2]; + +int getfrob() { + return bar_getfrob(2); +} \ No newline at end of file diff --git a/dyld/unit-tests/test-cases/weak-external-reloc-flat/main.c b/dyld/unit-tests/test-cases/weak-external-reloc-flat/main.c new file mode 100644 index 0000000..e74b44d --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc-flat/main.c @@ -0,0 +1,41 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// pfoo is in libfoo.dylib +// libfoo.dylib also has weak foo[] +// pfoo should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* pfoo; + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; +int* pbar = &bar[1]; + + +// libfoo.dylib has a weak frob[] +// libbar.dylib has a weak frob[] and a funtion that refrences frob[] +// the function should use libfoo's frob[] even if libfoo is flat +extern int getfrob(); + +int main() +{ + if ( *pfoo != 7 ) + FAIL("weak-external-reloc-flat, pfoo=%d", *pfoo); + else if ( *pbar != 21 ) + FAIL("weak-external-reloc-flat, pbar=%d", *pbar); + else if ( getfrob() != 22 ) + FAIL("weak-external-reloc-flat, frob()=%d", getfrob()); + else + PASS("weak-external-reloc-flat"); + + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc/Makefile b/dyld/unit-tests/test-cases/weak-external-reloc/Makefile new file mode 100644 index 0000000..b01a66b --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc/Makefile @@ -0,0 +1,30 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c librealmain.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c librealmain.dylib + +librealmain.dylib: realmain.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib librealmain.dylib + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc/bar.c b/dyld/unit-tests/test-cases/weak-external-reloc/bar.c new file mode 100644 index 0000000..615196e --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc/bar.c @@ -0,0 +1,6 @@ + + +int bar[] = { 20, 21, 22, 23 }; + +// needs something weak to join coalescing +int __attribute__((weak)) junk = 5; diff --git a/dyld/unit-tests/test-cases/weak-external-reloc/baz.c b/dyld/unit-tests/test-cases/weak-external-reloc/baz.c new file mode 100644 index 0000000..f35d05c --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc/baz.c @@ -0,0 +1,8 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc/foo.c b/dyld/unit-tests/test-cases/weak-external-reloc/foo.c new file mode 100644 index 0000000..9e4873a --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc/foo.c @@ -0,0 +1,10 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; + +int* pfoo = &foo[2]; + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc/main.c b/dyld/unit-tests/test-cases/weak-external-reloc/main.c new file mode 100644 index 0000000..a25c448 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc/main.c @@ -0,0 +1,10 @@ + +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/dyld/unit-tests/test-cases/weak-external-reloc/realmain.c b/dyld/unit-tests/test-cases/weak-external-reloc/realmain.c new file mode 100644 index 0000000..6870f5a --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-external-reloc/realmain.c @@ -0,0 +1,39 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// pfoo is in libfoo.dylib +// libfoo.dylib also has weak foo[] +// pfoo should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* pfoo; + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; +int* pbar = &bar[1]; + +// there is only one stuff, but it is weak +// so we are testing the case when stuff is not overridden +int __attribute__((weak)) stuff[] = { 1, 2, 3, 4, 5 }; +int* pstuff = &stuff[3]; + + +void realmain() +{ + if ( *pfoo != 7 ) + FAIL("weak-external-reloc, pfoo=%d", *pfoo); + else if ( *pbar != 21 ) + FAIL("weak-external-reloc, pbar=%d", *pbar); + else if ( *pstuff != 4 ) + FAIL("weak-external-reloc, pstuffr=%d", *pstuff); + else + PASS("weak-external-reloc"); +} + + diff --git a/dyld/unit-tests/test-cases/weak-in-dylib/Makefile b/dyld/unit-tests/test-cases/weak-in-dylib/Makefile new file mode 100644 index 0000000..cd54661 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-in-dylib/Makefile @@ -0,0 +1,21 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/weak-in-dylib/foo.c b/dyld/unit-tests/test-cases/weak-in-dylib/foo.c new file mode 100644 index 0000000..ec6f836 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-in-dylib/foo.c @@ -0,0 +1,7 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; + diff --git a/dyld/unit-tests/test-cases/weak-in-dylib/main.c b/dyld/unit-tests/test-cases/weak-in-dylib/main.c new file mode 100644 index 0000000..358e0f7 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-in-dylib/main.c @@ -0,0 +1,22 @@ + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// libfoo.dylib has a weak foo[] +extern int foo[]; + +int* pfoo3 = &foo[3]; + +int main() +{ + if ( *pfoo3 != 4 ) + FAIL("weak-in-dylib, pfoo3=%d", *pfoo3); + else if ( foo[2] != 3 ) + FAIL("weak-in-dylib, foo[2]=%d", foo[2]); + else + PASS("weak-in-dyliby"); + return 0; +} + diff --git a/dyld/unit-tests/test-cases/weak-lazy-slidable/Makefile b/dyld/unit-tests/test-cases/weak-lazy-slidable/Makefile new file mode 100644 index 0000000..274962b --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-lazy-slidable/Makefile @@ -0,0 +1,31 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c libfoo.dylib libother.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libother.dylib libfoo.dylib + +libother.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libother.dylib other.c + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbar3.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbar3.dylib + +libbar3.dylib: bar3.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar3.dylib bar3.c + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbar3.dylib libother.dylib + diff --git a/dyld/unit-tests/test-cases/weak-lazy-slidable/bar.c b/dyld/unit-tests/test-cases/weak-lazy-slidable/bar.c new file mode 100644 index 0000000..e52bf11 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-lazy-slidable/bar.c @@ -0,0 +1,12 @@ + + +#include + +bool bar1() +{ + return true; +} + +__attribute__((weak)) void junk() +{ +} diff --git a/dyld/unit-tests/test-cases/weak-lazy-slidable/bar3.c b/dyld/unit-tests/test-cases/weak-lazy-slidable/bar3.c new file mode 100644 index 0000000..0f27868 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-lazy-slidable/bar3.c @@ -0,0 +1,9 @@ + + +#include + +__attribute__((weak)) bool bar1() +{ + return false; +} + diff --git a/dyld/unit-tests/test-cases/weak-lazy-slidable/foo.c b/dyld/unit-tests/test-cases/weak-lazy-slidable/foo.c new file mode 100644 index 0000000..7e3c8a5 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-lazy-slidable/foo.c @@ -0,0 +1,29 @@ +#include + + + +extern bool bar1(); + + +__attribute__((weak)) bool bar1() +{ + return false; +} + +__attribute__((weak)) bool bar2() +{ + return true; +} + + +bool foo1() +{ + return bar1(); +} + +bool foo2() +{ + return bar2(); +} + + diff --git a/dyld/unit-tests/test-cases/weak-lazy-slidable/main.c b/dyld/unit-tests/test-cases/weak-lazy-slidable/main.c new file mode 100644 index 0000000..a91ef85 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-lazy-slidable/main.c @@ -0,0 +1,24 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + + +extern bool foo1(); +extern bool foo2(); + + +int main() +{ + if ( foo1() && foo2() ) + PASS("weak-lazy-slidable"); + else + FAIL("weak-lazy-slidable"); + + return EXIT_SUCCESS; +} + + diff --git a/dyld/unit-tests/test-cases/weak-lazy-slidable/other.c b/dyld/unit-tests/test-cases/weak-lazy-slidable/other.c new file mode 100644 index 0000000..0c04195 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-lazy-slidable/other.c @@ -0,0 +1,7 @@ +#include + +bool bar2() +{ + return false; +} + diff --git a/dyld/unit-tests/test-cases/weak-library/Makefile b/dyld/unit-tests/test-cases/weak-library/Makefile new file mode 100644 index 0000000..1d1863e --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-library/Makefile @@ -0,0 +1,41 @@ +## +# Copyright (c) 2005-2008 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + cd absent/ && ../${TESTROOT}/bin/exit-zero-pass.pl "weak-library" "weak-library" ./main + cd present/ && ../${TESTROOT}/bin/exit-zero-pass.pl "weak-library" "weak-library" ./main + +all: foo.c main.c + ${CC} -dynamiclib -prebind -seg1addr 400000 -o libfoo.dylib foo.c + mkdir -p absent/ + ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o absent/main main.c + mkdir -p present/ + ${CC} -prebind -I${TESTROOT}/include -L. -Wl,-weak-lfoo -o present/main main.c + mv libfoo.dylib present/ + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib absent present diff --git a/dyld/unit-tests/test-cases/weak-library/foo.c b/dyld/unit-tests/test-cases/weak-library/foo.c new file mode 100644 index 0000000..55808e4 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-library/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include "foo.h" + +int foo() +{ + return 10; +} diff --git a/dyld/unit-tests/test-cases/weak-library/foo.h b/dyld/unit-tests/test-cases/weak-library/foo.h new file mode 100644 index 0000000..78d1afd --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-library/foo.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +extern int foo(); diff --git a/dyld/unit-tests/test-cases/weak-library/main.c b/dyld/unit-tests/test-cases/weak-library/main.c new file mode 100644 index 0000000..b16a8e4 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-library/main.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include + +#include "test.h" + +int +main(int argc, char **argv) +{ + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/weak-non-lazy/Makefile b/dyld/unit-tests/test-cases/weak-non-lazy/Makefile new file mode 100644 index 0000000..c3d1b70 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-non-lazy/Makefile @@ -0,0 +1,30 @@ + + +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + +main: main.c librealmain.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c librealmain.dylib + +librealmain.dylib: realmain.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o librealmain.dylib realmain.c libfoo.dylib + +libfoo.dylib: foo.c libbar.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libfoo.dylib foo.c libbar.dylib + +libbar.dylib: bar.c libbaz.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbar.dylib bar.c libbaz.dylib + +libbaz.dylib: baz.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -o libbaz.dylib baz.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libbar.dylib libbaz.dylib librealmain.dylib + diff --git a/dyld/unit-tests/test-cases/weak-non-lazy/bar.c b/dyld/unit-tests/test-cases/weak-non-lazy/bar.c new file mode 100644 index 0000000..9db55bd --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-non-lazy/bar.c @@ -0,0 +1,6 @@ + + +int bar[] = { 20, 21, 22, 23 }; + +// needs something weak to join coalescing fun +int __attribute__((weak)) junk = 5; diff --git a/dyld/unit-tests/test-cases/weak-non-lazy/baz.c b/dyld/unit-tests/test-cases/weak-non-lazy/baz.c new file mode 100644 index 0000000..411ae68 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-non-lazy/baz.c @@ -0,0 +1,9 @@ + + + + + +int __attribute__((weak)) bar[] = { 30, 31, 32, 33 }; + + + diff --git a/dyld/unit-tests/test-cases/weak-non-lazy/foo.c b/dyld/unit-tests/test-cases/weak-non-lazy/foo.c new file mode 100644 index 0000000..a212684 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-non-lazy/foo.c @@ -0,0 +1,15 @@ + + + + + +int __attribute__((weak)) foo[] = { 1, 2, 3, 4 }; +int __attribute__((weak)) bar[] = { 10, 11, 12, 13 }; + +int* getfoo() +{ + return foo; +} + + + diff --git a/dyld/unit-tests/test-cases/weak-non-lazy/main.c b/dyld/unit-tests/test-cases/weak-non-lazy/main.c new file mode 100644 index 0000000..523b7be --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-non-lazy/main.c @@ -0,0 +1,12 @@ + + +// put test code in a dylib so that is slides +extern void realmain(); + +int main() +{ + realmain(); + return 0; +} + + diff --git a/dyld/unit-tests/test-cases/weak-non-lazy/realmain.c b/dyld/unit-tests/test-cases/weak-non-lazy/realmain.c new file mode 100644 index 0000000..ca4a6f2 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-non-lazy/realmain.c @@ -0,0 +1,36 @@ + + +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// getfoo() is in libfoo.dylib and uses a non-lazy pointer to reference foo +// libfoo.dylib also has weak foo[] +// getfoo() should be rebound to point into the foo[] in main +int foo[] = { 5, 6, 7, 8 }; +extern int* getfoo(); + +// libfoo.dylib has a weak bar[] +// libbar.dylib has a strong bar[] +// at build time linker only sees weak bar in libfoo.dylib +// at runtime, dyld uses strong bar in libbar.dylib +extern int bar[]; + + +int __attribute__((weak)) stuff[] = { 1, 2, 3, 4, 5 }; + + +void realmain() +{ + if ( getfoo()[2] != 7 ) + FAIL("weak-non-lazy, getfoo()[2]=%d", getfoo()[2]); + else if ( bar[1] != 21 ) + FAIL("weak-non-lazy, bar[1]=%d", bar[1]); + else if ( stuff[3] != 4 ) + FAIL("weak-external-reloc, pstuffr=%d", stuff[3]); + else + PASS("weak-non-lazy"); +} + + diff --git a/dyld/unit-tests/test-cases/weak-override/Makefile b/dyld/unit-tests/test-cases/weak-override/Makefile new file mode 100644 index 0000000..8d7799f --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-override/Makefile @@ -0,0 +1,42 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib: foo.c + ${CC} ${CCFLAGS} -I${TESTROOT}/include -dynamiclib -single_module -o libfoo.dylib foo.c + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib + diff --git a/dyld/unit-tests/test-cases/weak-override/foo.c b/dyld/unit-tests/test-cases/weak-override/foo.c new file mode 100644 index 0000000..984ee82 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-override/foo.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + + +int __attribute__((weak)) myfunc() +{ + return 0; +} + +int __attribute__((weak)) foo() +{ + return myfunc(); +} + +int (*myfuncproc)() = &myfunc; + +int bar() +{ + return (*myfuncproc)(); +} diff --git a/dyld/unit-tests/test-cases/weak-override/main.c b/dyld/unit-tests/test-cases/weak-override/main.c new file mode 100644 index 0000000..8010cc0 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-override/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005-2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +// foo is defined in libfoo.dylib it calls myfunc() +extern int foo(); +// bar is defined in libfoo.dylib it calls myfunc() +extern int bar(); + +int main() +{ + if ( (foo() == 10) && (bar() == 10) ) + PASS("weak-override"); + else + FAIL("weak-override"); + return EXIT_SUCCESS; +} + + +// myfunc() also defined in libfoo.dylib but weak there, so this should override +int myfunc() +{ + return 10; +} + diff --git a/dyld/unit-tests/test-cases/weak-symbol-flat/Makefile b/dyld/unit-tests/test-cases/weak-symbol-flat/Makefile new file mode 100644 index 0000000..dfd56ce --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol-flat/Makefile @@ -0,0 +1,46 @@ +# +# 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: + (export DYLD_IMAGE_SUFFIX=_missing && ./main) || echo "FAIL \"weak-symbol-flat\"" + ./main 1 + +all: main libfoo.dylib libfoo_missing.dylib + +libfoo.dylib : foo.c + ${CC} -dynamiclib -DSYMBOL_PRESENT -o libfoo.dylib foo.c + +libfoo_missing.dylib : foo.c + ${CC} -dynamiclib -o libfoo_missing.dylib foo.c -install_name libfoo.dylib + +main: main.c libfoo.dylib + ${CC} -I${TESTROOT}/include -L. -lfoo -o main main.c -flat_namespace + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib libfoo_missing.dylib main + + diff --git a/dyld/unit-tests/test-cases/weak-symbol-flat/foo.c b/dyld/unit-tests/test-cases/weak-symbol-flat/foo.c new file mode 100644 index 0000000..b4b8cea --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol-flat/foo.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +#ifdef SYMBOL_PRESENT +int foo() +{ + return 10; +} + +#endif + diff --git a/dyld/unit-tests/test-cases/weak-symbol-flat/foo.h b/dyld/unit-tests/test-cases/weak-symbol-flat/foo.h new file mode 100644 index 0000000..7f4d4ab --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol-flat/foo.h @@ -0,0 +1,25 @@ +/* + * 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 + +int foo() WEAK_IMPORT_ATTRIBUTE; diff --git a/dyld/unit-tests/test-cases/weak-symbol-flat/main.c b/dyld/unit-tests/test-cases/weak-symbol-flat/main.c new file mode 100644 index 0000000..146fe81 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol-flat/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // EXIT_SUCCESS + +#include "test.h" +#include "foo.h" + +int main(int argc, const char *argv[]) +{ + if (argc > 1) { + // when arg is passed, libfoo is used and foo should be defined + if ( &foo == NULL ) + FAIL("weak-symbol-flat foo not found"); + else if ( foo() != 10 ) + FAIL("weak-symbol-flat foo not found"); + else + PASS("weak-symbol-flat when symbol found"); + } + else { + // when no args are passed, libfoo_missing is used and foo should be undefined + if ( &foo != NULL ) + FAIL("weak-symbol-flat foo found"); + else + PASS("weak-symbol-flat when symbol missing"); + } + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/weak-symbol/Makefile b/dyld/unit-tests/test-cases/weak-symbol/Makefile new file mode 100644 index 0000000..f2d1cec --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol/Makefile @@ -0,0 +1,38 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + + +all-check: all check + +check: + export DYLD_IMAGE_SUFFIX=_missing && ${TESTROOT}/bin/exit-zero-pass.pl "weak-symbol" "weak-symbol" ./main + +all: foo.c main.c + ${CC} foo.c -dynamiclib -o libfoo.dylib -DSYMBOL_PRESENT + ${CC} foo.c -dynamiclib -o libfoo_missing.dylib -install_name libfoo.dylib + ${CC} -I${TESTROOT}/include -L. -lfoo main.c -o main + +clean: + ${RM} ${RMFLAGS} *~ libfoo.dylib libfoo_missing.dylib main diff --git a/dyld/unit-tests/test-cases/weak-symbol/foo.c b/dyld/unit-tests/test-cases/weak-symbol/foo.c new file mode 100644 index 0000000..c7a3409 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol/foo.c @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#if SYMBOL_PRESENT +int foo() +{ + return 10; +} +#endif diff --git a/dyld/unit-tests/test-cases/weak-symbol/foo.h b/dyld/unit-tests/test-cases/weak-symbol/foo.h new file mode 100644 index 0000000..7f4d4ab --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol/foo.h @@ -0,0 +1,25 @@ +/* + * 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 + +int foo() WEAK_IMPORT_ATTRIBUTE; diff --git a/dyld/unit-tests/test-cases/weak-symbol/main.c b/dyld/unit-tests/test-cases/weak-symbol/main.c new file mode 100644 index 0000000..e5c8905 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak-symbol/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include // EXIT_SUCCESS + +#include "test.h" +#include "foo.h" + +int main(int argc, const char* argv[]) +{ + if ( &foo == NULL ) + { + PASS("weak-symbol not available"); + } + return 0; +} diff --git a/dyld/unit-tests/test-cases/weak_import/Makefile b/dyld/unit-tests/test-cases/weak_import/Makefile new file mode 100644 index 0000000..7833962 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak_import/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test the weak_import attribute works +# + +all-check: all check + +check: + ./main + export DYLD_IMAGE_SUFFIX=_some && ./main missing + +all: main libfoo_some.dylib + + +main : main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -DALL_SYMBOLS=1 -dynamiclib -o libfoo.dylib + +libfoo_some.dylib : foo.c + ${CC} ${CCFLAGS} foo.c -dynamiclib -o libfoo_some.dylib -install_name libfoo.dylib + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib libfoo_some.dylib diff --git a/dyld/unit-tests/test-cases/weak_import/foo.c b/dyld/unit-tests/test-cases/weak_import/foo.c new file mode 100644 index 0000000..8f50835 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak_import/foo.c @@ -0,0 +1,19 @@ + + +#include "foo.h" + +void func1() {} +void func3() {} +int data1 = 0; +int data3; +int data5 = 0; + +#if ALL_SYMBOLS +void func2() {} +void func4() {} + +int data2 = 0; // weak_import initialized +int data4; // weak_import uninitialized +int data6 = 0; // weak_import +#endif + diff --git a/dyld/unit-tests/test-cases/weak_import/foo.h b/dyld/unit-tests/test-cases/weak_import/foo.h new file mode 100644 index 0000000..f455515 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak_import/foo.h @@ -0,0 +1,16 @@ + + +extern void func1(); +extern void func2() __attribute__((weak_import)); +extern void func3(); +extern void func4() __attribute__((weak_import)); + +extern int data1; +extern int data2 __attribute__((weak_import)); +extern int data3; +extern int data4 __attribute__((weak_import)); +extern int data5; +extern int data6 __attribute__((weak_import)); + + + diff --git a/dyld/unit-tests/test-cases/weak_import/main.c b/dyld/unit-tests/test-cases/weak_import/main.c new file mode 100644 index 0000000..a777a52 --- /dev/null +++ b/dyld/unit-tests/test-cases/weak_import/main.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include +#include +#include + +#include "test.h" // PASS(), FAIL() + +#include "foo.h" + + +int* pdata5 = &data5; +int* pdata6 = &data6; + + +int main(int argc, const char* argv[]) +{ + if ( argc > 1 ) { + // this weak case where the even symbol names are missing at runtime + func1(); + func3(); + data1 = data3; + if ( (&func2 == NULL) && (&func4 == NULL) && (&data2 == NULL) && (&data4 == NULL) && (pdata6 == NULL) ) + PASS("weak_import"); + else + FAIL("weak_import"); + } + else { + // this is the regular case where all symbols exist at runtime + func1(); + func2(); + func3(); + func4(); + data1 = data2 + data3 + data4; + PASS("weak_import"); + } + return 0; +} + diff --git a/dyld/unit-tests/test-cases/zero-fill-segment/Makefile b/dyld/unit-tests/test-cases/zero-fill-segment/Makefile new file mode 100644 index 0000000..f0380c7 --- /dev/null +++ b/dyld/unit-tests/test-cases/zero-fill-segment/Makefile @@ -0,0 +1,44 @@ +## +# Copyright (c) 2007 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +all-check: all check + +check: + ./main + +all: main + + +main: main.c foo.bundle + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c + +foo.bundle : foo.c zero.s + ${CC} ${CCFLAGS} -bundle foo.c zero.s -o foo.bundle + + + +clean: + ${RM} ${RMFLAGS} *~ main foo.bundle + diff --git a/dyld/unit-tests/test-cases/zero-fill-segment/foo.c b/dyld/unit-tests/test-cases/zero-fill-segment/foo.c new file mode 100644 index 0000000..4202b55 --- /dev/null +++ b/dyld/unit-tests/test-cases/zero-fill-segment/foo.c @@ -0,0 +1,8 @@ + +extern int foo[]; + +int getfoo(int x) +{ + return foo[x]; +} + diff --git a/dyld/unit-tests/test-cases/zero-fill-segment/main.c b/dyld/unit-tests/test-cases/zero-fill-segment/main.c new file mode 100644 index 0000000..0676756 --- /dev/null +++ b/dyld/unit-tests/test-cases/zero-fill-segment/main.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2007 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS +#include // dlopen() + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + void* handle = dlopen("foo.bundle", RTLD_LAZY); + if ( handle == NULL ) { + FAIL("zero-fill-segment: dlopen(\"%s\") failed with: %s", "foo.bundle", dlerror()); + exit(0); + } + + void* sym = dlsym(handle, "getfoo"); + if ( sym == NULL ) { + FAIL("zero-fill-segment: dlsym(handle, \"getfoo\") failed"); + exit(0); + } + + dlclose(handle); + + PASS("zero-fill-segment"); + return EXIT_SUCCESS; +} diff --git a/dyld/unit-tests/test-cases/zero-fill-segment/zero.s b/dyld/unit-tests/test-cases/zero-fill-segment/zero.s new file mode 100644 index 0000000..cfb52ea --- /dev/null +++ b/dyld/unit-tests/test-cases/zero-fill-segment/zero.s @@ -0,0 +1,5 @@ + +.globl _foo +.zerofill __MYZERO, __zero, _foo, 8192 + + diff --git a/dyld/unit-tests/test-cases/zero-length-segment/Makefile b/dyld/unit-tests/test-cases/zero-length-segment/Makefile new file mode 100644 index 0000000..297c760 --- /dev/null +++ b/dyld/unit-tests/test-cases/zero-length-segment/Makefile @@ -0,0 +1,50 @@ +## +# Copyright (c) 2005 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +SHELL = bash # use bash shell so we can redirect just stderr + + +all-check: all check + +check: + export DYLD_PRINT_SEGMENTS=1 && ./main 2> segments.log + grep __FOOBAR segments.log > /dev/null || echo "PASS zero-length-segment" + (grep __FOOBAR segments.log > /dev/null && echo "FAIL zero-length-segment") || /usr/bin/true + + +all: main + + +main: main.c libfoo.dylib + ${CC} ${CCFLAGS} -I${TESTROOT}/include -o main main.c libfoo.dylib + +libfoo.dylib : foo.c + ${CC} ${CCFLAGS} -dynamiclib foo.c -o libfoo.dylib -Wl,-sectcreate -Wl,__FOOBAR -Wl,__empty -Wl,/dev/null + + + +clean: + ${RM} ${RMFLAGS} *~ main libfoo.dylib segments.log + diff --git a/dyld/unit-tests/test-cases/zero-length-segment/foo.c b/dyld/unit-tests/test-cases/zero-length-segment/foo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/dyld/unit-tests/test-cases/zero-length-segment/foo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/dyld/unit-tests/test-cases/zero-length-segment/main.c b/dyld/unit-tests/test-cases/zero-length-segment/main.c new file mode 100644 index 0000000..f5a0e5d --- /dev/null +++ b/dyld/unit-tests/test-cases/zero-length-segment/main.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include // fprintf(), NULL +#include // exit(), EXIT_SUCCESS + +#include "test.h" // PASS(), FAIL(), XPASS(), XFAIL() + +int +main(int argc, const char* argv[]) +{ + return EXIT_SUCCESS; +}