mirror of
https://github.com/darlinghq/darling-dyld.git
synced 2024-10-06 17:33:30 +00:00
Moved Source From Main Repo
dyld-733.6
This commit is contained in:
commit
01ca8e46cb
13
.clang-format
Normal file
13
.clang-format
Normal file
@ -0,0 +1,13 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: WebKit
|
||||
|
||||
AlignConsecutiveDeclarations: true
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: true
|
||||
IndentWidth: 4
|
||||
|
||||
Standard: Cpp11
|
||||
|
||||
UseTab: Never
|
||||
|
||||
SortIncludes: false
|
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
build
|
||||
DerivedData
|
||||
dyld.xcodeproj/kledzik.mode1v3
|
||||
dyld.xcodeproj/kledzik.pbxuser
|
||||
dyld.xcodeproj/project.xcworkspace/
|
||||
dyld.xcodeproj/xcuserdata/
|
||||
dyld.xcodeproj/xcshareddata/
|
||||
.DS_Store
|
||||
.pyc
|
||||
|
367
APPLE_LICENSE
Normal file
367
APPLE_LICENSE
Normal file
@ -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."
|
167
CMakeLists.txt
Normal file
167
CMakeLists.txt
Normal file
@ -0,0 +1,167 @@
|
||||
project(dyld-apple)
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
if(COMMAND cmake_policy)
|
||||
cmake_policy(SET CMP0003 NEW)
|
||||
endif(COMMAND cmake_policy)
|
||||
|
||||
include (darling_exe)
|
||||
|
||||
add_definitions(-nostdinc)
|
||||
add_definitions(-D_LIBCPP_NO_EXCEPTIONS=1)
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wno-availability -include ${CMAKE_SOURCE_DIR}/src/duct/include/CrashReporterClient.h")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_SAVED "${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib")
|
||||
|
||||
add_definitions(-DHAVE_STDINT_H=1)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/launch-cache)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/mach-o
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dyld3/shared-cache
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dyld3
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
)
|
||||
|
||||
set(DYLIB_INSTALL_NAME "/usr/lib/system/libdyld.dylib")
|
||||
add_circular(system_dyld FAT
|
||||
SOURCES
|
||||
src/dyldAPIsInLibSystem.cpp
|
||||
src/dyld_stub_binder.S
|
||||
src/dyldLibSystemGlue.c
|
||||
src/threadLocalHelpers.S
|
||||
src/dyldLock.cpp
|
||||
src/threadLocalVariables.c
|
||||
src/dyld_process_info.cpp
|
||||
src/dyld_process_info_notify.cpp
|
||||
src/start_glue.S
|
||||
dyld3/AllImages.cpp
|
||||
dyld3/APIs.cpp
|
||||
dyld3/APIs_macOS.cpp
|
||||
dyld3/ClosureBuilder.cpp
|
||||
dyld3/Closure.cpp
|
||||
dyld3/ClosureFileSystemPhysical.cpp
|
||||
#dyld3/ClosurePrinter.cpp
|
||||
dyld3/ClosureWriter.cpp
|
||||
dyld3/Diagnostics.cpp
|
||||
dyld3/shared-cache/DyldSharedCache.cpp
|
||||
#dyld3/JSONReader.mm
|
||||
dyld3/libdyldEntryVector.cpp
|
||||
dyld3/Loading.cpp
|
||||
dyld3/Logging.cpp
|
||||
dyld3/MachOAnalyzer.cpp
|
||||
dyld3/MachOFile.cpp
|
||||
dyld3/MachOLoaded.cpp
|
||||
dyld3/PathOverrides.cpp
|
||||
dyld3/Tracing.cpp
|
||||
|
||||
UPWARD
|
||||
system_c
|
||||
system_kernel
|
||||
system_malloc
|
||||
system_blocks
|
||||
system_pthread
|
||||
system_sandbox
|
||||
libdispatch_shared
|
||||
compiler_rt
|
||||
platform
|
||||
launch
|
||||
)
|
||||
set_target_properties(system_dyld PROPERTIES
|
||||
OUTPUT_NAME "dyld")
|
||||
set_property(TARGET system_dyld_obj APPEND_STRING PROPERTY
|
||||
COMPILE_FLAGS " -fno-exceptions -fno-rtti -DBUILDING_LIBDYLD=1")
|
||||
set_property(TARGET system_dyld APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " -Wl,-compatibility_version,1.0.0 -Wl,-current_version,421.1.0")
|
||||
#target_link_libraries(system_dyld PRIVATE platform_static32 platform_static64 system_c system_kernel compiler_rt launch)
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_SAVED} -nostdlib")
|
||||
|
||||
add_executable(system_loader
|
||||
src/dyldStartup.S
|
||||
src/dyld2.cpp
|
||||
src/dyldAPIs.cpp
|
||||
src/dyld_debugger.cpp
|
||||
src/dyldInitialization.cpp
|
||||
src/dyldLock.cpp
|
||||
src/dyldNew.cpp
|
||||
src/ImageLoader.cpp
|
||||
src/ImageLoaderMachOClassic.cpp
|
||||
src/ImageLoaderMachOCompressed.cpp
|
||||
src/ImageLoaderMachO.cpp
|
||||
src/ImageLoaderMegaDylib.cpp
|
||||
src/dyldExceptions.c
|
||||
src/glue.c
|
||||
src/stub_binding_helper.S
|
||||
src/start_glue.S
|
||||
hell.c
|
||||
dyld3/BootArgs.cpp
|
||||
dyld3/ClosureBuilder.cpp
|
||||
dyld3/Closure.cpp
|
||||
dyld3/ClosureFileSystemNull.cpp
|
||||
dyld3/ClosureFileSystemPhysical.cpp
|
||||
#dyld3/ClosurePrinter.cpp
|
||||
dyld3/ClosureWriter.cpp
|
||||
dyld3/Tracing.cpp
|
||||
dyld3/Loading.cpp
|
||||
dyld3/Diagnostics.cpp
|
||||
dyld3/SharedCacheRuntime.cpp
|
||||
dyld3/shared-cache/DyldSharedCache.cpp
|
||||
#dyld3/shared-cache/FileUtils.cpp
|
||||
dyld3/MachOAnalyzer.cpp
|
||||
dyld3/MachOFile.cpp
|
||||
dyld3/MachOLoaded.cpp
|
||||
dyld3/PathOverrides.cpp
|
||||
dyld3/SharedCacheRuntime.cpp
|
||||
sandbox-dummy.c
|
||||
version.c # Too make the .exp file work
|
||||
)
|
||||
|
||||
use_ld64(system_loader)
|
||||
dsym(system_loader)
|
||||
set_target_properties(system_loader PROPERTIES OUTPUT_NAME "dyld")
|
||||
set_property(TARGET system_loader APPEND_STRING PROPERTY
|
||||
COMPILE_FLAGS " -DBUILDING_DYLD=1 -gfull -fPIC -fno-stack-check")
|
||||
set_property(TARGET system_loader APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-dylinker -Wl,-dead_strip -Wl,-data_const -nostdlib -Wl,-e,__dyld_start -Wl,-fixup_chains -Wl,-image_base,0x1fe00000 -Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/src/dyld.exp")
|
||||
target_link_libraries(system_loader libc_static system_blocks_static
|
||||
cxx_static
|
||||
unwind_static system_m_static cxxabi_static
|
||||
keymgr_static system_duct_static
|
||||
pthread_static macho_static
|
||||
system_trace_static
|
||||
corecrypto_static
|
||||
)
|
||||
|
||||
if (BUILD_TARGET_32BIT)
|
||||
target_link_libraries(system_loader libc_static32 compiler_rt_static32 platform_static32 system_kernel_static32)
|
||||
endif (BUILD_TARGET_32BIT)
|
||||
|
||||
if (BUILD_TARGET_64BIT)
|
||||
target_link_libraries(system_loader libc_static64 compiler_rt_static64 platform_static64 system_kernel_static64)
|
||||
endif (BUILD_TARGET_64BIT)
|
||||
|
||||
make_fat(system_loader)
|
||||
|
||||
install(TARGETS system_dyld DESTINATION libexec/darling/usr/lib/system)
|
||||
install(TARGETS system_loader DESTINATION libexec/darling/usr/lib)
|
||||
install(FILES
|
||||
doc/man/man1/dyld.1
|
||||
doc/man/man1/update_dyld_shared_cache.1
|
||||
DESTINATION
|
||||
libexec/darling/usr/share/man/man1
|
||||
)
|
||||
install(FILES
|
||||
doc/man/man3/dladdr.3
|
||||
doc/man/man3/dlclose.3
|
||||
doc/man/man3/dlerror.3
|
||||
doc/man/man3/dlopen.3
|
||||
doc/man/man3/dlopen_preflight.3
|
||||
doc/man/man3/dlsym.3
|
||||
doc/man/man3/dyld.3
|
||||
DESTINATION
|
||||
libexec/darling/usr/share/man/man3
|
||||
)
|
||||
|
102
bin/expand.rb
Executable file
102
bin/expand.rb
Executable file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'yaml'
|
||||
|
||||
if ENV["DRIVERKITROOT"]
|
||||
$availCmd = ENV["SDKROOT"] + ENV["DRIVERKITROOT"] + "/usr/local/libexec/availability.pl";
|
||||
else
|
||||
$availCmd = ENV["SDKROOT"] + "/usr/local/libexec/availability.pl";
|
||||
end
|
||||
|
||||
def versionString(vers)
|
||||
uvers = ""
|
||||
if vers =~ /^(\d+)$/
|
||||
uvers = "#{$1}_0"
|
||||
elsif vers =~ /^(\d+).(\d+)$/
|
||||
uvers = "#{$1}_#{$2}"
|
||||
elsif vers =~ /^(\d+).(\d+).(\d+)$/
|
||||
if $3 == 0
|
||||
uvers = "#{$1}_#{$2}"
|
||||
else
|
||||
uvers = "#{$1}_#{$2}_#{$3}"
|
||||
end
|
||||
end
|
||||
uvers
|
||||
end
|
||||
|
||||
def versionHex(vers)
|
||||
major = 0;
|
||||
minor = 0;
|
||||
revision = 0;
|
||||
|
||||
if vers =~ /^(\d+)$/
|
||||
major = $1.to_i;
|
||||
elsif vers =~ /^(\d+).(\d+)$/
|
||||
major = $1.to_i;
|
||||
minor = $2.to_i;
|
||||
elsif vers =~ /^(\d+).(\d+).(\d+)$/
|
||||
major = $1.to_i;
|
||||
minor = $2.to_i;
|
||||
revision = $3.to_i;
|
||||
end
|
||||
"0x00#{major.to_s(16).rjust(2, '0')}#{minor.to_s(16).rjust(2, '0')}#{revision.to_s(16).rjust(2, '0')}"
|
||||
end
|
||||
|
||||
def expandVersions(prefix, arg)
|
||||
versionList = `#{$availCmd} #{arg}`.gsub(/\s+/m, ' ').strip.split(" ")
|
||||
versionList.each { |version|
|
||||
puts "#define #{prefix}#{versionString(version)}".ljust(48, ' ') + versionHex(version)
|
||||
}
|
||||
end
|
||||
|
||||
def expandPlatformVersions(prefix, platform, arg)
|
||||
versionList = `#{$availCmd} #{arg}`.gsub(/\s+/m, ' ').strip.split(" ")
|
||||
versionList.each { |version|
|
||||
puts "static dyld_build_version_t dyld_platform_version_#{prefix}_#{versionString(version)}".ljust(72, ' ') + "= { .platform = #{platform}, .version = #{versionHex(version)} };"
|
||||
}
|
||||
end
|
||||
|
||||
def versionSetsForOSes(versionSets, key, platform, target)
|
||||
puts "#if #{target}"
|
||||
versionSets.each { |k,v|
|
||||
puts "#define dyld_#{k}_os_versions dyld_platform_version_#{platform}_#{versionString(v[key].to_s)}"
|
||||
}
|
||||
puts "#endif /* #{target} */"
|
||||
end
|
||||
|
||||
def expandVersionSets()
|
||||
versionSets = YAML.load(`#{$availCmd} --sets`)
|
||||
versionSetsForOSes(versionSets, "macos", "macOS", "TARGET_OS_OSX")
|
||||
versionSetsForOSes(versionSets, "ios", "iOS", "TARGET_OS_IOS")
|
||||
versionSetsForOSes(versionSets, "tvos", "tvOS", "TARGET_OS_TV")
|
||||
versionSetsForOSes(versionSets, "watchos", "watchOS", "TARGET_OS_WATCH")
|
||||
versionSetsForOSes(versionSets, "bridgeos", "bridgeOS", "TARGET_OS_BRIDGE")
|
||||
end
|
||||
|
||||
ARGF.each do |line|
|
||||
if line =~ /^\/\/\@MAC_VERSION_DEFS\@$/
|
||||
expandVersions("DYLD_MACOSX_VERSION_", "--macosx")
|
||||
elsif line =~ /^\/\/\@IOS_VERSION_DEFS\@$/
|
||||
expandVersions("DYLD_IOS_VERSION_", "--ios")
|
||||
elsif line =~ /^\/\/\@WATCHOS_VERSION_DEFS\@$/
|
||||
expandVersions("DYLD_WATCHOS_VERSION_", "--watchos")
|
||||
elsif line =~ /^\/\/\@TVOS_VERSION_DEFS\@$/
|
||||
expandVersions("DYLD_TVOS_VERSION_", "--appletvos")
|
||||
elsif line =~ /^\/\/\@BRIDGEOS_VERSION_DEFS\@$/
|
||||
expandVersions("DYLD_BRIDGEOS_VERSION_", "--bridgeos")
|
||||
elsif line =~ /^\/\/\@MACOS_PLATFORM_VERSION_DEFS\@$/
|
||||
expandPlatformVersions("macOS", "PLATFORM_MACOS", "--macosx")
|
||||
elsif line =~ /^\/\/\@IOS_PLATFORM_VERSION_DEFS\@$/
|
||||
expandPlatformVersions("iOS", "PLATFORM_IOS", "--ios")
|
||||
elsif line =~ /^\/\/\@WATCHOS_PLATFORM_VERSION_DEFS\@$/
|
||||
expandPlatformVersions("watchOS", "PLATFORM_WATCHOS", "--watchos")
|
||||
elsif line =~ /^\/\/\@TVOS_PLATFORM_VERSION_DEFS\@$/
|
||||
expandPlatformVersions("tvOS", "PLATFORM_TVOS", "--appletvos")
|
||||
elsif line =~ /^\/\/\@BRIDGEOS_PLATFORM_VERSION_DEFS\@$/
|
||||
expandPlatformVersions("bridgeOS", "PLATFORM_BRIDGEOS", "--bridgeos")
|
||||
elsif line =~ /^\/\/\@VERSION_SET_DEFS\@$/
|
||||
expandVersionSets()
|
||||
else
|
||||
puts line
|
||||
end
|
||||
end
|
30
bin/set-alt-dyld
Executable file
30
bin/set-alt-dyld
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/perl -w
|
||||
|
||||
use strict;
|
||||
undef $/;
|
||||
|
||||
if(@ARGV == 0)
|
||||
{
|
||||
print "Usage: $0 <macho-executable> [<macho-executable> ...]\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $arg;
|
||||
foreach $arg (@ARGV)
|
||||
{
|
||||
open IN, "<$arg" or die $!;
|
||||
my $in = <IN>;
|
||||
close IN or die $!;
|
||||
|
||||
if($in =~ s{/usr/lib/dyld}{/usr/local/dy})
|
||||
{
|
||||
open OUT, ">$arg" or die $!;
|
||||
print OUT $in;
|
||||
close OUT or die $!;
|
||||
}
|
||||
else
|
||||
{
|
||||
print STDERR "ERROR: $arg\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
0
configs/base.xcconfig
Normal file
0
configs/base.xcconfig
Normal file
2
configs/closured.xcconfig
Normal file
2
configs/closured.xcconfig
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
CODE_SIGN_ENTITLEMENTS[sdk=embedded*] = dyld3/closured/closured_entitlements.plist
|
26
configs/dyld.xcconfig
Normal file
26
configs/dyld.xcconfig
Normal file
@ -0,0 +1,26 @@
|
||||
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
|
||||
|
||||
OTHER_CODE_SIGN_FLAGS[sdk=*simulator*] = --entitlements $(SRCROOT)/dyld_sim-entitlements.plist
|
||||
OTHER_CODE_SIGN_FLAGS[sdk=iphoneos*] =
|
||||
OTHER_CODE_SIGN_FLAGS[sdk=macosx*] =
|
||||
|
||||
|
||||
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_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1 DEBUG=1
|
||||
|
||||
//:configuration = Release
|
||||
GCC_PREPROCESSOR_DEFINITIONS = DYLD_VERSION=$(RC_ProjectSourceVersion) BUILDING_DYLD=1
|
8
configs/libdyld.xcconfig
Normal file
8
configs/libdyld.xcconfig
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
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-lcorecrypto -Wl,-upward-lcompiler_rt
|
||||
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-lcorecrypto -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-lcorecrypto -Wl,-upward-lcompiler_rt
|
||||
LIBSYSTEM_LIBS[sdk=driverkit*] = -Wl,-upward-lsystem_platform -Wl,-upward-lsystem_malloc -Wl,-upward-lsystem_c -Wl,-upward-lsystem_pthread -Wl,-upward-lsystem_blocks -Wl,-upward-lsystem_kernel -Wl,-upward-lcompiler_rt
|
||||
|
||||
INSTALL_PATH = /usr/lib/system
|
||||
|
1
configs/update_dyld_shared_cache.xcconfig
Normal file
1
configs/update_dyld_shared_cache.xcconfig
Normal file
@ -0,0 +1 @@
|
||||
|
9
configs/update_dyld_sim_shared_cache.xcconfig
Normal file
9
configs/update_dyld_sim_shared_cache.xcconfig
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
#include "<DEVELOPER_DIR>/AppleInternal/XcodeConfig/PlatformSupportHost.xcconfig"
|
||||
|
||||
|
||||
//:configuration = Debug
|
||||
GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1 DEBUG=1
|
||||
|
||||
//:configuration = Release
|
||||
GCC_PREPROCESSOR_DEFINITIONS = BUILDING_CACHE_BUILDER=1
|
14
darling-scripts/add-version-macros.sh
Executable file
14
darling-scripts/add-version-macros.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -x
|
||||
|
||||
pushd $(dirname "$0") > /dev/null
|
||||
SCRIPTDIR=$(pwd -P)
|
||||
popd > /dev/null
|
||||
|
||||
export SRCROOT="$SCRIPTDIR/.."
|
||||
export SDKROOT="$SRCROOT/../../Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"
|
||||
|
||||
# copied and modified from dyld.xcodeproj
|
||||
${SRCROOT}/bin/expand.rb < "${SRCROOT}/include/mach-o/dyld_priv.h" > "${SRCROOT}/include/mach-o/dyld_priv.h.tmp"
|
||||
|
||||
mv "${SRCROOT}/include/mach-o/dyld_priv.h.tmp" "${SRCROOT}/include/mach-o/dyld_priv.h"
|
1771
doc/ReleaseNotes.txt
Normal file
1771
doc/ReleaseNotes.txt
Normal file
File diff suppressed because it is too large
Load Diff
10
doc/man/man1/closured.1
Normal file
10
doc/man/man1/closured.1
Normal file
@ -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.
|
289
doc/man/man1/dyld.1
Normal file
289
doc/man/man1/dyld.1
Normal file
@ -0,0 +1,289 @@
|
||||
.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/A/XXX or XXX.framework/XXX, where XXX and A 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 those directories and can't find the framework, it
|
||||
uses whatever it would have loaded if DYLD_FRAMEWORK_PATH had not been set.
|
||||
.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.
|
||||
If a framework is not found at its install path, dyld uses this
|
||||
as a list of directories to search for the framework.
|
||||
|
||||
By default, it is set to
|
||||
/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 if 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 dylib that a program uses, the dynamic linker looks for its
|
||||
leaf name in each directory in
|
||||
.SM DYLD_LIBRARY_PATH.
|
||||
.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.
|
||||
If a dylib is not found at its install path,
|
||||
dyld uses this as a list of directories to search for the dylib.
|
||||
By default, it is set
|
||||
to /usr/local/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 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 to not register dtrace static probes with the kernel.
|
||||
.TP
|
||||
.B DYLD_PRINT_INITIALIZERS
|
||||
Causes dyld to print out a line when running each initializer in every image. Initializers
|
||||
run by dyld include 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)
|
112
doc/man/man1/dyld_usage.1
Normal file
112
doc/man/man1/dyld_usage.1
Normal file
@ -0,0 +1,112 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "DYLD_USAGE" "1" "2018-07-28" "" "dyld"
|
||||
.SH NAME
|
||||
dyld_usage \- report dynamic linker activity in real-time
|
||||
.
|
||||
.nr rst2man-indent-level 0
|
||||
.
|
||||
.de1 rstReportMargin
|
||||
\\$1 \\n[an-margin]
|
||||
level \\n[rst2man-indent-level]
|
||||
level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
-
|
||||
\\n[rst2man-indent0]
|
||||
\\n[rst2man-indent1]
|
||||
\\n[rst2man-indent2]
|
||||
..
|
||||
.de1 INDENT
|
||||
.\" .rstReportMargin pre:
|
||||
. RS \\$1
|
||||
. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
|
||||
. nr rst2man-indent-level +1
|
||||
.\" .rstReportMargin post:
|
||||
..
|
||||
.de UNINDENT
|
||||
. RE
|
||||
.\" indent \\n[an-margin]
|
||||
.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.nr rst2man-indent-level -1
|
||||
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
|
||||
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
|
||||
..
|
||||
.SH SYNOPSIS
|
||||
.sp
|
||||
\fBdyld_usage\fP \fB[\-e] [\-f mode] [\-t seconds] [\-R rawfile [\-S start_time]
|
||||
[\-E end_time]] [pid | cmd [pid | cmd] ...]\fP
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
The \fBdyld_usage\fP utility presents an ongoing display of information
|
||||
pertaining to dynamic linker activity within one or more processes. It requires
|
||||
root privileges due to the kernel tracing facility it uses to operate. By
|
||||
default \fBdyld_usage\fP monitors \fIdyld\fP activity in all processes except
|
||||
the running \fIdyld_usage\fP process, \fITerminal\fP, \fItelnetd\fP, \fIsshd\fP, \fIrlogind\fP,
|
||||
\fItcsh\fP, \fIcsh\fP, and \fIsh\fP\&. These defaults can be overridden such that output is
|
||||
limited to include or exclude a list of processes specified on the command line.
|
||||
Processes may be specified either by file name or by process id.
|
||||
.sp
|
||||
The output of \fBdyld_usage\fP is formatted according to the size of your
|
||||
window. A narrow window will display fewer columns of data. Use a wide window
|
||||
for maximum data display.
|
||||
.SH OPTIONS
|
||||
.sp
|
||||
\fBdyld_usage\fP supports the following options:
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-e
|
||||
Exclude the specified list of pids and commands from the sample, and exclude
|
||||
\fBdyld_usage\fP by default.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-R
|
||||
specifies a raw trace file to process.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B \-t
|
||||
specifies timeout in seconds (for use in automated tools).
|
||||
.UNINDENT
|
||||
.SH DISPLAY
|
||||
.sp
|
||||
The data columns displayed are as follows:
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B TIMESTAMP
|
||||
Time of day when call occurred.
|
||||
.TP
|
||||
.B OPERATION
|
||||
The \fIdyld\fP operation triggered by the process. Typically these operations
|
||||
are triggered by process launch or via a \fBdlopen\fP or \fBdlsym\fP system
|
||||
call. System call entries include both the parameters to the system call and
|
||||
the system call\(aqs return code (e.g., 0 on success).
|
||||
.TP
|
||||
.B TIME INTERVAL
|
||||
The elapsed time spent in the dynamic linker operation or system call.
|
||||
.TP
|
||||
.B PROCESS NAME
|
||||
The process that generated the dynamic linker activity. If space allows, the
|
||||
thread id will be appended to the process name (i.e., Mail.nnn).
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH SAMPLE USAGE
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
\fBsudo dyld_usage Mail\fP
|
||||
.sp
|
||||
\fBdyld_usage\fP will display dynamic link operations for all instances of
|
||||
processes named Mail.
|
||||
.UNINDENT
|
||||
.UNINDENT
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
\fBdyld(1)\fP, \fBfs_usage(1)\fP
|
||||
.SH AUTHOR
|
||||
Apple, Inc.
|
||||
.SH COPYRIGHT
|
||||
2000-2018, Apple, Inc.
|
||||
.\" Generated by docutils manpage writer.
|
||||
.
|
75
doc/man/man1/update_dyld_shared_cache.1
Normal file
75
doc/man/man1/update_dyld_shared_cache.1
Normal file
@ -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
|
73
doc/man/man3/dladdr.3
Normal file
73
doc/man/man3/dladdr.3
Normal file
@ -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 <jacereda@users.sourceforge.net>
|
||||
and Peter O'Gorman <ogorman@users.sourceforge.net>.
|
||||
.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
|
43
doc/man/man3/dlclose.3
Normal file
43
doc/man/man3/dlclose.3
Normal file
@ -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 support unloading (e.g. NSAddImage()) was used to load
|
||||
it or some other dynamic 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
|
||||
retrieved 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
|
34
doc/man/man3/dlerror.3
Normal file
34
doc/man/man3/dlerror.3
Normal file
@ -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
|
187
doc/man/man3/dlopen.3
Normal file
187
doc/man/man3/dlopen.3
Normal file
@ -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
|
33
doc/man/man3/dlopen_preflight.3
Normal file
33
doc/man/man3/dlopen_preflight.3
Normal file
@ -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
|
94
doc/man/man3/dlsym.3
Normal file
94
doc/man/man3/dlsym.3
Normal file
@ -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 are looking up a C++ symbol, you need to use the mangled C++ symbol name.
|
||||
name.
|
||||
.Sh SEE ALSO
|
||||
.Xr dlopen 3
|
||||
.Xr dlerror 3
|
||||
.Xr dyld 3
|
||||
.Xr ld 1
|
||||
.Xr cc 1
|
128
doc/man/man3/dyld.3
Normal file
128
doc/man/man3/dyld.3
Normal file
@ -0,0 +1,128 @@
|
||||
.Dd June 20, 2019
|
||||
.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. The function is called with the dyld lock held. This blocks other
|
||||
threads from using dlopen(). Therefore, the function should do minimal work and
|
||||
return.
|
||||
.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
|
||||
https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html
|
253
doc/rst/conf.py
Normal file
253
doc/rst/conf.py
Normal file
@ -0,0 +1,253 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# dyld documentation build configuration file.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
from datetime import date
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
# extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'dyld'
|
||||
copyright = u'2000-%d, Apple, Inc.' % date.today().year
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short version.
|
||||
# version = '7'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
# release = '7'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
today_fmt = '%Y-%m-%d'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
show_authors = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'friendly'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'default'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = { "nosidebar": True }
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ["_themes"]
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
#html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
html_last_updated_fmt = '%Y-%m-%d'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {'index': 'indexsidebar.html'}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
# htmlhelp_basename = 'doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'dyld.tex', u'dyld Documentation',
|
||||
u'dyld project', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = []
|
||||
|
||||
# Automatically derive the list of man pages from the contents of the command
|
||||
# guide subdirectory.
|
||||
basedir = os.path.dirname(__file__)
|
||||
man_page_authors = "Apple, Inc."
|
||||
command_guide_subpath = '.'
|
||||
command_guide_path = os.path.join(basedir, command_guide_subpath)
|
||||
for name in os.listdir(command_guide_path):
|
||||
# Ignore non-ReST files and the index page.
|
||||
if not name.endswith('.rst') or name in ('index.rst',):
|
||||
continue
|
||||
|
||||
# Otherwise, automatically extract the description.
|
||||
file_subpath = os.path.join(command_guide_subpath, name)
|
||||
with open(os.path.join(command_guide_path, name)) as f:
|
||||
title = f.readline().rstrip('\n')
|
||||
header = f.readline().rstrip('\n')
|
||||
|
||||
if len(header) != len(title):
|
||||
print >>sys.stderr, (
|
||||
"error: invalid header in %r (does not match title)" % (
|
||||
file_subpath,))
|
||||
if ' - ' not in title:
|
||||
print >>sys.stderr, (
|
||||
("error: invalid title in %r "
|
||||
"(expected '<name> - <description>')") % (
|
||||
file_subpath,))
|
||||
|
||||
# Split the name out of the title.
|
||||
name,description = title.split(' - ', 1)
|
||||
man_pages.append((file_subpath.replace('.rst',''), name,
|
||||
description, man_page_authors, 1))
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
# FIXME: Define intersphinx configuration.
|
||||
intersphinx_mapping = {}
|
76
doc/rst/dyld_usage.rst
Normal file
76
doc/rst/dyld_usage.rst
Normal file
@ -0,0 +1,76 @@
|
||||
dyld_usage - report dynamic linker activity in real-time
|
||||
==========================================================
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
|
||||
:program:`dyld_usage` **[-e] [-f mode] [-t seconds] [-R rawfile [-S start_time]
|
||||
[-E end_time]] [pid | cmd [pid | cmd] ...]**
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
The :program:`dyld_usage` utility presents an ongoing display of information
|
||||
pertaining to dynamic linker activity within one or more processes. It requires
|
||||
root privileges due to the kernel tracing facility it uses to operate. By
|
||||
default :program:`dyld_usage` monitors `dyld` activity in all processes except
|
||||
the running `dyld_usage` process, `Terminal`, `telnetd`, `sshd`, `rlogind`,
|
||||
`tcsh`, `csh`, and `sh`. These defaults can be overridden such that output is
|
||||
limited to include or exclude a list of processes specified on the command line.
|
||||
Processes may be specified either by file name or by process id.
|
||||
|
||||
The output of :program:`dyld_usage` is formatted according to the size of your
|
||||
window. A narrow window will display fewer columns of data. Use a wide window
|
||||
for maximum data display.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
:program:`dyld_usage` supports the following options:
|
||||
|
||||
.. option:: -e
|
||||
|
||||
Exclude the specified list of pids and commands from the sample, and exclude
|
||||
:program:`dyld_usage` by default.
|
||||
|
||||
.. option:: -R
|
||||
|
||||
specifies a raw trace file to process.
|
||||
|
||||
.. option:: -t
|
||||
|
||||
specifies timeout in seconds (for use in automated tools).
|
||||
|
||||
DISPLAY
|
||||
-------
|
||||
|
||||
The data columns displayed are as follows:
|
||||
|
||||
TIMESTAMP
|
||||
Time of day when call occurred.
|
||||
|
||||
OPERATION
|
||||
The `dyld` operation triggered by the process. Typically these operations
|
||||
are triggered by process launch or via a ``dlopen`` or ``dlsym`` system
|
||||
call. System call entries include both the parameters to the system call and
|
||||
the system call's return code (e.g., 0 on success).
|
||||
|
||||
TIME INTERVAL
|
||||
The elapsed time spent in the dynamic linker operation or system call.
|
||||
|
||||
PROCESS NAME
|
||||
The process that generated the dynamic linker activity. If space allows, the
|
||||
thread id will be appended to the process name (i.e., Mail.nnn).
|
||||
|
||||
|
||||
SAMPLE USAGE
|
||||
------------
|
||||
|
||||
``sudo dyld_usage Mail``
|
||||
|
||||
:program:`dyld_usage` will display dynamic link operations for all instances of
|
||||
processes named Mail.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
:manpage:`dyld(1)`, :manpage:`fs_usage(1)`
|
||||
|
9
doc/rst/index.rst
Normal file
9
doc/rst/index.rst
Normal file
@ -0,0 +1,9 @@
|
||||
DYLD Command Guide
|
||||
------------------
|
||||
|
||||
The following documents are command descriptions for some of the DYLD tools.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
dyld_usage
|
22
doc/tracing/dyld.codes
Normal file
22
doc/tracing/dyld.codes
Normal file
@ -0,0 +1,22 @@
|
||||
0x1f070000 dyld.static_intializer
|
||||
0x1f070004 dyld.launch_executable
|
||||
0x1f070008 dyld.map_file
|
||||
0x1f07000c dyld.apply_fixups
|
||||
0x1f070010 dyld.attach_codesignature
|
||||
0x1f070014 dyld.build_closure
|
||||
0x1f070018 dyld.add_image_callback
|
||||
0x1f07001c dyld.remove_image_callback
|
||||
0x1f070020 dyld.objc_image_init
|
||||
0x1f070024 dyld.objc_images_map
|
||||
0x1f070028 dyld.apply_interposing
|
||||
0x1f07002c dyld.gdb_image_notifier
|
||||
0x1f070030 dyld.remote_image_notifier
|
||||
0x1f080000 dyld.dlopen
|
||||
0x1f080004 dyld.dlopen_preflight
|
||||
0x1f080008 dyld.dlclose
|
||||
0x1f08000c dyld.dlsym
|
||||
0x1f080010 dyld.dladdr
|
||||
0x1f090000 dyld.DEBUG.vm_remap
|
||||
0x1f090004 dyld.DEBUG.vm_dealloc
|
||||
0x1f090008 dyld.DEBUG.map_loop
|
||||
0x1f09000c dyld.DEBUG.marker
|
127
doc/tracing/dyld.plist
Normal file
127
doc/tracing/dyld.plist
Normal file
@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>dyld</string>
|
||||
<key>Children</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>dlopen</string>
|
||||
<key>Type</key>
|
||||
<string>Interval</string>
|
||||
<key>EventsMatchedBy</key>
|
||||
<string>Arg1</string>
|
||||
<key>KTraceCodeBegin</key>
|
||||
<string>0x1f070005</string>
|
||||
<key>KTraceCodeEnd</key>
|
||||
<string>0x1f070006</string>
|
||||
<key>ArgValueTypesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<key>ArgNamesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>Path</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>dlopen_preflight</string>
|
||||
<key>Type</key>
|
||||
<string>Interval</string>
|
||||
<key>EventsMatchedBy</key>
|
||||
<string>Arg1</string>
|
||||
<key>KTraceCodeBegin</key>
|
||||
<string>0x1f070009</string>
|
||||
<key>KTraceCodeEnd</key>
|
||||
<string>0x1f07000a</string>
|
||||
<key>ArgValueTypesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<key>ArgNamesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>Path</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>map_file</string>
|
||||
<key>Type</key>
|
||||
<string>Interval</string>
|
||||
<key>EventsMatchedBy</key>
|
||||
<string>Arg1</string>
|
||||
<key>KTraceCodeBegin</key>
|
||||
<string>0x1f070005</string>
|
||||
<key>KTraceCodeEnd</key>
|
||||
<string>0x1f070006</string>
|
||||
<key>ArgValueTypesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<key>ArgNamesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>Path</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>dlsym</string>
|
||||
<key>Type</key>
|
||||
<string>Interval</string>
|
||||
<key>EventsMatchedBy</key>
|
||||
<string>Arg1</string>
|
||||
<key>KTraceCodeBegin</key>
|
||||
<string>0x1f07000d</string>
|
||||
<key>KTraceCodeEnd</key>
|
||||
<string>0x1f07000e</string>
|
||||
<key>ArgValueTypesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>String</string>
|
||||
</dict>
|
||||
<key>ArgNamesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>Path</string>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Name</key>
|
||||
<string>Static Initializer</string>
|
||||
<key>Type</key>
|
||||
<string>Interval</string>
|
||||
<key>EventsMatchedBy</key>
|
||||
<string>Arg1</string>
|
||||
<key>KTraceCodeBegin</key>
|
||||
<string>0x1f070001</string>
|
||||
<key>KTraceCodeEnd</key>
|
||||
<string>0x1f070002</string>
|
||||
<key>ArgValueTypesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>Hex</string>
|
||||
<key>Arg3</key>
|
||||
<string>Hex</string>
|
||||
</dict>
|
||||
<key>ArgNamesBegin</key>
|
||||
<dict>
|
||||
<key>Arg2</key>
|
||||
<string>Mach Header</string>
|
||||
<key>Arg3</key>
|
||||
<string>Initializer Address</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
4133
dyld.xcodeproj/project.pbxproj
Normal file
4133
dyld.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load Diff
1447
dyld3/APIs.cpp
Normal file
1447
dyld3/APIs.cpp
Normal file
File diff suppressed because it is too large
Load Diff
241
dyld3/APIs.h
Normal file
241
dyld3/APIs.h
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <mach-o/dyld_priv.h>
|
||||
|
||||
#include "dlfcn.h"
|
||||
|
||||
|
||||
#define TEMP_HIDDEN __attribute__((visibility("hidden")))
|
||||
|
||||
//
|
||||
// The implementation of all dyld load/unload API's must hold a global lock
|
||||
// so that the next load/unload does start until the current is complete.
|
||||
// This lock is recursive so that initializers can call dlopen().
|
||||
// This is done using the macros DYLD_LOCK_THIS_BLOCK.
|
||||
// Example:
|
||||
//
|
||||
// void dyld_load_api() {
|
||||
// DYLD_LOAD_LOCK_THIS_BLOCK;
|
||||
// // free to do stuff here
|
||||
// // that accesses dyld internal data structures
|
||||
// }
|
||||
//
|
||||
//
|
||||
|
||||
#define DYLD_LOAD_LOCK_THIS_BLOCK RecursiveAutoLock _dyld_load_lock;
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
class __attribute__((visibility("hidden"))) RecursiveAutoLock
|
||||
{
|
||||
public:
|
||||
RecursiveAutoLock() {
|
||||
pthread_mutex_lock(&_sMutex);
|
||||
}
|
||||
~RecursiveAutoLock() {
|
||||
pthread_mutex_unlock(&_sMutex);
|
||||
}
|
||||
private:
|
||||
static pthread_mutex_t _sMutex;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
uint32_t dyld_get_program_sdk_watch_os_version() TEMP_HIDDEN;
|
||||
uint32_t dyld_get_program_min_watch_os_version() TEMP_HIDDEN;
|
||||
|
||||
uint32_t dyld_get_program_sdk_bridge_os_version() TEMP_HIDDEN;
|
||||
uint32_t dyld_get_program_min_bridge_os_version() TEMP_HIDDEN;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
dyld_platform_t dyld_get_active_platform(void) TEMP_HIDDEN;
|
||||
dyld_platform_t dyld_get_base_platform(dyld_platform_t platform) TEMP_HIDDEN;
|
||||
bool dyld_is_simulator_platform(dyld_platform_t platform) TEMP_HIDDEN;
|
||||
bool dyld_sdk_at_least(const struct mach_header* mh, dyld_build_version_t version) TEMP_HIDDEN;
|
||||
bool dyld_minos_at_least(const struct mach_header* mh, dyld_build_version_t version) TEMP_HIDDEN;
|
||||
bool dyld_program_sdk_at_least(dyld_build_version_t version) TEMP_HIDDEN;
|
||||
bool dyld_program_minos_at_least(dyld_build_version_t version) TEMP_HIDDEN;
|
||||
void dyld_get_image_versions(const struct mach_header* mh, void (^callback)(dyld_platform_t platform, uint32_t sdk_version, uint32_t min_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_internal(const char* path, int mode, void* callerAddress) TEMP_HIDDEN;
|
||||
|
||||
bool dlopen_preflight_internal(const char* path) TEMP_HIDDEN;
|
||||
|
||||
void* dlsym_internal(void* handle, const char* symbolName, void* callerAddress) 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_shared_cache_optimized() TEMP_HIDDEN;
|
||||
|
||||
bool _dyld_shared_cache_is_locally_built() TEMP_HIDDEN;
|
||||
|
||||
bool dyld_need_closure(const char* execPath, const char* tempDir) TEMP_HIDDEN;
|
||||
|
||||
void _dyld_images_for_addresses(unsigned count, const void* addresses[], struct dyld_image_uuid_offset infos[]) TEMP_HIDDEN;
|
||||
|
||||
void _dyld_register_for_image_loads(void (*func)(const mach_header* mh, const char* path, bool unloadable)) TEMP_HIDDEN;
|
||||
|
||||
void _dyld_register_for_bulk_image_loads(void (*func)(unsigned imageCount, const struct mach_header* mhs[], const char* paths[])) 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;
|
||||
|
||||
bool dyld_has_inserted_or_interposing_libraries() 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_atfork_prepare() TEMP_HIDDEN;
|
||||
void _dyld_atfork_parent() TEMP_HIDDEN;
|
||||
void _dyld_fork_child() TEMP_HIDDEN;
|
||||
|
||||
void _dyld_missing_symbol_abort() TEMP_HIDDEN;
|
||||
|
||||
const char* _dyld_get_objc_selector(const char* selName) TEMP_HIDDEN;
|
||||
|
||||
void _dyld_for_each_objc_class(const char* className,
|
||||
void (^callback)(void* classPtr, bool isLoaded, bool* stop)) TEMP_HIDDEN;
|
||||
|
||||
void _dyld_for_each_objc_protocol(const char* protocolName,
|
||||
void (^callback)(void* protocolPtr, bool isLoaded, bool* stop)) 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__
|
||||
|
||||
|
605
dyld3/APIs_macOS.cpp
Normal file
605
dyld3/APIs_macOS.cpp
Normal file
@ -0,0 +1,605 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <_simple.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <TargetConditionals.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <mach-o/dyld_priv.h>
|
||||
#include <mach-o/dyld_images.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "dlfcn.h"
|
||||
|
||||
#include "AllImages.h"
|
||||
#include "Loading.h"
|
||||
#include "Logging.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "DyldSharedCache.h"
|
||||
#include "APIs.h"
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
// from APIs.cpp
|
||||
void parseDlHandle(void* h, const MachOLoaded** mh, bool* dontContinue);
|
||||
|
||||
|
||||
// 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
|
||||
OFIInfo result;
|
||||
result.path = strdup(path);
|
||||
result.memSource = nullptr;
|
||||
result.memLength = 0;
|
||||
result.loadAddress = nullptr;
|
||||
result.imageNum = 0;
|
||||
*ofi = gAllImages.addNSObjectFileImage(result);
|
||||
|
||||
log_apis("NSCreateObjectFileImageFromFile() => %p\n", *ofi);
|
||||
|
||||
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;
|
||||
|
||||
// check if it is current arch mach-o or fat with slice for current arch
|
||||
bool usable = false;
|
||||
const MachOFile* mf = (MachOFile*)memImage;
|
||||
if ( mf->hasMachOMagic() && mf->isMachO(diag, memImageSize) ) {
|
||||
usable = (gAllImages.archs().grade(mf->cputype, mf->cpusubtype) != 0);
|
||||
}
|
||||
else if ( const FatFile* ff = FatFile::isFatFile(memImage) ) {
|
||||
uint64_t sliceOffset;
|
||||
uint64_t sliceLen;
|
||||
bool missingSlice;
|
||||
if ( ff->isFatFileWithSlice(diag, memImageSize, gAllImages.archs(), sliceOffset, sliceLen, missingSlice) ) {
|
||||
mf = (MachOFile*)((long)memImage+sliceOffset);
|
||||
if ( mf->isMachO(diag, sliceLen) ) {
|
||||
usable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( usable ) {
|
||||
if ( !mf->supportsPlatform(Platform::macOS) )
|
||||
usable = false;
|
||||
}
|
||||
if ( !usable ) {
|
||||
log_apis("NSCreateObjectFileImageFromMemory() not mach-o\n");
|
||||
return NSObjectFileImageFailure;
|
||||
}
|
||||
|
||||
// this API can only be used with bundles
|
||||
if ( !mf->isBundle() ) {
|
||||
log_apis("NSCreateObjectFileImageFromMemory() not a bundle\n");
|
||||
return NSObjectFileImageInappropriateFile;
|
||||
}
|
||||
|
||||
// allocate ofi that just lists the memory range
|
||||
OFIInfo result;
|
||||
result.path = nullptr;
|
||||
result.memSource = memImage;
|
||||
result.memLength = memImageSize;
|
||||
result.loadAddress = nullptr;
|
||||
result.imageNum = 0;
|
||||
*ofi = gAllImages.addNSObjectFileImage(result);
|
||||
|
||||
log_apis("NSCreateObjectFileImageFromMemory() => %p\n", *ofi);
|
||||
|
||||
return NSObjectFileImageSuccess;
|
||||
}
|
||||
|
||||
NSModule NSLinkModule(NSObjectFileImage ofi, const char* moduleName, uint32_t options)
|
||||
{
|
||||
DYLD_LOAD_LOCK_THIS_BLOCK
|
||||
log_apis("NSLinkModule(%p, \"%s\", 0x%08X)\n", ofi, moduleName, options);
|
||||
|
||||
__block const char* path = nullptr;
|
||||
bool foundImage = gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
|
||||
// if this is memory based image, write to temp file, then use file based loading
|
||||
if ( image.memSource != nullptr ) {
|
||||
// make temp file with content of memory buffer
|
||||
image.path = nullptr;
|
||||
char tempFileName[PATH_MAX];
|
||||
const char* tmpDir = getenv("TMPDIR");
|
||||
if ( (tmpDir != nullptr) && (strlen(tmpDir) > 2) ) {
|
||||
strlcpy(tempFileName, tmpDir, PATH_MAX);
|
||||
if ( tmpDir[strlen(tmpDir)-1] != '/' )
|
||||
strlcat(tempFileName, "/", PATH_MAX);
|
||||
}
|
||||
else
|
||||
strlcpy(tempFileName,"/tmp/", PATH_MAX);
|
||||
strlcat(tempFileName, "NSCreateObjectFileImageFromMemory-XXXXXXXX", PATH_MAX);
|
||||
int fd = ::mkstemp(tempFileName);
|
||||
if ( fd != -1 ) {
|
||||
ssize_t writtenSize = ::pwrite(fd, image.memSource, image.memLength, 0);
|
||||
if ( writtenSize == image.memLength ) {
|
||||
image.path = strdup(tempFileName);
|
||||
}
|
||||
else {
|
||||
log_apis("NSLinkModule() => NULL (could not save memory image to temp file)\n");
|
||||
}
|
||||
::close(fd);
|
||||
}
|
||||
}
|
||||
path = image.path;
|
||||
});
|
||||
|
||||
if (!foundImage) {
|
||||
// ofi is invalid if not in list
|
||||
log_apis("NSLinkModule() => NULL (invalid NSObjectFileImage)\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!path)
|
||||
return nullptr;
|
||||
|
||||
// dlopen the binary outside of the read lock as we don't want to risk deadlock
|
||||
Diagnostics diag;
|
||||
void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
|
||||
const MachOLoaded* loadAddress = gAllImages.dlopen(diag, path, false, false, false, false, true, callerAddress);
|
||||
if ( diag.hasError() ) {
|
||||
log_apis(" NSLinkModule: failed: %s\n", diag.errorMessage());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Now update the load address of this object
|
||||
gAllImages.forNSObjectFileImage(ofi, ^(OFIInfo &image) {
|
||||
image.loadAddress = loadAddress;
|
||||
|
||||
// if memory based load, delete temp file
|
||||
if ( image.memSource != nullptr ) {
|
||||
log_apis(" NSLinkModule: delete temp file: %s\n", image.path);
|
||||
::unlink(image.path);
|
||||
}
|
||||
});
|
||||
|
||||
log_apis("NSLinkModule() => %p\n", loadAddress);
|
||||
return (NSModule)loadAddress;
|
||||
}
|
||||
|
||||
// NSUnLinkModule unmaps the image, but does not release the NSObjectFileImage
|
||||
bool NSUnLinkModule(NSModule module, uint32_t options)
|
||||
{
|
||||
DYLD_LOAD_LOCK_THIS_BLOCK
|
||||
log_apis("NSUnLinkModule(%p, 0x%08X)\n", module, options);
|
||||
|
||||
__block const mach_header* mh = nullptr;
|
||||
gAllImages.infoForImageMappedAt(module, ^(const LoadedImage& foundImage, uint8_t permissions) {
|
||||
mh = foundImage.loadedAddress();
|
||||
});
|
||||
|
||||
if ( mh != nullptr )
|
||||
gAllImages.decRefCount(mh); // removes image if reference count went to zero
|
||||
|
||||
log_apis("NSUnLinkModule() => %d\n", mh != nullptr);
|
||||
|
||||
return mh != nullptr;
|
||||
}
|
||||
|
||||
// NSDestroyObjectFileImage releases the NSObjectFileImage, but the mapped image may remain in use
|
||||
bool NSDestroyObjectFileImage(NSObjectFileImage imageHandle)
|
||||
{
|
||||
log_apis("NSDestroyObjectFileImage(%p)\n", imageHandle);
|
||||
|
||||
__block const void* memSource = nullptr;
|
||||
__block size_t memLength = 0;
|
||||
__block const char* path = nullptr;
|
||||
bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
|
||||
// keep copy of info
|
||||
memSource = image.memSource;
|
||||
memLength = image.memLength;
|
||||
path = image.path;
|
||||
});
|
||||
|
||||
if (!foundImage)
|
||||
return false;
|
||||
|
||||
// remove from list
|
||||
gAllImages.removeNSObjectFileImage(imageHandle);
|
||||
|
||||
// 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 imageHandle, const char* symbolName)
|
||||
{
|
||||
log_apis("NSIsSymbolDefinedInObjectFileImage(%p, %s)\n", imageHandle, symbolName);
|
||||
|
||||
__block bool hasSymbol = false;
|
||||
bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
|
||||
void* addr;
|
||||
bool resultPointsToInstructions = false;
|
||||
hasSymbol = image.loadAddress->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
|
||||
});
|
||||
|
||||
// ofi is invalid if not in list
|
||||
if (!foundImage)
|
||||
return false;
|
||||
|
||||
return hasSymbol;
|
||||
}
|
||||
|
||||
void* NSGetSectionDataInObjectFileImage(NSObjectFileImage imageHandle, const char* segmentName, const char* sectionName, size_t* size)
|
||||
{
|
||||
__block const void* result = nullptr;
|
||||
bool foundImage = gAllImages.forNSObjectFileImage(imageHandle, ^(OFIInfo &image) {
|
||||
uint64_t sz;
|
||||
result = image.loadAddress->findSectionContent(segmentName, sectionName, sz);
|
||||
*size = (size_t)sz;
|
||||
});
|
||||
|
||||
// ofi is invalid if not in list
|
||||
if (!foundImage)
|
||||
return nullptr;
|
||||
|
||||
return (void*)result;
|
||||
}
|
||||
|
||||
const char* NSNameOfModule(NSModule m)
|
||||
{
|
||||
log_apis("NSNameOfModule(%p)\n", m);
|
||||
|
||||
__block const char* result = nullptr;
|
||||
gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
|
||||
result = gAllImages.imagePath(foundImage.image());
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* NSLibraryNameForModule(NSModule m)
|
||||
{
|
||||
log_apis("NSLibraryNameForModule(%p)\n", m);
|
||||
|
||||
__block const char* result = nullptr;
|
||||
gAllImages.infoForImageMappedAt(m, ^(const LoadedImage& foundImage, uint8_t permissions) {
|
||||
result = gAllImages.imagePath(foundImage.image());
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static bool flatFindSymbol(const char* symbolName, void** symbolAddress, const mach_header** foundInImageAtLoadAddress)
|
||||
{
|
||||
__block bool result = false;
|
||||
gAllImages.forEachImage(^(const LoadedImage& loadedImage, bool& stop) {
|
||||
bool resultPointsToInstructions = false;
|
||||
if ( loadedImage.loadedAddress()->hasExportedSymbol(symbolName, nullptr, symbolAddress, &resultPointsToInstructions) ) {
|
||||
*foundInImageAtLoadAddress = loadedImage.loadedAddress();
|
||||
stop = true;
|
||||
result = true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
void* addr;
|
||||
bool resultPointsToInstructions = false;
|
||||
return ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const MachOLoaded* mh = (const MachOLoaded*)module;
|
||||
void* addr;
|
||||
bool resultPointsToInstructions = false;
|
||||
if ( mh->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
|
||||
return (NSSymbol)addr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NSSymbol NSLookupSymbolInImage(const mach_header* mh, const char* symbolName, uint32_t options)
|
||||
{
|
||||
log_apis("NSLookupSymbolInImage(%p, \"%s\", 0x%08X)\n", mh, symbolName, options);
|
||||
|
||||
void* addr;
|
||||
bool resultPointsToInstructions = false;
|
||||
if ( ((MachOLoaded*)mh)->hasExportedSymbol(symbolName, nullptr, &addr, &resultPointsToInstructions) ) {
|
||||
log_apis(" NSLookupSymbolInImage() => %p\n", addr);
|
||||
return (NSSymbol)addr;
|
||||
}
|
||||
if ( options & NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR ) {
|
||||
log_apis(" NSLookupSymbolInImage() => NULL\n");
|
||||
return nullptr;
|
||||
}
|
||||
// FIXME: abort();
|
||||
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);
|
||||
|
||||
__block NSModule result = nullptr;
|
||||
gAllImages.infoForImageMappedAt(symbol, ^(const LoadedImage& foundImage, uint8_t permissions) {
|
||||
result = (NSModule)foundImage.loadedAddress();
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
|
||||
return ( dlopen_internal(pathName, 0, callerAddress) != nullptr);
|
||||
}
|
||||
|
||||
bool NSAddLibraryWithSearching(const char* pathName)
|
||||
{
|
||||
log_apis("NSAddLibraryWithSearching(%s)\n", pathName);
|
||||
|
||||
void* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
|
||||
return ( dlopen_internal(pathName, 0, callerAddress) != 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* callerAddress = __builtin_return_address(1); // note layers: 1: real client, 0: libSystem glue
|
||||
void* h = dlopen_internal(imageName, dloptions, callerAddress);
|
||||
if ( h != nullptr ) {
|
||||
const MachOLoaded* 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
|
||||
|
1942
dyld3/AllImages.cpp
Normal file
1942
dyld3/AllImages.cpp
Normal file
File diff suppressed because it is too large
Load Diff
278
dyld3/AllImages.h
Normal file
278
dyld3/AllImages.h
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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 <mach-o/loader.h>
|
||||
#include <pthread.h>
|
||||
#include <os/lock_private.h>
|
||||
#include <mach-o/dyld_priv.h>
|
||||
|
||||
#include "Closure.h"
|
||||
#include "Loading.h"
|
||||
#include "MachOLoaded.h"
|
||||
#include "DyldSharedCache.h"
|
||||
|
||||
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
// only in macOS and deprecated
|
||||
struct VIS_HIDDEN OFIInfo
|
||||
{
|
||||
const char* path; // = nullptr;
|
||||
const void* memSource; // = nullptr;
|
||||
size_t memLength; // = 0;
|
||||
const dyld3::MachOLoaded* loadAddress; // = nullptr;
|
||||
uint64_t imageNum; // = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
class VIS_HIDDEN AllImages
|
||||
{
|
||||
public:
|
||||
typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide);
|
||||
typedef void (*LoadNotifyFunc)(const mach_header* mh, const char* path, bool unloadable);
|
||||
typedef void (*BulkLoadNotifier)(unsigned count, const mach_header* mhs[], const char* paths[]);
|
||||
|
||||
void init(const closure::LaunchClosure* closure, const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
|
||||
const Array<LoadedImage>& initialImages);
|
||||
void setRestrictions(bool allowAtPaths, bool allowEnvPaths);
|
||||
void setHasCacheOverrides(bool someCacheImageOverriden);
|
||||
bool hasCacheOverrides() const;
|
||||
void setMainPath(const char* path);
|
||||
void applyInitialImages();
|
||||
|
||||
void addImages(const Array<LoadedImage>& newImages);
|
||||
void removeImages(const Array<LoadedImage>& unloadImages);
|
||||
void runImageNotifiers(const Array<LoadedImage>& newImages);
|
||||
void runImageCallbacks(const Array<LoadedImage>& newImages);
|
||||
void applyInterposingToDyldCache(const closure::Closure* closure);
|
||||
void runStartupInitialzers();
|
||||
void runInitialzersBottomUp(const closure::Image* topImage);
|
||||
void runLibSystemInitializer(LoadedImage& libSystem);
|
||||
|
||||
uint32_t count() const;
|
||||
|
||||
void forEachImage(void (^handler)(const LoadedImage& loadedImage, bool& stop)) const;
|
||||
const MachOLoaded* findDependent(const MachOLoaded* mh, uint32_t depIndex);
|
||||
void visitDependentsTopDown(const LoadedImage& start, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const;
|
||||
void infoForImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const;
|
||||
bool infoForImageMappedAt(const void* addr, const MachOLoaded** ml, uint64_t* textSize, const char** path) const;
|
||||
void infoForNonCachedImageMappedAt(const void* addr, void (^handler)(const LoadedImage& foundImage, uint8_t permissions)) const;
|
||||
void infoForImageWithLoadAddress(const MachOLoaded*, void (^handler)(const LoadedImage& foundImage)) const;
|
||||
const char* pathForImageMappedAt(const void* addr) const;
|
||||
const char* imagePathByIndex(uint32_t index) const;
|
||||
const mach_header* imageLoadAddressByIndex(uint32_t index) const;
|
||||
bool immutableMemory(const void* addr, size_t length) const;
|
||||
void* interposeValue(void* value) const;
|
||||
|
||||
bool hasInsertedOrInterposingLibraries() const;
|
||||
bool isRestricted() const;
|
||||
const MachOLoaded* mainExecutable() const;
|
||||
const closure::Image* mainExecutableImage() const;
|
||||
const void* cacheLoadAddress() const { return _dyldCacheAddress; }
|
||||
const char* dyldCachePath() const { return _dyldCachePath; }
|
||||
bool dyldCacheHasPath(const char* path) const;
|
||||
const char* imagePath(const closure::Image*) const;
|
||||
dyld_platform_t platform() const;
|
||||
const GradedArchs& archs() const;
|
||||
|
||||
const Array<const closure::ImageArray*>& imagesArrays();
|
||||
|
||||
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 addLoadNotifier(LoadNotifyFunc);
|
||||
void addBulkLoadNotifier(BulkLoadNotifier);
|
||||
|
||||
|
||||
void setOldAllImageInfo(dyld_all_image_infos* old) { _oldAllImageInfos = old; }
|
||||
dyld_all_image_infos* oldAllImageInfo() const { return _oldAllImageInfos;}
|
||||
void notifyMonitorMain();
|
||||
void notifyMonitorLoads(const Array<LoadedImage>& newImages);
|
||||
void notifyMonitorUnloads(const Array<LoadedImage>& unloadingImages);
|
||||
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
NSObjectFileImage addNSObjectFileImage(const OFIInfo&);
|
||||
void removeNSObjectFileImage(NSObjectFileImage);
|
||||
bool forNSObjectFileImage(NSObjectFileImage imageHandle,
|
||||
void (^handler)(OFIInfo& image));
|
||||
#endif
|
||||
|
||||
const char* getObjCSelector(const char* selName) const;
|
||||
void forEachObjCClass(const char* className,
|
||||
void (^callback)(void* classPtr, bool isLoaded, bool* stop)) const;
|
||||
void forEachObjCProtocol(const char* protocolName,
|
||||
void (^callback)(void* protocolPtr, bool isLoaded, bool* stop)) const;
|
||||
|
||||
const MachOLoaded* dlopen(Diagnostics& diag, const char* path, bool rtldNoLoad, bool rtldLocal, bool rtldNoDelete, bool forceBindLazies, bool fromOFI, const void* callerAddress);
|
||||
|
||||
struct ProgramVars
|
||||
{
|
||||
const void* mh;
|
||||
int* NXArgcPtr;
|
||||
const char*** NXArgvPtr;
|
||||
const char*** environPtr;
|
||||
const char** __prognamePtr;
|
||||
};
|
||||
void setProgramVars(ProgramVars* vars);
|
||||
|
||||
// Note these are to be used exclusively by forking
|
||||
void takeLockBeforeFork();
|
||||
void releaseLockInForkParent();
|
||||
void resetLockInForkChild();
|
||||
|
||||
private:
|
||||
friend class Reaper;
|
||||
|
||||
struct DlopenCount {
|
||||
const mach_header* loadAddress;
|
||||
uintptr_t refCount;
|
||||
};
|
||||
|
||||
//
|
||||
// The ImmutableRanges structure is used to make dyld_is_memory_immutable()
|
||||
// fast and lock free. The table contains just ranges that are immutable,
|
||||
// which means they are non-writable and will never be unloaded.
|
||||
// This means the table is only every appended to. No entries are ever removed
|
||||
// or changed. This makes it easier to be lock-less. The array fields
|
||||
// all start as zero. Entries are only appended with the writer lock,
|
||||
// so we don't need to worry about multiple writers colliding. And when
|
||||
// appending, the end field is set before the start field. Readers
|
||||
// of this structure just walk down the array and quit at the first
|
||||
// start field that is zero.
|
||||
//
|
||||
struct ImmutableRanges {
|
||||
std::atomic<ImmutableRanges*> next;
|
||||
uintptr_t arraySize;
|
||||
struct {
|
||||
std::atomic<uintptr_t> start;
|
||||
std::atomic<uintptr_t> end;
|
||||
} array[2]; // programs with only main-exe and dyld cache fit in here
|
||||
};
|
||||
|
||||
const MachOLoaded* loadImage(Diagnostics& diag, closure::ImageNum topImageNum, const closure::DlopenClosure* newClosure,
|
||||
bool rtldLocal, bool rtldNoDelete, bool rtldNow, bool fromOFI);
|
||||
|
||||
|
||||
typedef void (*Initializer)(int argc, const char* argv[], char* envp[], const char* apple[], const ProgramVars* vars);
|
||||
typedef const Array<LoadedImage> StartImageArray;
|
||||
|
||||
void runInitialzersInImage(const mach_header* imageLoadAddress, const closure::Image* image);
|
||||
void mirrorToOldAllImageInfos();
|
||||
void garbageCollectImages();
|
||||
void breadthFirstRecurseDependents(Array<closure::ImageNum>& visited, const LoadedImage& nodeLi, bool& stop, void (^handler)(const LoadedImage& aLoadedImage, bool& stop)) const;
|
||||
void appendToImagesArray(const closure::ImageArray* newArray);
|
||||
void withReadLock(void (^work)()) const;
|
||||
void withWriteLock(void (^work)());
|
||||
void withNotifiersLock(void (^work)()) const;
|
||||
bool findImage(const mach_header* loadAddress, LoadedImage& foundImage) const;
|
||||
bool findImageNum(closure::ImageNum imageNum, LoadedImage& foundImage) const;
|
||||
LoadedImage findImageNum(closure::ImageNum num, uint32_t& indexHint);
|
||||
bool swapImageState(closure::ImageNum num, uint32_t& indexHint, LoadedImage::State expectedCurrentState, LoadedImage::State newState);
|
||||
void runAllInitializersInImage(const closure::Image* image, const MachOLoaded* ml);
|
||||
void recomputeBounds();
|
||||
void runAllStaticTerminators();
|
||||
uintptr_t resolveTarget(closure::Image::ResolvedSymbolTarget target) const;
|
||||
void addImmutableRange(uintptr_t start, uintptr_t end);
|
||||
|
||||
void constructMachPorts(int slot);
|
||||
void teardownMachPorts(int slot);
|
||||
void forEachPortSlot(void (^callback)(int slot));
|
||||
void sendMachMessage(int slot, mach_msg_id_t msg_id, mach_msg_header_t* msg_buffer, mach_msg_size_t msg_size);
|
||||
void notifyMonitoringDyld(bool unloading, const Array<LoadedImage>& images);
|
||||
|
||||
static void runAllStaticTerminatorsHelper(void*);
|
||||
|
||||
typedef closure::ImageArray ImageArray;
|
||||
|
||||
const closure::LaunchClosure* _mainClosure = nullptr;
|
||||
const DyldSharedCache* _dyldCacheAddress = nullptr;
|
||||
const char* _dyldCachePath = nullptr;
|
||||
uint64_t _dyldCacheSlide = 0;
|
||||
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;
|
||||
dyld_uuid_info* _oldUUIDArray = nullptr;
|
||||
const GradedArchs* _archs = nullptr;
|
||||
ImmutableRanges _immutableRanges = { nullptr, 2 };
|
||||
uint32_t _oldArrayAllocCount = 0;
|
||||
uint32_t _oldUUIDAllocCount = 0;
|
||||
closure::ImageNum _nextImageNum = 0;
|
||||
int32_t _gcCount = 0;
|
||||
bool _processDOFs = false;
|
||||
bool _allowAtPaths = false;
|
||||
bool _allowEnvPaths = false;
|
||||
bool _someImageOverridden = false;
|
||||
uintptr_t _lowestNonCached = 0;
|
||||
uintptr_t _highestNonCached = UINTPTR_MAX;
|
||||
#ifdef OS_UNFAIR_RECURSIVE_LOCK_INIT
|
||||
mutable os_unfair_recursive_lock _globalLock = OS_UNFAIR_RECURSIVE_LOCK_INIT;
|
||||
#else
|
||||
mutable pthread_mutex_t _globalLock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
|
||||
#endif
|
||||
GrowableArray<const ImageArray*, 4, 4> _imagesArrays;
|
||||
GrowableArray<NotifyFunc, 4, 4> _loadNotifiers;
|
||||
GrowableArray<NotifyFunc, 4, 4> _unloadNotifiers;
|
||||
GrowableArray<LoadNotifyFunc, 4, 4> _loadNotifiers2;
|
||||
GrowableArray<BulkLoadNotifier, 2, 2> _loadBulkNotifiers;
|
||||
GrowableArray<DlopenCount, 4, 4> _dlopenRefCounts;
|
||||
GrowableArray<LoadedImage, 16> _loadedImages;
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
uint64_t _nextObjectFileImageNum = 0;
|
||||
GrowableArray<OFIInfo, 4, 1> _objectFileImages;
|
||||
#endif
|
||||
|
||||
// ObjC selectors
|
||||
// This is an array of the base addresses of sections containing selector strings
|
||||
GrowableArray<uintptr_t, 4, 4> _objcSelectorHashTableImages;
|
||||
const closure::ObjCSelectorOpt* _objcSelectorHashTable = nullptr;
|
||||
|
||||
// ObjC classes
|
||||
// This is an array of the base addresses of (name vmaddr, data vmaddr) pairs of sections in each image
|
||||
GrowableArray<std::pair<uintptr_t, uintptr_t>, 4, 4> _objcClassHashTableImages;
|
||||
const closure::ObjCClassOpt* _objcClassHashTable = nullptr;
|
||||
const closure::ObjCClassDuplicatesOpt* _objcClassDuplicatesHashTable = nullptr;
|
||||
const closure::ObjCClassOpt* _objcProtocolHashTable = nullptr;
|
||||
const objc_opt::objc_opt_t* _dyldCacheObjCOpt = nullptr;
|
||||
};
|
||||
|
||||
extern AllImages gAllImages;
|
||||
|
||||
|
||||
} // dyld3
|
||||
|
||||
|
||||
#endif // __ALL_IMAGES_H__
|
322
dyld3/Array.h
Normal file
322
dyld3/Array.h
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* 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 Array_h
|
||||
#define Array_h
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <mach/mach.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if !TARGET_OS_DRIVERKIT && (BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
#include <CrashReporterClient.h>
|
||||
#else
|
||||
#define CRSetCrashLogMessage(x)
|
||||
#define CRSetCrashLogMessage2(x)
|
||||
#endif
|
||||
|
||||
#define VIS_HIDDEN __attribute__((visibility("hidden")))
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
|
||||
//
|
||||
// Similar to std::vector<> but storage is pre-allocated and cannot be re-allocated.
|
||||
// Storage is normally stack allocated.
|
||||
//
|
||||
// Use push_back() to add elements and range based for loops to iterate and [] to access by index.
|
||||
//
|
||||
template <typename T>
|
||||
class VIS_HIDDEN Array
|
||||
{
|
||||
public:
|
||||
Array() : _elements(nullptr), _allocCount(0), _usedCount(0) {}
|
||||
Array(T* storage, uintptr_t allocCount, uintptr_t usedCount=0) : _elements(storage), _allocCount(allocCount), _usedCount(usedCount) {}
|
||||
void setInitialStorage(T* storage, uintptr_t allocCount) { assert(_usedCount == 0); _elements=storage; _allocCount=allocCount; }
|
||||
|
||||
T& operator[](size_t idx) { assert(idx < _usedCount); return _elements[idx]; }
|
||||
const T& operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
|
||||
T& back() { assert(_usedCount > 0); return _elements[_usedCount-1]; }
|
||||
uintptr_t count() const { return _usedCount; }
|
||||
uintptr_t maxCount() const { return _allocCount; }
|
||||
uintptr_t freeCount() const { return _allocCount - _usedCount; }
|
||||
bool empty() const { return (_usedCount == 0); }
|
||||
uintptr_t index(const T& element) { return &element - _elements; }
|
||||
void push_back(const T& t) { assert(_usedCount < _allocCount); _elements[_usedCount++] = t; }
|
||||
void default_constuct_back() { assert(_usedCount < _allocCount); new (&_elements[_usedCount++])T(); }
|
||||
void pop_back() { assert(_usedCount > 0); _usedCount--; }
|
||||
T* begin() { return &_elements[0]; }
|
||||
T* end() { return &_elements[_usedCount]; }
|
||||
const T* begin() const { return &_elements[0]; }
|
||||
const T* end() const { return &_elements[_usedCount]; }
|
||||
const Array<T> subArray(uintptr_t start, uintptr_t size) const { assert(start+size <= _usedCount);
|
||||
return Array<T>(&_elements[start], size, size); }
|
||||
bool contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
|
||||
void remove(size_t idx) { assert(idx < _usedCount); ::memmove(&_elements[idx], &_elements[idx+1], sizeof(T)*(_usedCount-idx-1)); }
|
||||
|
||||
protected:
|
||||
T* _elements;
|
||||
uintptr_t _allocCount;
|
||||
uintptr_t _usedCount;
|
||||
};
|
||||
|
||||
|
||||
// If an Array<>.setInitialStorage() is used, the array may out live the stack space of the storage.
|
||||
// To allow cleanup to be done to array elements when the stack goes away, you can make a local
|
||||
// variable of ArrayFinalizer<>.
|
||||
template <typename T>
|
||||
class VIS_HIDDEN ArrayFinalizer
|
||||
{
|
||||
public:
|
||||
typedef void (^CleanUp)(T& element);
|
||||
ArrayFinalizer(Array<T>& array, CleanUp handler) : _array(array), _handler(handler) { }
|
||||
~ArrayFinalizer() { for(T& element : _array) _handler(element); }
|
||||
private:
|
||||
Array<T>& _array;
|
||||
CleanUp _handler;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Similar to Array<> but if the array overflows, it is re-allocated using vm_allocate().
|
||||
// When the variable goes out of scope, any vm_allocate()ed storage is released.
|
||||
// if MAXCOUNT is specified, then only one one vm_allocate() to that size is done.
|
||||
//
|
||||
template <typename T, uintptr_t MAXCOUNT=0xFFFFFFFF>
|
||||
class VIS_HIDDEN OverflowSafeArray : public Array<T>
|
||||
{
|
||||
public:
|
||||
OverflowSafeArray() : Array<T>(nullptr, 0) {}
|
||||
OverflowSafeArray(T* stackStorage, uintptr_t stackAllocCount) : Array<T>(stackStorage, stackAllocCount) {}
|
||||
~OverflowSafeArray();
|
||||
|
||||
OverflowSafeArray(OverflowSafeArray&) = default;
|
||||
OverflowSafeArray& operator=(OverflowSafeArray&& other);
|
||||
|
||||
void push_back(const T& t) { verifySpace(1); this->_elements[this->_usedCount++] = t; }
|
||||
void default_constuct_back() { verifySpace(1); new (&this->_elements[this->_usedCount++])T(); }
|
||||
void clear() { this->_usedCount = 0; }
|
||||
void reserve(uintptr_t n) { if (this->_allocCount < n) growTo(n); }
|
||||
void resize(uintptr_t n) {
|
||||
if (n == this->_usedCount)
|
||||
return;
|
||||
if (n < this->_usedCount) {
|
||||
this->_usedCount = n;
|
||||
return;
|
||||
}
|
||||
reserve(n);
|
||||
this->_usedCount = n;
|
||||
}
|
||||
|
||||
protected:
|
||||
void growTo(uintptr_t n);
|
||||
void verifySpace(uintptr_t n) { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
|
||||
|
||||
private:
|
||||
vm_address_t _overflowBuffer = 0;
|
||||
vm_size_t _overflowBufferSize = 0;
|
||||
};
|
||||
|
||||
|
||||
template <typename T, uintptr_t MAXCOUNT>
|
||||
inline void OverflowSafeArray<T,MAXCOUNT>::growTo(uintptr_t n)
|
||||
{
|
||||
vm_address_t oldBuffer = _overflowBuffer;
|
||||
vm_size_t oldBufferSize = _overflowBufferSize;
|
||||
if ( MAXCOUNT != 0xFFFFFFFF ) {
|
||||
assert(oldBufferSize == 0); // only re-alloc once
|
||||
// MAXCOUNT is specified, so immediately jump to that size
|
||||
_overflowBufferSize = round_page(std::max(MAXCOUNT, n) * sizeof(T));
|
||||
}
|
||||
else {
|
||||
// MAXCOUNT is not specified, keep doubling size
|
||||
_overflowBufferSize = round_page(std::max(this->_allocCount * 2, n) * sizeof(T));
|
||||
}
|
||||
kern_return_t kr = ::vm_allocate(mach_task_self(), &_overflowBuffer, _overflowBufferSize, VM_FLAGS_ANYWHERE);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
#if BUILDING_LIBDYLD
|
||||
//FIXME We should figure out a way to do this in dyld
|
||||
char crashString[256];
|
||||
snprintf(crashString, 256, "OverflowSafeArray failed to allocate %lu bytes, vm_allocate returned: %d\n",
|
||||
_overflowBufferSize, kr);
|
||||
CRSetCrashLogMessage(crashString);
|
||||
#endif
|
||||
assert(0);
|
||||
}
|
||||
::memcpy((void*)_overflowBuffer, this->_elements, this->_usedCount*sizeof(T));
|
||||
this->_elements = (T*)_overflowBuffer;
|
||||
this->_allocCount = _overflowBufferSize / sizeof(T);
|
||||
|
||||
if ( oldBuffer != 0 )
|
||||
::vm_deallocate(mach_task_self(), oldBuffer, oldBufferSize);
|
||||
}
|
||||
|
||||
template <typename T, uintptr_t MAXCOUNT>
|
||||
inline OverflowSafeArray<T,MAXCOUNT>::~OverflowSafeArray()
|
||||
{
|
||||
if ( _overflowBuffer != 0 )
|
||||
::vm_deallocate(mach_task_self(), _overflowBuffer, _overflowBufferSize);
|
||||
}
|
||||
|
||||
template <typename T, uintptr_t MAXCOUNT>
|
||||
inline OverflowSafeArray<T,MAXCOUNT>& OverflowSafeArray<T,MAXCOUNT>::operator=(OverflowSafeArray<T,MAXCOUNT>&& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
// Free our buffer if we have one
|
||||
if ( _overflowBuffer != 0 )
|
||||
::vm_deallocate(mach_task_self(), _overflowBuffer, _overflowBufferSize);
|
||||
|
||||
// Now take the buffer from the other array
|
||||
this->_elements = other._elements;
|
||||
this->_allocCount = other._allocCount;
|
||||
this->_usedCount = other._usedCount;
|
||||
_overflowBuffer = other._overflowBuffer;
|
||||
_overflowBufferSize = other._overflowBufferSize;
|
||||
|
||||
// Now reset the other object so that it doesn't try to deallocate the memory later.
|
||||
other._elements = nullptr;
|
||||
other._allocCount = 0;
|
||||
other._usedCount = 0;
|
||||
other._overflowBuffer = 0;
|
||||
other._overflowBufferSize = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if BUILDING_LIBDYLD
|
||||
//
|
||||
// Similar to std::vector<> but storage is initially allocated in the object. But if it needs to
|
||||
// grow beyond, it will use malloc. The QUANT template arg is the "quantum" size for allocations.
|
||||
// When the allocation needs to be grown, it is re-allocated at the required size rounded up to
|
||||
// the next quantum.
|
||||
//
|
||||
// Use push_back() to add elements and range based for loops to iterate and [] to access by index.
|
||||
//
|
||||
// Note: this should be a subclass of Array<T> but doing so disables the compiler from optimizing away static constructors
|
||||
//
|
||||
template <typename T, int QUANT=4, int INIT=1>
|
||||
class VIS_HIDDEN GrowableArray
|
||||
{
|
||||
public:
|
||||
|
||||
T& operator[](size_t idx) { assert(idx < _usedCount); return _elements[idx]; }
|
||||
const T& operator[](size_t idx) const { assert(idx < _usedCount); return _elements[idx]; }
|
||||
T& back() { assert(_usedCount > 0); return _elements[_usedCount-1]; }
|
||||
uintptr_t count() const { return _usedCount; }
|
||||
uintptr_t maxCount() const { return _allocCount; }
|
||||
bool empty() const { return (_usedCount == 0); }
|
||||
uintptr_t index(const T& element) { return &element - _elements; }
|
||||
void push_back(const T& t) { verifySpace(1); _elements[_usedCount++] = t; }
|
||||
void append(const Array<T>& a);
|
||||
void pop_back() { assert(_usedCount > 0); _usedCount--; }
|
||||
T* begin() { return &_elements[0]; }
|
||||
T* end() { return &_elements[_usedCount]; }
|
||||
const T* begin() const { return &_elements[0]; }
|
||||
const T* end() const { return &_elements[_usedCount]; }
|
||||
const Array<T> subArray(uintptr_t start, uintptr_t size) const { assert(start+size <= _usedCount);
|
||||
return Array<T>(&_elements[start], size, size); }
|
||||
const Array<T>& array() const { return *((Array<T>*)this); }
|
||||
bool contains(const T& targ) const { for (const T& a : *this) { if ( a == targ ) return true; } return false; }
|
||||
void erase(T& targ);
|
||||
|
||||
protected:
|
||||
void growTo(uintptr_t n);
|
||||
void verifySpace(uintptr_t n) { if (this->_usedCount+n > this->_allocCount) growTo(this->_usedCount + n); }
|
||||
|
||||
private:
|
||||
T* _elements = _initialAlloc;
|
||||
uintptr_t _allocCount = INIT;
|
||||
uintptr_t _usedCount = 0;
|
||||
T _initialAlloc[INIT] = { };
|
||||
};
|
||||
|
||||
|
||||
template <typename T, int QUANT, int INIT>
|
||||
inline void GrowableArray<T,QUANT,INIT>::growTo(uintptr_t n)
|
||||
{
|
||||
uintptr_t newCount = (n + QUANT - 1) & (-QUANT);
|
||||
T* newArray = (T*)::malloc(sizeof(T)*newCount);
|
||||
T* oldArray = this->_elements;
|
||||
if ( this->_usedCount != 0 )
|
||||
::memcpy(newArray, oldArray, sizeof(T)*this->_usedCount);
|
||||
this->_elements = newArray;
|
||||
this->_allocCount = newCount;
|
||||
if ( oldArray != this->_initialAlloc )
|
||||
::free(oldArray);
|
||||
}
|
||||
|
||||
template <typename T, int QUANT, int INIT>
|
||||
inline void GrowableArray<T,QUANT,INIT>::append(const Array<T>& a)
|
||||
{
|
||||
verifySpace(a.count());
|
||||
::memcpy(&_elements[_usedCount], a.begin(), a.count()*sizeof(T));
|
||||
_usedCount += a.count();
|
||||
}
|
||||
|
||||
template <typename T, int QUANT, int INIT>
|
||||
inline void GrowableArray<T,QUANT,INIT>::erase(T& targ)
|
||||
{
|
||||
intptr_t index = &targ - _elements;
|
||||
assert(index >= 0);
|
||||
assert(index < (intptr_t)_usedCount);
|
||||
intptr_t moveCount = _usedCount-index-1;
|
||||
if ( moveCount > 0 )
|
||||
::memcpy(&_elements[index], &_elements[index+1], moveCount*sizeof(T));
|
||||
_usedCount -= 1;
|
||||
}
|
||||
|
||||
#endif // BUILDING_LIBDYLD
|
||||
|
||||
|
||||
|
||||
// STACK_ALLOC_ARRAY(foo, myarray, 10);
|
||||
// myarray is of type Array<foo>
|
||||
#define STACK_ALLOC_ARRAY(_type, _name, _count) \
|
||||
uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
|
||||
__block dyld3::Array<_type> _name((_type*)__##_name##_array_alloc, _count);
|
||||
|
||||
|
||||
// STACK_ALLOC_OVERFLOW_SAFE_ARRAY(foo, myarray, 10);
|
||||
// myarray is of type OverflowSafeArray<foo>
|
||||
#define STACK_ALLOC_OVERFLOW_SAFE_ARRAY(_type, _name, _count) \
|
||||
uintptr_t __##_name##_array_alloc[1 + ((sizeof(_type)*(_count))/sizeof(uintptr_t))]; \
|
||||
__block dyld3::OverflowSafeArray<_type> _name((_type*)__##_name##_array_alloc, _count);
|
||||
|
||||
|
||||
// work around compiler bug where:
|
||||
// __block type name[count];
|
||||
// is not accessible in a block
|
||||
#define BLOCK_ACCCESSIBLE_ARRAY(_type, _name, _count) \
|
||||
_type __##_name##_array_alloc[_count]; \
|
||||
_type* _name = __##_name##_array_alloc;
|
||||
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* Array_h */
|
82
dyld3/BootArgs.cpp
Normal file
82
dyld3/BootArgs.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
//
|
||||
// BootArgs.cpp
|
||||
// dyld
|
||||
//
|
||||
// Created by Louis Gerbarg on 11/14/18.
|
||||
//
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include "Loading.h" // For internalInstall()
|
||||
#include "BootArgs.h"
|
||||
|
||||
namespace dyld3 {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
bool BootArgs::contains(const char* arg)
|
||||
{
|
||||
//FIXME: Use strnstr(). Unfortunately we are missing an imp in libc.a
|
||||
#if TARGET_OS_SIMULATOR
|
||||
return false;
|
||||
#else
|
||||
// don't check for boot-args on customer installs
|
||||
if ( !internalInstall() )
|
||||
return false;
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
uint64_t BootArgs::_flags = 0;
|
||||
|
||||
bool BootArgs::forceCustomerCache() {
|
||||
return (_flags & kForceCustomerCacheMask);
|
||||
}
|
||||
|
||||
bool BootArgs::forceDyld2() {
|
||||
// If both force dyld2 and dyld3 are set then use dyld3
|
||||
if (_flags & kForceDyld3CacheMask) { return false; }
|
||||
if (_flags & kForceDyld2CacheMask) { return true; }
|
||||
if (contains("force_dyld2=1")) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BootArgs::forceDyld3() {
|
||||
return ((_flags & kForceDyld3CacheMask) || contains("force_dyld3=1"));
|
||||
}
|
||||
|
||||
bool BootArgs::enableDyldTestMode() {
|
||||
return (_flags & kDyldTestModeMask);
|
||||
}
|
||||
|
||||
bool BootArgs::enableCompactImageInfo() {
|
||||
return (_flags & kEnableCompactImageInfoMask);
|
||||
}
|
||||
|
||||
void BootArgs::setFlags(uint64_t flags) {
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
return;
|
||||
#else
|
||||
// don't check for boot-args on customer installs
|
||||
if ( !internalInstall() )
|
||||
return;
|
||||
_flags = flags;
|
||||
#endif
|
||||
}
|
||||
};
|
38
dyld3/BootArgs.h
Normal file
38
dyld3/BootArgs.h
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// BootArgs.hpp
|
||||
// dyld
|
||||
//
|
||||
// Created by Louis Gerbarg on 11/14/18.
|
||||
//
|
||||
|
||||
#ifndef __DYLD_BOOTARGS_H__
|
||||
#define __DYLD_BOOTARGS_H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#define VIS_HIDDEN __attribute__((visibility("hidden")))
|
||||
|
||||
namespace dyld3 {
|
||||
#if BUILDING_DYLD
|
||||
struct VIS_HIDDEN BootArgs {
|
||||
static bool contains(const char* arg);
|
||||
static bool forceCustomerCache();
|
||||
static bool forceDyld2();
|
||||
static bool forceDyld3();
|
||||
static bool enableDyldTestMode();
|
||||
static bool enableCompactImageInfo();
|
||||
static void setFlags(uint64_t flags);
|
||||
private:
|
||||
static const uint64_t kForceCustomerCacheMask = 1<<0;
|
||||
static const uint64_t kDyldTestModeMask = 1<<1;
|
||||
static const uint64_t kForceDyld2CacheMask = 1<<15;
|
||||
static const uint64_t kForceDyld3CacheMask = 1<<16;
|
||||
static const uint64_t kEnableCompactImageInfoMask = 1<<17;
|
||||
//FIXME: Move this into __DATA_CONST once it is enabled for dyld
|
||||
static uint64_t _flags;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* __DYLD_BOOTARGS_H__ */
|
1517
dyld3/Closure.cpp
Normal file
1517
dyld3/Closure.cpp
Normal file
File diff suppressed because it is too large
Load Diff
957
dyld3/Closure.h
Normal file
957
dyld3/Closure.h
Normal file
@ -0,0 +1,957 @@
|
||||
/*
|
||||
* 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 Closures_h
|
||||
#define Closures_h
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach-o/loader.h>
|
||||
|
||||
#include "Diagnostics.h"
|
||||
#include "Array.h"
|
||||
#include "MachOLoaded.h"
|
||||
#include "SupportedArchs.h"
|
||||
|
||||
namespace objc_opt {
|
||||
struct objc_opt_t;
|
||||
}
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
|
||||
|
||||
// bump this number each time binary format changes
|
||||
enum { kFormatVersion = 10 };
|
||||
|
||||
|
||||
typedef uint32_t ImageNum;
|
||||
|
||||
const ImageNum kFirstDyldCacheImageNum = 0x00000001;
|
||||
const ImageNum kLastDyldCacheImageNum = 0x00000FFF;
|
||||
const ImageNum kFirstOtherOSImageNum = 0x00001001;
|
||||
const ImageNum kLastOtherOSImageNum = 0x00001FFF;
|
||||
const ImageNum kFirstLaunchClosureImageNum = 0x00002000;
|
||||
const ImageNum kMissingWeakLinkedImage = 0x0FFFFFFF;
|
||||
|
||||
class ObjCSelectorOpt;
|
||||
class ObjCClassOpt;
|
||||
class ObjCClassDuplicatesOpt;
|
||||
|
||||
//
|
||||
// Generic typed range of bytes (similar to load commands)
|
||||
// Must be 4-byte aligned
|
||||
//
|
||||
struct VIS_HIDDEN TypedBytes
|
||||
{
|
||||
enum class Type : uint32_t {
|
||||
// containers which have an overall length and TypedBytes inside their content
|
||||
launchClosure = 1, // contains TypedBytes of closure attributes including imageArray
|
||||
imageArray = 2, // sizeof(ImageArray) + sizeof(uint32_t)*count + size of all images
|
||||
image = 3, // contains TypedBytes of image attributes
|
||||
dlopenClosure = 4, // contains TypedBytes of closure attributes including imageArray
|
||||
|
||||
// attributes for Images
|
||||
imageFlags = 7, // sizeof(Image::Flags)
|
||||
pathWithHash = 8, // len = uint32_t + length path + 1, use multiple entries for aliases
|
||||
fileInodeAndTime = 9, // sizeof(FileInfo)
|
||||
cdHash = 10, // 20, use multiple entries on watchOS for all hashes
|
||||
uuid = 11, // 16
|
||||
mappingInfo = 12, // sizeof(MappingInfo)
|
||||
diskSegment = 13, // sizeof(DiskSegment) * count
|
||||
cacheSegment = 14, // sizeof(DyldCacheSegment) * count
|
||||
dependents = 15, // sizeof(LinkedImage) * count
|
||||
initOffsets = 16, // sizeof(uint32_t) * count
|
||||
dofOffsets = 17, // sizeof(uint32_t) * count
|
||||
codeSignLoc = 18, // sizeof(CodeSignatureLocation)
|
||||
fairPlayLoc = 19, // sizeof(FairPlayRange)
|
||||
rebaseFixups = 20, // sizeof(RebasePattern) * count
|
||||
bindFixups = 21, // sizeof(BindPattern) * count
|
||||
cachePatchInfo = 22, // deprecated
|
||||
textFixups = 23, // sizeof(TextFixupPattern) * count
|
||||
imageOverride = 24, // sizeof(ImageNum)
|
||||
initBefores = 25, // sizeof(ImageNum) * count
|
||||
initsSection = 26, // sizeof(InitializerSectionRange)
|
||||
chainedFixupsTargets = 27, // sizeof(ResolvedSymbolTarget) * count
|
||||
termOffsets = 28, // sizeof(uint32_t) * count
|
||||
chainedStartsOffset = 29, // sizeof(uint64_t)
|
||||
objcFixups = 30, // sizeof(ResolvedSymbolTarget) + (sizeof(uint32_t) * 2) + (sizeof(ProtocolISAFixup) * count) + (sizeof(SelectorReferenceFixup) * count)
|
||||
|
||||
// attributes for Closures (launch or dlopen)
|
||||
closureFlags = 32, // sizeof(Closure::Flags)
|
||||
dyldCacheUUID = 33, // 16
|
||||
missingFiles = 34,
|
||||
envVar = 35, // "DYLD_BLAH=stuff"
|
||||
topImage = 36, // sizeof(ImageNum)
|
||||
libDyldEntry = 37, // sizeof(ResolvedSymbolTarget)
|
||||
libSystemNum = 38, // sizeof(ImageNum)
|
||||
bootUUID = 39, // c-string 40
|
||||
mainEntry = 40, // sizeof(ResolvedSymbolTarget)
|
||||
startEntry = 41, // sizeof(ResolvedSymbolTarget) // used by programs built with crt1.o
|
||||
cacheOverrides = 42, // sizeof(PatchEntry) * count // used if process uses interposing or roots (cached dylib overrides)
|
||||
interposeTuples = 43, // sizeof(InterposingTuple) * count
|
||||
existingFiles = 44, // uint64_t + (SkippedFiles * count)
|
||||
selectorTable = 45, // uint32_t + (sizeof(ObjCSelectorImage) * count) + hashTable size
|
||||
classTable = 46, // (3 * uint32_t) + (sizeof(ObjCClassImage) * count) + classHashTable size + protocolHashTable size
|
||||
warning = 47, // len = uint32_t + length path + 1, use one entry per warning
|
||||
duplicateClassesTable = 48, // duplicateClassesHashTable
|
||||
};
|
||||
|
||||
Type type : 8;
|
||||
uint32_t payloadLength : 24;
|
||||
|
||||
const void* payload() const;
|
||||
void* payload();
|
||||
};
|
||||
|
||||
static_assert(sizeof(TypedBytes) == 4, "Wrong size for TypedBytes");
|
||||
|
||||
|
||||
//
|
||||
// A TypedBytes which is a bag of other TypedBytes
|
||||
//
|
||||
struct VIS_HIDDEN ContainerTypedBytes : TypedBytes
|
||||
{
|
||||
void forEachAttribute(void (^callback)(const TypedBytes* typedBytes, bool& stop)) const;
|
||||
void forEachAttributePayload(Type requestedType, void (^handler)(const void* payload, uint32_t size, bool& stop)) const;
|
||||
const void* findAttributePayload(Type requestedType, uint32_t* payloadSize=nullptr) const;
|
||||
private:
|
||||
const TypedBytes* first() const;
|
||||
const TypedBytes* next(const TypedBytes*) const;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Information about a mach-o file
|
||||
//
|
||||
struct VIS_HIDDEN Image : ContainerTypedBytes
|
||||
{
|
||||
enum class LinkKind { regular=0, weak=1, upward=2, reExport=3 };
|
||||
|
||||
size_t size() const;
|
||||
ImageNum imageNum() const;
|
||||
bool representsImageNum(ImageNum num) const; // imageNum() or isOverrideOfDyldCacheImage()
|
||||
uint32_t maxLoadCount() const;
|
||||
const char* path() const;
|
||||
const char* leafName() const;
|
||||
bool getUuid(uuid_t) const;
|
||||
bool isInvalid() const;
|
||||
bool inDyldCache() const;
|
||||
bool hasObjC() const;
|
||||
bool hasInitializers() const;
|
||||
bool hasPrecomputedObjC() const;
|
||||
bool hasTerminators() const;
|
||||
bool hasReadOnlyData() const;
|
||||
bool hasChainedFixups() const;
|
||||
bool isBundle() const;
|
||||
bool isDylib() const;
|
||||
bool isExecutable() const;
|
||||
bool hasWeakDefs() const;
|
||||
bool mayHavePlusLoads() const;
|
||||
bool is64() const;
|
||||
bool neverUnload() const;
|
||||
bool cwdMustBeThisDir() const;
|
||||
bool isPlatformBinary() const;
|
||||
bool overridableDylib() const;
|
||||
bool hasFileModTimeAndInode(uint64_t& inode, uint64_t& mTime) const;
|
||||
void forEachCDHash(void (^handler)(const uint8_t cdHash[20], bool& stop)) const;
|
||||
void forEachAlias(void (^handler)(const char* aliasPath, bool& stop)) const;
|
||||
void forEachDependentImage(void (^handler)(uint32_t dependentIndex, LinkKind kind, ImageNum imageNum, bool& stop)) const;
|
||||
ImageNum dependentImageNum(uint32_t depIndex) const;
|
||||
bool containsAddress(const void* addr, const void* imageLoadAddress, uint8_t* permissions=nullptr) const;
|
||||
bool forEachInitializerSection(void (^handler)(uint32_t sectionOffset, uint32_t sectionSize)) const;
|
||||
void forEachInitializer(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
|
||||
void forEachTerminator(const void* imageLoadAddress, void (^handler)(const void* terminator)) const;
|
||||
void forEachImageToInitBefore(void (^handler)(ImageNum imageToInit, bool& stop)) const;
|
||||
void forEachDOF(const void* imageLoadAddress, void (^handler)(const void* initializer)) const;
|
||||
bool hasPathWithHash(const char* path, uint32_t hash) const;
|
||||
bool isOverrideOfDyldCacheImage(ImageNum& cacheImageNum) const;
|
||||
uint64_t textSize() const;
|
||||
|
||||
union ResolvedSymbolTarget
|
||||
{
|
||||
enum Kinds { kindRebase, kindSharedCache, kindImage, kindAbsolute };
|
||||
|
||||
struct Rebase {
|
||||
uint64_t kind : 2, // kindRebase
|
||||
unused : 62; // all zeros
|
||||
};
|
||||
struct SharedCache {
|
||||
uint64_t kind : 2; // kindSharedCache
|
||||
int64_t offset : 62;
|
||||
};
|
||||
struct Image {
|
||||
uint64_t kind : 2, // kindImage
|
||||
imageNum : 22; // ImageNum
|
||||
int64_t offset : 40;
|
||||
};
|
||||
struct Absolute {
|
||||
uint64_t kind : 2, // kindAbsolute
|
||||
value : 62; // sign extended
|
||||
};
|
||||
Rebase rebase;
|
||||
SharedCache sharedCache;
|
||||
Image image;
|
||||
Absolute absolute;
|
||||
uint64_t raw;
|
||||
|
||||
bool operator==(const ResolvedSymbolTarget& rhs) const {
|
||||
return (raw == rhs.raw);
|
||||
}
|
||||
bool operator!=(const ResolvedSymbolTarget& rhs) const {
|
||||
return (raw != rhs.raw);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ResolvedSymbolTarget) == 8);
|
||||
|
||||
|
||||
// ObjC optimisations
|
||||
struct ObjCImageOffset {
|
||||
union {
|
||||
uint32_t raw = 0;
|
||||
struct {
|
||||
uint32_t imageIndex : 8;
|
||||
uint32_t imageOffset : 24;
|
||||
};
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
// The unused value so that we have a sentinel in our hash table
|
||||
sentinelValue = 0xFFFFFFFF,
|
||||
|
||||
// The maximum image index
|
||||
maximumImageIndex = (1U << 8) - 1,
|
||||
|
||||
// The maximum offset from the start of the strings section
|
||||
maximumOffset = (1U << 24) - 1
|
||||
};
|
||||
};
|
||||
|
||||
struct ObjCClassNameImageOffset {
|
||||
union {
|
||||
uint32_t raw = 0;
|
||||
struct {
|
||||
uint32_t classNameImageIndex : 8;
|
||||
uint32_t classNameImageOffset : 24;
|
||||
};
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
// The unused value so that we have a sentinel in our hash table
|
||||
sentinelValue = 0xFFFFFFFF,
|
||||
|
||||
// The maximum image index
|
||||
maximumImageIndex = (1U << 8) - 1,
|
||||
|
||||
// The maximum offset from the start of the strings section
|
||||
maximumOffset = (1U << 24) - 1
|
||||
};
|
||||
};
|
||||
|
||||
struct ObjCClassImageOffset {
|
||||
union {
|
||||
uint32_t raw = 0;
|
||||
struct {
|
||||
uint32_t imageIndex : 8;
|
||||
uint32_t imageOffset : 23;
|
||||
uint32_t isDuplicate : 1; // == 0
|
||||
} classData;
|
||||
struct {
|
||||
uint32_t count : 8;
|
||||
uint32_t index : 23;
|
||||
uint32_t isDuplicate : 1; // == 1
|
||||
} duplicateData;
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
// The unused value so that we have a sentinel in our hash table
|
||||
sentinelValue = 0xFFFFFFFF,
|
||||
|
||||
// The maximum image index
|
||||
maximumImageIndex = (1U << 8) - 1,
|
||||
|
||||
// The maximum offset from the start of the class data section
|
||||
maximumOffset = (1U << 23) - 1
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(ObjCClassImageOffset) == 4, "Invalid size");
|
||||
|
||||
static_assert(ObjCClassNameImageOffset::maximumImageIndex == ObjCClassImageOffset::maximumImageIndex , "Invalid indices");
|
||||
|
||||
struct ObjCDuplicateClass {
|
||||
union {
|
||||
uint32_t raw = 0;
|
||||
struct {
|
||||
uint32_t sharedCacheClassOptIndex : 20;
|
||||
uint32_t sharedCacheClassDuplicateIndex : 12;
|
||||
};
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
// The unused value so that we have a sentinel in our hash table
|
||||
sentinelValue = 0xFFFFFFFF
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(ObjCDuplicateClass) == 4, "Invalid size");
|
||||
|
||||
struct ObjCSelectorImage {
|
||||
ImageNum imageNum;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct ObjCClassImage {
|
||||
ImageNum imageNum;
|
||||
uint32_t offsetOfClassNames;
|
||||
uint32_t offsetOfClasses;
|
||||
};
|
||||
|
||||
typedef MachOLoaded::ChainedFixupPointerOnDisk ChainedFixupPointerOnDisk;
|
||||
|
||||
// the following are only valid if inDyldCache() returns true
|
||||
uint32_t cacheOffset() const;
|
||||
uint32_t patchStartIndex() const;
|
||||
uint32_t patchCount() const;
|
||||
void forEachCacheSegment(void (^handler)(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop)) const;
|
||||
|
||||
|
||||
// the following are only valid if inDyldCache() returns false
|
||||
uint64_t vmSizeToMap() const;
|
||||
uint64_t sliceOffsetInFile() const;
|
||||
bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
|
||||
bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
|
||||
void forEachDiskSegment(void (^handler)(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize,
|
||||
uint8_t permissions, bool laterReadOnly, bool& stop)) const;
|
||||
void forEachFixup(void (^rebase)(uint64_t imageOffsetToRebase, bool& stop),
|
||||
void (^bind)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
|
||||
void (^chainedFixups)(uint64_t imageOffsetToStarts, const Array<ResolvedSymbolTarget>& targets, bool& stop),
|
||||
void (^fixupObjCImageInfo)(uint64_t imageOffsetToFixup),
|
||||
void (^fixupObjCProtocol)(uint64_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop),
|
||||
void (^fixupObjCSelRef)(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool& stop),
|
||||
void (^fixupObjCStableSwift)(uint64_t imageOffsetToFixup, bool& stop),
|
||||
void (^fixupObjCMethodList)(uint64_t imageOffsetToFixup, bool& stop)) const;
|
||||
void forEachTextReloc(void (^rebase)(uint32_t imageOffsetToRebase, bool& stop),
|
||||
void (^bind)(uint32_t imageOffsetToBind, ResolvedSymbolTarget bindTarget, bool& stop)) const;
|
||||
|
||||
static_assert(sizeof(ResolvedSymbolTarget) == 8, "Overflow in size of SymbolTargetLocation");
|
||||
|
||||
static uint32_t hashFunction(const char*);
|
||||
|
||||
private:
|
||||
friend struct Closure;
|
||||
friend class ImageWriter;
|
||||
friend class ClosureBuilder;
|
||||
friend class ClosureWriter;
|
||||
friend class LaunchClosureWriter;
|
||||
|
||||
uint32_t pageSize() const;
|
||||
|
||||
struct Flags
|
||||
{
|
||||
uint64_t imageNum : 16,
|
||||
maxLoadCount : 12,
|
||||
isInvalid : 1, // an error occurred creating the info for this image
|
||||
has16KBpages : 1,
|
||||
is64 : 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,
|
||||
isDylib : 1,
|
||||
isExecutable : 1,
|
||||
overridableDylib : 1, // only applicable to cached dylibs
|
||||
inDyldCache : 1,
|
||||
hasTerminators : 1,
|
||||
hasReadOnlyData : 1,
|
||||
hasChainedFixups : 1,
|
||||
hasPrecomputedObjC : 1,
|
||||
padding : 17;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Flags) == sizeof(uint64_t), "Flags overflow");
|
||||
|
||||
const Flags& getFlags() const;
|
||||
|
||||
struct PathAndHash
|
||||
{
|
||||
uint32_t hash;
|
||||
char path[];
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
// We only have three bits for permissions, and need a way to
|
||||
// encode segments that are initially r/w then made read-only
|
||||
// after fixups are done. Previously, the only valid patterns
|
||||
// were: ---, r--, rw-, r-x. We now add -w- to mean r/w -> r/o.
|
||||
enum { kReadOnlyDataPermissions = VM_PROT_WRITE };
|
||||
};
|
||||
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
struct CodeSignatureLocation
|
||||
{
|
||||
uint32_t fileOffset;
|
||||
uint32_t fileSize;
|
||||
};
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
uint64_t inode;
|
||||
uint64_t modTime;
|
||||
};
|
||||
|
||||
struct FairPlayRange
|
||||
{
|
||||
uint32_t rangeStart; // The byte offset of the start of the range.
|
||||
uint32_t rangeLength; // How long is the fairplay range in bytes
|
||||
};
|
||||
|
||||
struct MappingInfo
|
||||
{
|
||||
uint32_t totalVmPages;
|
||||
uint32_t sliceOffsetIn4K;
|
||||
};
|
||||
|
||||
struct InitializerSectionRange
|
||||
{
|
||||
uint32_t sectionOffset;
|
||||
uint32_t sectionSize;
|
||||
};
|
||||
|
||||
struct LinkedImage {
|
||||
|
||||
LinkedImage() : imgNum(0), linkKind(0) {
|
||||
}
|
||||
LinkedImage(LinkKind k, ImageNum num) : imgNum(num), linkKind((uint32_t)k) {
|
||||
assert((num & 0xC0000000) == 0);
|
||||
}
|
||||
|
||||
LinkKind kind() const { return (LinkKind)linkKind; }
|
||||
ImageNum imageNum() const { return imgNum; }
|
||||
void clearKind() { linkKind = 0; }
|
||||
|
||||
bool operator==(const LinkedImage& rhs) const {
|
||||
return (linkKind == rhs.linkKind) && (imgNum == rhs.imgNum);
|
||||
}
|
||||
bool operator!=(const LinkedImage& rhs) const {
|
||||
return (linkKind != rhs.linkKind) || (imgNum != rhs.imgNum);
|
||||
}
|
||||
private:
|
||||
uint32_t imgNum : 30,
|
||||
linkKind : 2; // LinkKind
|
||||
};
|
||||
|
||||
const Array<LinkedImage> dependentsArray() const;
|
||||
|
||||
struct RebasePattern
|
||||
{
|
||||
uint32_t repeatCount : 20,
|
||||
contigCount : 8, // how many contiguous pointers neeed rebasing
|
||||
skipCount : 4; // how many pointers to skip between contig groups
|
||||
// If contigCount == 0, then there are no rebases for this entry,
|
||||
// instead it advances the rebase location by repeatCount*skipCount.
|
||||
// If all fields are zero, then the rebase position is reset to the start.
|
||||
// This is to support old binaries with some non-monotonically-increasing rebases.
|
||||
};
|
||||
const Array<RebasePattern> rebaseFixups() const;
|
||||
|
||||
struct BindPattern
|
||||
{
|
||||
Image::ResolvedSymbolTarget target;
|
||||
uint64_t startVmOffset : 40, // max 1TB offset
|
||||
skipCount : 8,
|
||||
repeatCount : 16;
|
||||
};
|
||||
const Array<BindPattern> bindFixups() const;
|
||||
|
||||
// An optimzied selector reference bind will either point to the shared cache
|
||||
// or a binary optimized in our launch closure. We can use the index in to each
|
||||
// of their respective selector hash tables as the target or the bind.
|
||||
union SelectorReferenceFixup
|
||||
{
|
||||
uint32_t chainStartVMOffset;
|
||||
struct {
|
||||
uint32_t index : 24, // max 16m entries in the table
|
||||
next : 7, // (next * 4) -> offset to next fixup
|
||||
inSharedCache : 1; // 0 -> in the closure. 1 -> in the cache
|
||||
} chainEntry;
|
||||
};
|
||||
|
||||
struct ProtocolISAFixup
|
||||
{
|
||||
uint64_t startVmOffset : 40, // max 1TB offset
|
||||
skipCount : 8,
|
||||
repeatCount : 16;
|
||||
};
|
||||
|
||||
struct ClassStableSwiftFixup
|
||||
{
|
||||
uint64_t startVmOffset : 40, // max 1TB offset
|
||||
skipCount : 8,
|
||||
repeatCount : 16;
|
||||
};
|
||||
|
||||
struct MethodListFixup
|
||||
{
|
||||
uint64_t startVmOffset : 40, // max 1TB offset
|
||||
skipCount : 8,
|
||||
repeatCount : 16;
|
||||
};
|
||||
|
||||
void objcFixups(ResolvedSymbolTarget& objcProtocolClassTarget,
|
||||
uint64_t& objcImageInfoVMOffset,
|
||||
Array<ProtocolISAFixup>& protocolISAFixups,
|
||||
Array<SelectorReferenceFixup>& selRefFixups,
|
||||
Array<ClassStableSwiftFixup>& classStableSwiftFixups,
|
||||
Array<MethodListFixup>& methodListFixups) const;
|
||||
|
||||
struct TextFixupPattern
|
||||
{
|
||||
Image::ResolvedSymbolTarget target;
|
||||
uint32_t startVmOffset;
|
||||
uint16_t repeatCount;
|
||||
uint16_t skipCount;
|
||||
};
|
||||
const Array<TextFixupPattern> textFixups() const;
|
||||
|
||||
// for use with chained fixups
|
||||
uint64_t chainedStartsOffset() const;
|
||||
const Array<Image::ResolvedSymbolTarget> chainedTargets() const;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
Dyld cache patching notes:
|
||||
|
||||
The dyld cache needs to be patched to support interposing and dylib "roots".
|
||||
|
||||
For cached dylibs overrides:
|
||||
Closure build time:
|
||||
1) LoadedImages will contain the new dylib, so all symbol look ups
|
||||
will naturally find new impl. Only dyld cache needs special help.
|
||||
2) LoadedImages entry will have flag for images that override cache.
|
||||
3) When setting Closure attributes, if flag is set, builder will
|
||||
iterate PatchableExport entries in Image* from cache and create
|
||||
a PatchEntry for each.
|
||||
Runtime:
|
||||
1) [lib]dyld will iterate PatchEntry in closure and patch cache
|
||||
|
||||
For interposing:
|
||||
Closure build time:
|
||||
1) After Images built, if __interpose section(s) exist, builder will
|
||||
build InterposingTuple entries for closure
|
||||
2) For being-built closure and launch closure, apply any InterposingTuple
|
||||
to modify Image fixups before Image finalized.
|
||||
3) Builder will find PatchableExport entry that matchs stock Impl
|
||||
and add PatchEntry to closure for it.
|
||||
Runtime:
|
||||
1) When closure is loaded (launch or dlopen) PatchEntries are
|
||||
applied (exactly once) to whole cache.
|
||||
2) For each DlopenClosure loaded, any InterposeTuples in *launch* closure
|
||||
are applied to all new images in new DlopenClosure.
|
||||
|
||||
For weak-def coalesing:
|
||||
Closure build time:
|
||||
1) weak_bind entries are turned into -3 ordinal lookup which search through images
|
||||
in load order for first def (like flat). This fixups up all images not in cache.
|
||||
2) When processing -3 ordinals, it continues past first found and if any images
|
||||
past it are in dyld cache and export that same symbol, a PatchEntry is added to
|
||||
closure to fix up all cached uses of that symbol.
|
||||
3) If a weak_bind has strong bit set (no fixup, just def), all images from the dyld
|
||||
cache are checked to see if the export that symbol, if so, a PatchEntry is added
|
||||
to the closure.
|
||||
Runtime:
|
||||
1) When closure is loaded (launch or dlopen) PatchEntries are
|
||||
applied (exactly once) to whole cache.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// An array (accessible by index) list of Images
|
||||
//
|
||||
struct VIS_HIDDEN ImageArray : public TypedBytes
|
||||
{
|
||||
size_t size() const;
|
||||
size_t startImageNum() const;
|
||||
uint32_t imageCount() const;
|
||||
void forEachImage(void (^callback)(const Image* image, bool& stop)) const;
|
||||
bool hasPath(const char* path, ImageNum& num) const;
|
||||
const Image* imageForNum(ImageNum) const;
|
||||
void deallocate() const;
|
||||
|
||||
static const Image* findImage(const Array<const ImageArray*> imagesArrays, ImageNum imageNum);
|
||||
|
||||
private:
|
||||
friend class ImageArrayWriter;
|
||||
|
||||
uint32_t firstImageNum;
|
||||
uint32_t count : 31;
|
||||
uint32_t hasRoots : 1; // True if this ImageArray contains roots with imageNum's below firstImageNum
|
||||
uint32_t offsets[];
|
||||
// Image data
|
||||
};
|
||||
|
||||
|
||||
struct InterposingTuple
|
||||
{
|
||||
Image::ResolvedSymbolTarget stockImplementation;
|
||||
Image::ResolvedSymbolTarget newImplementation;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Describes how dyld should load a set of mach-o files
|
||||
//
|
||||
struct VIS_HIDDEN Closure : public ContainerTypedBytes
|
||||
{
|
||||
size_t size() const;
|
||||
const ImageArray* images() const;
|
||||
ImageNum topImage() const;
|
||||
void deallocate() const;
|
||||
|
||||
friend class ClosureWriter;
|
||||
|
||||
struct PatchEntry
|
||||
{
|
||||
ImageNum overriddenDylibInCache;
|
||||
uint32_t exportCacheOffset;
|
||||
Image::ResolvedSymbolTarget replacement;
|
||||
};
|
||||
|
||||
struct Warning
|
||||
{
|
||||
enum Type : uint32_t {
|
||||
duplicateObjCClass = 0
|
||||
};
|
||||
|
||||
Type type;
|
||||
char message[];
|
||||
};
|
||||
|
||||
void forEachPatchEntry(void (^handler)(const PatchEntry& entry)) const;
|
||||
void forEachWarning(Warning::Type type, void (^handler)(const char* warning, bool& stop)) const;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Describes how dyld should launch a main executable
|
||||
//
|
||||
struct VIS_HIDDEN LaunchClosure : public Closure
|
||||
{
|
||||
// Represents a file on disk we tried to load but skipped as it wasn't valid for our use
|
||||
struct SkippedFile
|
||||
{
|
||||
const char* path;
|
||||
uint64_t inode;
|
||||
uint64_t mtime;
|
||||
};
|
||||
|
||||
bool builtAgainstDyldCache(uuid_t cacheUUID) const;
|
||||
const char* bootUUID() const;
|
||||
void forEachMustBeMissingFile(void (^handler)(const char* path, bool& stop)) const;
|
||||
void forEachSkipIfExistsFile(void (^handler)(const SkippedFile& file, bool& stop)) const;
|
||||
void forEachEnvVar(void (^handler)(const char* keyEqualValue, bool& stop)) const;
|
||||
ImageNum libSystemImageNum() const;
|
||||
void libDyldEntry(Image::ResolvedSymbolTarget& loc) const;
|
||||
bool mainEntry(Image::ResolvedSymbolTarget& mainLoc) const;
|
||||
bool startEntry(Image::ResolvedSymbolTarget& startLoc) const;
|
||||
uint32_t initialLoadCount() const;
|
||||
void forEachInterposingTuple(void (^handler)(const InterposingTuple& tuple, bool& stop)) const;
|
||||
bool usedAtPaths() const;
|
||||
bool usedFallbackPaths() const;
|
||||
bool selectorHashTable(Array<Image::ObjCSelectorImage>& imageNums,
|
||||
const ObjCSelectorOpt*& hashTable) const;
|
||||
bool classAndProtocolHashTables(Array<Image::ObjCClassImage>& imageNums,
|
||||
const ObjCClassOpt*& classHashTable,
|
||||
const ObjCClassOpt*& protocolHashTable) const;
|
||||
void duplicateClassesHashTable(const ObjCClassDuplicatesOpt*& duplicateClassesHashTable) const;
|
||||
bool hasInsertedLibraries() const;
|
||||
bool hasInterposings() const;
|
||||
|
||||
static bool buildClosureCachePath(const char* mainExecutablePath, char closurePath[], const char* tempDir,
|
||||
bool makeDirsIfMissing);
|
||||
|
||||
private:
|
||||
friend class LaunchClosureWriter;
|
||||
|
||||
struct Flags
|
||||
{
|
||||
uint32_t usedAtPaths : 1,
|
||||
usedFallbackPaths : 1,
|
||||
initImageCount : 16,
|
||||
hasInsertedLibraries : 1,
|
||||
padding : 13;
|
||||
};
|
||||
const Flags& getFlags() const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Describes how dyld should dlopen() a mach-o file
|
||||
//
|
||||
struct VIS_HIDDEN DlopenClosure : public Closure
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
// Precomputed perfect hash table of strings.
|
||||
// Base class for closure precomputed selector table and class table.
|
||||
class VIS_HIDDEN ObjCStringTable {
|
||||
public:
|
||||
typedef uint32_t StringTarget;
|
||||
|
||||
private:
|
||||
typedef uint8_t StringHashCheckByte;
|
||||
|
||||
protected:
|
||||
|
||||
uint32_t capacity;
|
||||
uint32_t occupied;
|
||||
uint32_t shift;
|
||||
uint32_t mask;
|
||||
StringTarget sentinelTarget;
|
||||
uint32_t roundedTabSize;
|
||||
uint64_t salt;
|
||||
|
||||
uint32_t scramble[256];
|
||||
uint8_t tab[0]; /* tab[mask+1] (always power-of-2). Rounded up to roundedTabSize */
|
||||
// uint8_t checkbytes[capacity]; /* check byte for each string */
|
||||
// int32_t offsets[capacity]; /* offsets from &capacity to cstrings */
|
||||
|
||||
StringHashCheckByte* checkBytesOffset() {
|
||||
return &tab[roundedTabSize];
|
||||
}
|
||||
|
||||
const StringHashCheckByte* checkBytesOffset() const {
|
||||
return &tab[roundedTabSize];
|
||||
}
|
||||
|
||||
StringTarget* targetsOffset() {
|
||||
return (StringTarget*)(checkBytesOffset() + capacity);
|
||||
}
|
||||
|
||||
const StringTarget* targetsOffset() const {
|
||||
return (StringTarget*)(checkBytesOffset() + capacity);
|
||||
}
|
||||
|
||||
dyld3::Array<StringHashCheckByte> checkBytes() {
|
||||
return dyld3::Array<StringHashCheckByte>((StringHashCheckByte *)checkBytesOffset(), capacity, capacity);
|
||||
}
|
||||
const dyld3::Array<StringHashCheckByte> checkBytes() const {
|
||||
return dyld3::Array<StringHashCheckByte>((StringHashCheckByte *)checkBytesOffset(), capacity, capacity);
|
||||
}
|
||||
|
||||
dyld3::Array<StringTarget> targets() {
|
||||
return dyld3::Array<StringTarget>((StringTarget *)targetsOffset(), capacity, capacity);
|
||||
}
|
||||
const dyld3::Array<StringTarget> targets() const {
|
||||
return dyld3::Array<StringTarget>((StringTarget *)targetsOffset(), capacity, capacity);
|
||||
}
|
||||
|
||||
uint32_t hash(const char *key, size_t keylen) const;
|
||||
|
||||
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.
|
||||
StringHashCheckByte checkbyte(const char *key, size_t keylen) const
|
||||
{
|
||||
return ((key[0] & 0x7) << 5) | ((uint8_t)keylen & 0x1f);
|
||||
}
|
||||
|
||||
StringHashCheckByte checkbyte(const char *key) const
|
||||
{
|
||||
return checkbyte(key, strlen(key));
|
||||
}
|
||||
|
||||
StringTarget getPotentialTarget(const char *key) const
|
||||
{
|
||||
uint32_t index = getIndex(key);
|
||||
if (index == indexNotFound)
|
||||
return sentinelTarget;
|
||||
return targets()[index];
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
enum : uint32_t {
|
||||
indexNotFound = ~0U
|
||||
};
|
||||
|
||||
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
|
||||
StringHashCheckByte h_check = checkBytes()[h];
|
||||
StringHashCheckByte key_check = checkbyte(key, keylen);
|
||||
if (h_check != key_check)
|
||||
return indexNotFound;
|
||||
return h;
|
||||
}
|
||||
|
||||
template<typename PerfectHashT>
|
||||
static size_t size(const PerfectHashT& phash) {
|
||||
// Round tab[] to at least 4 in length to ensure the uint32_t's after are aligned
|
||||
uint32_t roundedTabSize = std::max(phash.mask+1, 4U);
|
||||
size_t tableSize = 0;
|
||||
tableSize += sizeof(ObjCStringTable);
|
||||
tableSize += roundedTabSize;
|
||||
tableSize += phash.capacity * sizeof(StringHashCheckByte);
|
||||
tableSize += phash.capacity * sizeof(StringTarget);
|
||||
return tableSize;
|
||||
}
|
||||
|
||||
// Get a string if it has an entry in the table
|
||||
const char* getString(const char* selName, const Array<uintptr_t>& baseAddresses) const;
|
||||
|
||||
void forEachString(const Array<Image::ObjCSelectorImage>& selectorImages,
|
||||
void (^callback)(uint64_t selVMOffset, ImageNum imageNum)) const;
|
||||
|
||||
template<typename PerfectHashT, typename ImageOffsetT>
|
||||
void write(const PerfectHashT& phash, const Array<std::pair<const char*, ImageOffsetT>>& strings);
|
||||
};
|
||||
|
||||
class VIS_HIDDEN ObjCSelectorOpt : public ObjCStringTable {
|
||||
public:
|
||||
// Get a string if it has an entry in the table
|
||||
// Returns true if an entry is found and sets the imageNum and vmOffset.
|
||||
bool getStringLocation(uint32_t index, const Array<closure::Image::ObjCSelectorImage>& selImages,
|
||||
ImageNum& imageNum, uint64_t& vmOffset) const;
|
||||
|
||||
void forEachString(const Array<Image::ObjCSelectorImage>& selectorImages,
|
||||
void (^callback)(uint64_t selVMOffset, ImageNum imageNum)) const;
|
||||
};
|
||||
|
||||
class VIS_HIDDEN ObjCClassOpt : public ObjCStringTable {
|
||||
private:
|
||||
// ...ObjCStringTable fields...
|
||||
// ClassTarget classOffsets[capacity]; /* offsets from &capacity to class_t and header_info */
|
||||
// DuplicateCount duplicateCount;
|
||||
// ClassTarget duplicateOffsets[duplicatedClasses];
|
||||
|
||||
typedef uint32_t DuplicateCount;
|
||||
typedef Image::ObjCClassImageOffset ClassTarget;
|
||||
|
||||
ClassTarget *classOffsetsStart() { return (ClassTarget *)&targetsOffset()[capacity]; }
|
||||
const ClassTarget *classOffsetsStart() const { return (const ClassTarget *)&targetsOffset()[capacity]; }
|
||||
|
||||
dyld3::Array<ClassTarget> classOffsets() {
|
||||
return dyld3::Array<ClassTarget>((ClassTarget *)classOffsetsStart(), capacity, capacity);
|
||||
}
|
||||
const dyld3::Array<ClassTarget> classOffsets() const {
|
||||
return dyld3::Array<ClassTarget>((ClassTarget *)classOffsetsStart(), capacity, capacity);
|
||||
}
|
||||
|
||||
DuplicateCount& duplicateCount() { return *(DuplicateCount *)&classOffsetsStart()[capacity]; }
|
||||
const DuplicateCount& duplicateCount() const { return *(const DuplicateCount *)&classOffsetsStart()[capacity]; }
|
||||
|
||||
ClassTarget *duplicateOffsetsStart() { return (ClassTarget *)(&duplicateCount()+1); }
|
||||
const ClassTarget *duplicateOffsetsStart() const { return (const ClassTarget *)(&duplicateCount()+1); }
|
||||
|
||||
dyld3::Array<ClassTarget> duplicateOffsets(uint32_t preCalculatedDuplicateCount) {
|
||||
return dyld3::Array<ClassTarget>((ClassTarget *)duplicateOffsetsStart(), preCalculatedDuplicateCount, duplicateCount());
|
||||
}
|
||||
const dyld3::Array<ClassTarget> duplicateOffsets(uint32_t preCalculatedDuplicateCount) const {
|
||||
return dyld3::Array<ClassTarget>((ClassTarget *)duplicateOffsetsStart(), preCalculatedDuplicateCount, duplicateCount());
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
template<typename PerfectHashT>
|
||||
static size_t size(PerfectHashT& phash, uint32_t duplicateCount)
|
||||
{
|
||||
size_t tableSize = 0;
|
||||
tableSize += ObjCStringTable::size(phash);
|
||||
tableSize += phash.capacity * sizeof(ClassTarget);
|
||||
tableSize += sizeof(DuplicateCount);
|
||||
tableSize += duplicateCount * sizeof(ClassTarget);
|
||||
return tableSize;
|
||||
}
|
||||
|
||||
void forEachClass(const char* className,
|
||||
const Array<std::pair<uintptr_t, uintptr_t>>& nameAndDataBaseAddresses,
|
||||
void (^callback)(void* classPtr, bool isLoaded, bool* stop)) const;
|
||||
|
||||
void forEachClass(const Array<Image::ObjCClassImage>& classImages,
|
||||
void (^nameCallback)(uint64_t classNameVMOffset, ImageNum imageNum),
|
||||
void (^implCallback)(uint64_t classVMOffset, ImageNum imageNum)) const;
|
||||
|
||||
template<typename PerfectHashT, typename ImageOffsetT, typename ClassesMapT>
|
||||
void write(const PerfectHashT& phash, const Array<std::pair<const char*, ImageOffsetT>>& strings,
|
||||
const ClassesMapT& classes, uint32_t preCalculatedDuplicateCount);
|
||||
};
|
||||
|
||||
class VIS_HIDDEN ObjCClassDuplicatesOpt : public ObjCStringTable {
|
||||
public:
|
||||
// Get a class if it has an entry in the table
|
||||
bool getClassLocation(const char* className, const objc_opt::objc_opt_t* objCOpt, void*& classImpl) const;
|
||||
|
||||
void forEachClass(void (^callback)(Image::ObjCDuplicateClass duplicateClass)) const;
|
||||
};
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif // Closures_h
|
||||
|
||||
|
3791
dyld3/ClosureBuilder.cpp
Normal file
3791
dyld3/ClosureBuilder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
365
dyld3/ClosureBuilder.h
Normal file
365
dyld3/ClosureBuilder.h
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
* 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 ClosureBuilder_h
|
||||
#define ClosureBuilder_h
|
||||
|
||||
|
||||
#include "Closure.h"
|
||||
#include "ClosureFileSystem.h"
|
||||
#include "ClosureWriter.h"
|
||||
#include "PathOverrides.h"
|
||||
#include "DyldSharedCache.h"
|
||||
#include "MachOAnalyzer.h"
|
||||
#include "Map.h"
|
||||
#include "Loading.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace objc_opt {
|
||||
struct objc_clsopt_t;
|
||||
struct objc_selopt_t;
|
||||
struct objc_protocolopt2_t;
|
||||
}
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
|
||||
namespace closure {
|
||||
|
||||
|
||||
|
||||
class VIS_HIDDEN ClosureBuilder
|
||||
{
|
||||
public:
|
||||
|
||||
struct LaunchErrorInfo
|
||||
{
|
||||
uintptr_t kind;
|
||||
const char* clientOfDylibPath;
|
||||
const char* targetDylibPath;
|
||||
const char* symbol;
|
||||
};
|
||||
|
||||
struct ResolvedTargetInfo
|
||||
{
|
||||
const MachOLoaded* foundInDylib;
|
||||
const char* requestedSymbolName;
|
||||
const char* foundSymbolName;
|
||||
uint64_t addend;
|
||||
bool weakBindCoalese;
|
||||
bool weakBindSameImage;
|
||||
bool isWeakDef;
|
||||
bool skippableWeakDef;
|
||||
int libOrdinal;
|
||||
};
|
||||
|
||||
struct CacheDylibsBindingHandlers
|
||||
{
|
||||
void (^rebase)(ImageNum, const MachOLoaded* imageToFix, uint32_t runtimeOffset);
|
||||
void (^bind)(ImageNum, const MachOLoaded* imageToFix, uint32_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo);
|
||||
void (^chainedBind)(ImageNum, const MachOLoaded*, const dyld_chained_starts_in_image* starts, const Array<Image::ResolvedSymbolTarget>& targets, const Array<ResolvedTargetInfo>& targetInfos);
|
||||
};
|
||||
|
||||
enum class AtPath { none, all, onlyInRPaths };
|
||||
|
||||
ClosureBuilder(uint32_t startImageNum, const FileSystem& fileSystem, const DyldSharedCache* dyldCache, bool dyldCacheIsLive,
|
||||
const GradedArchs& archs, const PathOverrides& pathOverrides,
|
||||
AtPath atPathHandling=AtPath::all, bool allowRelativePaths=true, LaunchErrorInfo* errorInfo=nullptr,
|
||||
Platform platform=MachOFile::currentPlatform(), const CacheDylibsBindingHandlers* handlers=nullptr);
|
||||
~ClosureBuilder();
|
||||
Diagnostics& diagnostics() { return _diag; }
|
||||
|
||||
const LaunchClosure* makeLaunchClosure(const LoadedFileInfo& fileInfo, bool allowInsertFailures);
|
||||
|
||||
const LaunchClosure* makeLaunchClosure(const char* mainPath,bool allowInsertFailures);
|
||||
|
||||
|
||||
static const DlopenClosure* sRetryDlopenClosure;
|
||||
const DlopenClosure* makeDlopenClosure(const char* dylibPath, const LaunchClosure* mainClosure, const Array<LoadedImage>& loadedList,
|
||||
closure::ImageNum callerImageNum, bool noLoad, bool forceBindLazies, bool canUseSharedCacheClosure,
|
||||
closure::ImageNum* topImageNum);
|
||||
|
||||
ImageNum nextFreeImageNum() const { return _startImageNum + _nextIndex; }
|
||||
|
||||
void setDyldCacheInvalidFormatVersion();
|
||||
|
||||
|
||||
struct PatchableExport
|
||||
{
|
||||
uint32_t cacheOffsetOfImpl;
|
||||
uint32_t cacheOffsetOfName;
|
||||
uint32_t patchLocationsCount;
|
||||
const dyld_cache_patchable_location* patchLocations;
|
||||
};
|
||||
|
||||
struct CachedDylibInfo
|
||||
{
|
||||
LoadedFileInfo fileInfo;
|
||||
};
|
||||
|
||||
struct CachedDylibAlias
|
||||
{
|
||||
const char* realPath;
|
||||
const char* aliasPath;
|
||||
};
|
||||
|
||||
const ImageArray* makeDyldCacheImageArray(bool customerCache, const Array<CachedDylibInfo>& dylibs, const Array<CachedDylibAlias>& aliases);
|
||||
|
||||
const ImageArray* makeOtherDylibsImageArray(const Array<LoadedFileInfo>& otherDylibs, uint32_t cachedDylibsCount);
|
||||
|
||||
static void buildLoadOrder(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Closure* toAdd);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
struct InitInfo
|
||||
{
|
||||
uint32_t initOrder;
|
||||
bool danglingUpward;
|
||||
bool visited;
|
||||
};
|
||||
|
||||
struct BuilderLoadedImage
|
||||
{
|
||||
Array<Image::LinkedImage> dependents;
|
||||
ImageNum imageNum;
|
||||
uint32_t unmapWhenDone : 1,
|
||||
contentRebased : 1,
|
||||
hasInits : 1,
|
||||
markNeverUnload : 1,
|
||||
rtldLocal : 1,
|
||||
isBadImage : 1,
|
||||
mustBuildClosure : 1,
|
||||
hasMissingWeakImports : 1,
|
||||
padding : 12,
|
||||
overrideImageNum : 12;
|
||||
LoadedFileInfo loadedFileInfo;
|
||||
|
||||
// Convenience method to get the information from the loadedFileInfo
|
||||
const MachOAnalyzer* loadAddress() const { return (const MachOAnalyzer*)loadedFileInfo.fileContent; }
|
||||
const char* path() const { return loadedFileInfo.path; }
|
||||
};
|
||||
|
||||
struct LoadedImageChain
|
||||
{
|
||||
LoadedImageChain* previous;
|
||||
BuilderLoadedImage& image;
|
||||
};
|
||||
|
||||
// Represents how the current image is linked
|
||||
enum class LinkageType {
|
||||
kStatic, // Linked via LC_LOAD_* by main executable or other dylib
|
||||
kDynamic, // Only used for the image we dlopen'ed, not anything it links
|
||||
kInserted // This is an inserted library
|
||||
};
|
||||
|
||||
typedef LaunchClosure::SkippedFile SkippedFile;
|
||||
|
||||
|
||||
void recursiveLoadDependents(LoadedImageChain& forImageChain, bool canUseSharedCacheClosure = true);
|
||||
void loadDanglingUpwardLinks(bool canUseSharedCacheClosure = true);
|
||||
void forEachResolvedPathVar(const char* loadPath, const LoadedImageChain& forImageChain, bool implictRPath, LinkageType linkageType,
|
||||
void (^handler)(const char* possiblePath, bool& stop));
|
||||
bool findImage(const char* loadPath, const LoadedImageChain& forImageChain, BuilderLoadedImage*& foundImage, LinkageType linkageType,
|
||||
uint32_t compatVersion, bool canUseSharedCacheClosure);
|
||||
void buildImage(ImageWriter& writer, BuilderLoadedImage& forImage);
|
||||
void addSegments(ImageWriter& writer, const MachOAnalyzer* mh);
|
||||
void addRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh);
|
||||
void addSynthesizedRebaseInfo(ImageWriter& writer, const MachOAnalyzer* mh);
|
||||
void addSynthesizedBindInfo(ImageWriter& writer, const MachOAnalyzer* mh);
|
||||
void addBindInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
|
||||
void reportRebasesAndBinds(ImageWriter& writer, BuilderLoadedImage& forImage);
|
||||
void addChainedFixupInfo(ImageWriter& writer, BuilderLoadedImage& forImage);
|
||||
void addInterposingTuples(LaunchClosureWriter& writer, const Image* image, const MachOAnalyzer* mh);
|
||||
void computeInitOrder(ImageWriter& writer, uint32_t loadIndex);
|
||||
void addClosureInfo(LaunchClosureWriter& closureWriter);
|
||||
void depthFirstRecurseSetInitInfo(uint32_t loadIndex, InitInfo initInfos[], uint32_t& initOrder, bool& hasError);
|
||||
bool findSymbol(BuilderLoadedImage& fromImage, int libraryOrdinal, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend,
|
||||
Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
|
||||
bool findSymbolInImage(const MachOAnalyzer* macho, const char* symbolName, uint64_t addend, bool followReExports,
|
||||
bool weakImport, Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
|
||||
const MachOAnalyzer* machOForImageNum(ImageNum imageNum);
|
||||
ImageNum imageNumForMachO(const MachOAnalyzer* mh);
|
||||
const MachOAnalyzer* findDependent(const MachOLoaded* mh, uint32_t depIndex);
|
||||
BuilderLoadedImage& findLoadedImage(ImageNum imageNum);
|
||||
BuilderLoadedImage& findLoadedImage(const MachOAnalyzer* mh);
|
||||
uint32_t index(const BuilderLoadedImage&);
|
||||
bool expandAtLoaderPath(const char* loadPath, bool fromLCRPATH, const BuilderLoadedImage& loadedImage, char fixedPath[]);
|
||||
bool expandAtExecutablePath(const char* loadPath, bool fromLCRPATH, char fixedPath[]);
|
||||
void addMustBeMissingPath(const char* path);
|
||||
void addSkippedFile(const char* path, uint64_t inode, uint64_t mtime);
|
||||
const char* strdup_temp(const char* path);
|
||||
bool overridableDylib(const BuilderLoadedImage& forImage);
|
||||
void forEachBind(BuilderLoadedImage& forImage, void (^handler)(uint64_t runtimeOffset, Image::ResolvedSymbolTarget target, const ResolvedTargetInfo& targetInfo, bool& stop),
|
||||
void (^strongHandler)(const char* strongSymbolName),
|
||||
void (^missingLazyBindHandler)());
|
||||
bool findMissingSymbolHandler(Image::ResolvedSymbolTarget& target, ResolvedTargetInfo& targetInfo);
|
||||
|
||||
struct HashCString {
|
||||
static size_t hash(const char* v);
|
||||
};
|
||||
|
||||
struct EqualCString {
|
||||
static bool equal(const char* s1, const char* s2);
|
||||
};
|
||||
|
||||
struct HashPointer {
|
||||
template<typename T>
|
||||
static size_t hash(const T* v) {
|
||||
return std::hash<const T*>{}(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct EqualPointer {
|
||||
template<typename T>
|
||||
static bool equal(const T* s1, const T* s2) {
|
||||
return s1 == s2;
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjCOptimizerImage {
|
||||
|
||||
ObjCOptimizerImage() {
|
||||
}
|
||||
|
||||
~ObjCOptimizerImage() {
|
||||
}
|
||||
|
||||
typedef std::pair<closure::Image::ObjCClassNameImageOffset, closure::Image::ObjCClassImageOffset> SeenClass;
|
||||
|
||||
struct SelectorFixup {
|
||||
uint32_t fixupVMOffset;
|
||||
bool isSharedCache;
|
||||
union {
|
||||
struct {
|
||||
uint32_t selectorTableIndex;
|
||||
} sharedCache;
|
||||
struct {
|
||||
const char* selectorString;
|
||||
} image;
|
||||
};
|
||||
};
|
||||
|
||||
BuilderLoadedImage* loadedImage = nullptr;
|
||||
ImageWriter* writer = nullptr;
|
||||
uint64_t fairplayFileOffsetStart = 0;
|
||||
uint64_t fairplayFileOffsetEnd = 0;
|
||||
Diagnostics diag;
|
||||
|
||||
// Image info optimisation
|
||||
uint64_t objcImageInfoVMOffset;
|
||||
|
||||
// Class and protocol optimisation data structures
|
||||
OverflowSafeArray<std::pair<uint64_t, uint64_t>> classesNameAndDataVMOffsets;
|
||||
OverflowSafeArray<SeenClass> seenClasses;
|
||||
OverflowSafeArray<uint64_t> classStableSwiftFixups;
|
||||
Map<const char*, dyld3::closure::Image::ObjCDuplicateClass, HashCString, EqualCString> classSharedCacheDuplicates;
|
||||
OverflowSafeArray<SeenClass> seenProtocols;
|
||||
OverflowSafeArray<uint64_t> protocolISAFixups;
|
||||
|
||||
// Selector optimsation data structures
|
||||
OverflowSafeArray<SelectorFixup> selectorFixups;
|
||||
Map<const char*, dyld3::closure::Image::ObjCImageOffset, HashCString, EqualCString> selectorMap;
|
||||
std::optional<uint64_t> methodNameVMOffset;
|
||||
};
|
||||
|
||||
bool optimizeObjC(Array<ImageWriter>& writers);
|
||||
void optimizeObjCSelectors(const objc_opt::objc_selopt_t* objcSelOpt,
|
||||
const Map<const char*, dyld3::closure::Image::ObjCImageOffset, HashCString, EqualCString>& closureSelectorMap,
|
||||
ObjCOptimizerImage& image);
|
||||
void optimizeObjCClasses(const objc_opt::objc_clsopt_t* objcClassOpt,
|
||||
const Map<const dyld3::MachOAnalyzer*, bool, HashPointer, EqualPointer>& sharedCacheImagesMap,
|
||||
const Map<const char*, dyld3::closure::Image::ObjCDuplicateClass, HashCString, EqualCString>& duplicateSharedCacheClasses,
|
||||
ObjCOptimizerImage& image);
|
||||
void optimizeObjCProtocols(const objc_opt::objc_protocolopt2_t* objcProtocolOpt,
|
||||
const Map<const dyld3::MachOAnalyzer*, bool, HashPointer, EqualPointer>& sharedCacheImagesMap,
|
||||
ObjCOptimizerImage& image);
|
||||
void writeClassOrProtocolHashTable(bool classes, Array<ObjCOptimizerImage>& objcImages);
|
||||
|
||||
void addDuplicateObjCClassWarning(const char* className,
|
||||
const char* duplicateDefinitionPath,
|
||||
const char* canonicalDefinitionPath);
|
||||
|
||||
static bool inLoadedImageArray(const Array<LoadedImage>& loadedList, ImageNum imageNum);
|
||||
static void buildLoadOrderRecurse(Array<LoadedImage>& loadedList, const Array<const ImageArray*>& imagesArrays, const Image* toAdd);
|
||||
void invalidateInitializerRoots();
|
||||
|
||||
const FileSystem& _fileSystem;
|
||||
const DyldSharedCache* const _dyldCache;
|
||||
const PathOverrides& _pathOverrides;
|
||||
const GradedArchs& _archs;
|
||||
Platform const _platform;
|
||||
uint32_t const _startImageNum;
|
||||
const ImageArray* _dyldImageArray = nullptr;
|
||||
const CacheDylibsBindingHandlers* _handlers = nullptr;
|
||||
const Array<CachedDylibAlias>* _aliases = nullptr;
|
||||
const AtPath _atPathHandling = AtPath::none;
|
||||
uint32_t _mainProgLoadIndex = 0;
|
||||
const char* _mainProgLoadPath = nullptr;
|
||||
Diagnostics _diag;
|
||||
LaunchErrorInfo* _launchErrorInfo = nullptr;
|
||||
PathPool* _tempPaths = nullptr;
|
||||
PathPool* _mustBeMissingPaths = nullptr;
|
||||
OverflowSafeArray<SkippedFile> _skippedFiles;
|
||||
uint32_t _nextIndex = 0;
|
||||
OverflowSafeArray<BuilderLoadedImage,2048> _loadedImages;
|
||||
OverflowSafeArray<Image::LinkedImage,65536> _dependencies; // all dylibs in cache need ~20,000 edges
|
||||
OverflowSafeArray<InterposingTuple> _interposingTuples;
|
||||
OverflowSafeArray<Closure::PatchEntry> _weakDefCacheOverrides;
|
||||
OverflowSafeArray<const char*> _weakDefsFromChainedBinds;
|
||||
OverflowSafeArray<uint8_t> _objcSelectorsHashTable;
|
||||
OverflowSafeArray<Image::ObjCSelectorImage> _objcSelectorsHashTableImages;
|
||||
OverflowSafeArray<uint8_t> _objcClassesHashTable;
|
||||
OverflowSafeArray<uint8_t> _objcProtocolsHashTable;
|
||||
OverflowSafeArray<Image::ObjCClassImage> _objcClassesHashTableImages;
|
||||
OverflowSafeArray<uint8_t> _objcClassesDuplicatesHashTable;
|
||||
PathPool* _objcDuplicateClassWarnings = nullptr;
|
||||
uint32_t _alreadyInitedIndex = 0;
|
||||
bool _isLaunchClosure = false;
|
||||
bool _makingDyldCacheImages = false;
|
||||
bool _dyldCacheIsLive = false; // means kernel is rebasing dyld cache content being viewed
|
||||
bool _makingClosuresInCache = false;
|
||||
bool _makingCustomerCache = false;
|
||||
bool _allowRelativePaths = false;
|
||||
bool _atPathUsed = false;
|
||||
bool _fallbackPathUsed = false;
|
||||
bool _allowMissingLazies = false;
|
||||
bool _dyldCacheInvalidFormatVersion = false;
|
||||
bool _foundNonCachedImage = false; // true means we have one or more images from disk we need to build closure(s) for
|
||||
bool _foundDyldCacheRoots = false; // true if one or more images are roots of the shared cache
|
||||
bool _foundMissingLazyBinds = false; // true if one or more images having missing lazy binds
|
||||
ImageNum _libDyldImageNum = 0;
|
||||
ImageNum _libSystemImageNum = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif /* ClosureBuilder_h */
|
82
dyld3/ClosureFileSystem.h
Normal file
82
dyld3/ClosureFileSystem.h
Normal file
@ -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 ClosureFileSystem_h
|
||||
#define ClosureFileSystem_h
|
||||
|
||||
// For MAXPATHLEN
|
||||
#include <sys/param.h>
|
||||
// For va_list
|
||||
#include <stdarg.h>
|
||||
// For uint64_t
|
||||
#include <stdint.h>
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
struct LoadedFileInfo {
|
||||
const void* fileContent = nullptr;
|
||||
uint64_t fileContentLen = 0;
|
||||
uint64_t sliceOffset = 0;
|
||||
uint64_t sliceLen : 63,
|
||||
isSipProtected : 1;
|
||||
uint64_t inode = 0;
|
||||
uint64_t mtime = 0;
|
||||
void (*unload)(const LoadedFileInfo&) = nullptr;
|
||||
const char* path = nullptr;
|
||||
};
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
|
||||
class FileSystem {
|
||||
protected:
|
||||
FileSystem() { }
|
||||
|
||||
public:
|
||||
|
||||
// Get the real path for a given path, if it exists.
|
||||
// Returns true if the real path was found and updates the given buffer iff that is the case
|
||||
virtual bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const = 0;
|
||||
|
||||
// Returns true on success. If an error occurs the given callback will be called with the reason.
|
||||
// On success, info is filled with info about the loaded file. If the path supplied includes a symlink,
|
||||
// the supplier realerPath is filled in with the real path of the file, otherwise it is set to the empty string.
|
||||
virtual bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const = 0;
|
||||
|
||||
// Frees the buffer allocated by loadFile()
|
||||
virtual void unloadFile(const LoadedFileInfo& info) const = 0;
|
||||
|
||||
// Frees all but the requested range and adjusts info to new buffer location
|
||||
// Remaining buffer can be freed later with unloadFile()
|
||||
virtual void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const = 0;
|
||||
|
||||
// If a file exists at path, returns true and sets inode and mtime
|
||||
virtual bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr,
|
||||
bool* issetuid=nullptr, bool* inodesMatchRuntime = nullptr) const = 0;
|
||||
};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* ClosureFileSystem_h */
|
45
dyld3/ClosureFileSystemNull.cpp
Normal file
45
dyld3/ClosureFileSystemNull.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 "ClosureFileSystemNull.h"
|
||||
|
||||
using dyld3::closure::FileSystemNull;
|
||||
|
||||
bool FileSystemNull::getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FileSystemNull::loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileSystemNull::unloadFile(const LoadedFileInfo& info) const {
|
||||
}
|
||||
|
||||
void FileSystemNull::unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const {
|
||||
}
|
||||
|
||||
bool FileSystemNull::fileExists(const char* path, uint64_t* inode, uint64_t* mtime,
|
||||
bool* issetuid, bool* inodesMatchRuntime) const {
|
||||
return false;
|
||||
}
|
56
dyld3/ClosureFileSystemNull.h
Normal file
56
dyld3/ClosureFileSystemNull.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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 ClosureFileSystemNull_h
|
||||
#define ClosureFileSystemNull_h
|
||||
|
||||
#include "ClosureFileSystem.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
|
||||
class __attribute__((visibility("hidden"))) FileSystemNull : public FileSystem {
|
||||
public:
|
||||
FileSystemNull() : FileSystem() { }
|
||||
|
||||
bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const override;
|
||||
|
||||
bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const override;
|
||||
|
||||
void unloadFile(const LoadedFileInfo& info) const override;
|
||||
|
||||
void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const override;
|
||||
|
||||
bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr,
|
||||
bool* issetuid=nullptr, bool* inodesMatchRuntime = nullptr) const override;
|
||||
};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* ClosureFileSystemNull_h */
|
289
dyld3/ClosureFileSystemPhysical.cpp
Normal file
289
dyld3/ClosureFileSystemPhysical.cpp
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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 "ClosureFileSystemPhysical.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#if BUILDING_UPDATE_DYLD_CACHE_BUILDER
|
||||
#include <rootless.h>
|
||||
#endif
|
||||
#include <sys/errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <mach/mach.h>
|
||||
#if !TARGET_OS_SIMULATOR && !TARGET_OS_DRIVERKIT
|
||||
#include <sandbox.h>
|
||||
#include <sandbox/private.h>
|
||||
#endif
|
||||
|
||||
using dyld3::closure::FileSystemPhysical;
|
||||
|
||||
bool FileSystemPhysical::getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const {
|
||||
__block bool success = false;
|
||||
// first pass: open file and ask kernel for canonical path
|
||||
forEachPath(possiblePath, ^(const char* aPath, unsigned prefixLen, bool& stop) {
|
||||
int fd = ::open(aPath, O_RDONLY, 0);
|
||||
if ( fd != -1 ) {
|
||||
char tempPath[MAXPATHLEN];
|
||||
success = (fcntl(fd, F_GETPATH, tempPath) == 0);
|
||||
::close(fd);
|
||||
if ( success ) {
|
||||
// if prefix was used, remove it
|
||||
strcpy(realPath, &tempPath[prefixLen]);
|
||||
}
|
||||
stop = true;
|
||||
}
|
||||
});
|
||||
if (success)
|
||||
return success;
|
||||
|
||||
// second pass: file does not exist but may be a symlink to a non-existent file
|
||||
// This is only for use on-device on platforms where dylibs are removed
|
||||
if ( _overlayPath == nullptr && _rootPath == nullptr ) {
|
||||
realpath(possiblePath, realPath);
|
||||
int realpathErrno = errno;
|
||||
// If realpath() resolves to a path which does not exist on disk, errno is set to ENOENT
|
||||
success = (realpathErrno == ENOENT) || (realpathErrno == 0);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool sandboxBlocked(const char* path, const char* kind)
|
||||
{
|
||||
#if TARGET_OS_SIMULATOR || TARGET_OS_DRIVERKIT
|
||||
// sandbox calls not yet supported in dyld_sim
|
||||
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
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
void FileSystemPhysical::forEachPath(const char* path, void (^handler)(const char* fullPath, unsigned prefixLen, bool& stop)) const
|
||||
{
|
||||
bool stop = false;
|
||||
char altPath[PATH_MAX];
|
||||
if ( _overlayPath != nullptr ) {
|
||||
strlcpy(altPath, _overlayPath, PATH_MAX);
|
||||
strlcat(altPath, path, PATH_MAX);
|
||||
handler(altPath, (unsigned)strlen(_overlayPath), stop);
|
||||
if ( stop )
|
||||
return;
|
||||
}
|
||||
if ( _rootPath != nullptr ) {
|
||||
strlcpy(altPath, _rootPath, PATH_MAX);
|
||||
strlcat(altPath, path, PATH_MAX);
|
||||
handler(altPath, (unsigned)strlen(_rootPath), stop);
|
||||
if ( stop )
|
||||
return;
|
||||
}
|
||||
else {
|
||||
handler(path, 0, stop);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isFileRelativePath(const char* path)
|
||||
{
|
||||
if ( path[0] == '/' )
|
||||
return false;
|
||||
if ( path[0] != '.' )
|
||||
return true;
|
||||
if ( path[1] == '/' )
|
||||
return true;
|
||||
if ( (path[1] == '.') && (path[2] == '/') )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns true on success. If an error occurs the given callback will be called with the reason.
|
||||
// On success, info is filled with info about the loaded file. If the path supplied includes a symlink,
|
||||
// the supplier realerPath is filled in with the real path of the file, otherwise it is set to the empty string.
|
||||
bool FileSystemPhysical::loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const {
|
||||
if ( !_allowRelativePaths && isFileRelativePath(path) ) {
|
||||
error("relative file paths not allowed '%s'", path);
|
||||
return false;
|
||||
}
|
||||
// open file
|
||||
__block int fd;
|
||||
__block struct stat statBuf;
|
||||
__block bool sipProtected = false;
|
||||
forEachPath(path, ^(const char* aPath, unsigned prefixLen, bool& stop) {
|
||||
fd = ::open(aPath, O_RDONLY, 0);
|
||||
if ( fd == -1 ) {
|
||||
int openErrno = errno;
|
||||
if ( (openErrno == EPERM) && sandboxBlockedOpen(path) )
|
||||
error("file system sandbox blocked open(\"%s\", O_RDONLY)", path);
|
||||
else if ( (openErrno != ENOENT) && (openErrno != ENOTDIR) )
|
||||
error("open(\"%s\", O_RDONLY) failed with errno=%d", path, openErrno);
|
||||
}
|
||||
else {
|
||||
// get file info
|
||||
#if TARGET_OS_SIMULATOR
|
||||
if ( ::stat(aPath, &statBuf) != 0 ) {
|
||||
#else
|
||||
if ( ::fstat(fd, &statBuf) != 0 ) {
|
||||
#endif
|
||||
int statErr = errno;
|
||||
if ( (statErr == EPERM) && sandboxBlockedStat(path) )
|
||||
error("file system sandbox blocked stat(\"%s\")", path);
|
||||
else
|
||||
error("stat(\"%s\") failed with errno=%d", path, errno);
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
else {
|
||||
// Get the realpath of the file if it is a symlink
|
||||
char tempPath[MAXPATHLEN];
|
||||
if ( fcntl(fd, F_GETPATH, tempPath) == 0 ) {
|
||||
const char* realPathWithin = &tempPath[prefixLen];
|
||||
// Don't set the realpath if it is just the same as the regular path
|
||||
if ( strcmp(path, realPathWithin) == 0 ) {
|
||||
// zero out realerPath if path is fine as-is
|
||||
// <rdar://45018392> don't trash input 'path' if realerPath is same buffer as path
|
||||
if ( realerPath != path )
|
||||
realerPath[0] = '\0';
|
||||
}
|
||||
else
|
||||
strcpy(realerPath, realPathWithin);
|
||||
#if BUILDING_UPDATE_DYLD_CACHE_BUILDER
|
||||
sipProtected = (rootless_check_trusted_fd(fd) == 0);
|
||||
#endif
|
||||
stop = true;
|
||||
}
|
||||
else {
|
||||
error("Could not get real path for \"%s\"\n", path);
|
||||
::close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if ( fd == -1 )
|
||||
return false;
|
||||
|
||||
// only regular files can be loaded
|
||||
if ( !S_ISREG(statBuf.st_mode) ) {
|
||||
error("not a file for %s", path);
|
||||
::close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// mach-o files must be at list one page in size
|
||||
if ( statBuf.st_size < 4096 ) {
|
||||
error("file too short %s", path);
|
||||
::close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
info.fileContent = nullptr;
|
||||
info.fileContentLen = statBuf.st_size;
|
||||
info.sliceOffset = 0;
|
||||
info.sliceLen = statBuf.st_size;
|
||||
info.isSipProtected = sipProtected;
|
||||
info.inode = statBuf.st_ino;
|
||||
info.mtime = statBuf.st_mtime;
|
||||
info.path = path;
|
||||
|
||||
// mmap() whole file
|
||||
void* wholeFile = ::mmap(nullptr, (size_t)statBuf.st_size, PROT_READ, MAP_PRIVATE|MAP_RESILIENT_CODESIGN, fd, 0);
|
||||
if ( wholeFile == MAP_FAILED ) {
|
||||
int mmapErr = errno;
|
||||
if ( mmapErr == EPERM ) {
|
||||
if ( sandboxBlockedMmap(path) )
|
||||
error("file system sandbox blocked mmap() of '%s'", path);
|
||||
else
|
||||
error("code signing blocked mmap() of '%s'", path);
|
||||
}
|
||||
else {
|
||||
error("mmap() failed with errno=%d for %s", errno, path);
|
||||
}
|
||||
::close(fd);
|
||||
return false;
|
||||
}
|
||||
info.fileContent = wholeFile;
|
||||
|
||||
// Set unmap as the unload method.
|
||||
info.unload = [](const LoadedFileInfo& info) {
|
||||
::munmap((void*)info.fileContent, (size_t)info.fileContentLen);
|
||||
};
|
||||
|
||||
::close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSystemPhysical::unloadFile(const LoadedFileInfo& info) const {
|
||||
if (info.unload)
|
||||
info.unload(info);
|
||||
}
|
||||
|
||||
void FileSystemPhysical::unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const {
|
||||
// Unmap from 0..keepStartOffset and (keepStartOffset+keepLength)..info.fileContentLen
|
||||
if (keepStartOffset)
|
||||
::munmap((void*)info.fileContent, (size_t)keepStartOffset);
|
||||
if ((keepStartOffset + keepLength) != info.fileContentLen) {
|
||||
// Round up to page alignment
|
||||
keepLength = (keepLength + PAGE_SIZE - 1) & (-PAGE_SIZE);
|
||||
::munmap((void*)((char*)info.fileContent + keepStartOffset + keepLength), (size_t)(info.fileContentLen - (keepStartOffset + keepLength)));
|
||||
}
|
||||
info.fileContent = (const void*)((char*)info.fileContent + keepStartOffset);
|
||||
info.fileContentLen = keepLength;
|
||||
}
|
||||
|
||||
bool FileSystemPhysical::fileExists(const char* path, uint64_t* inode, uint64_t* mtime,
|
||||
bool* issetuid, bool* inodesMatchRuntime) const {
|
||||
__block bool result = false;
|
||||
forEachPath(path, ^(const char* aPath, unsigned prefixLen, bool& stop) {
|
||||
struct stat statBuf;
|
||||
if ( ::stat(aPath, &statBuf) == 0 ) {
|
||||
if (inode)
|
||||
*inode = statBuf.st_ino;
|
||||
if (mtime)
|
||||
*mtime = statBuf.st_mtime;
|
||||
if (issetuid)
|
||||
*issetuid = (statBuf.st_mode & (S_ISUID|S_ISGID));
|
||||
if (inodesMatchRuntime)
|
||||
*inodesMatchRuntime = true;
|
||||
stop = true;
|
||||
result = true;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
66
dyld3/ClosureFileSystemPhysical.h
Normal file
66
dyld3/ClosureFileSystemPhysical.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 ClosureFileSystemPhysical_h
|
||||
#define ClosureFileSystemPhysical_h
|
||||
|
||||
#include "ClosureFileSystem.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
|
||||
class __attribute__((visibility("hidden"))) FileSystemPhysical : public FileSystem {
|
||||
public:
|
||||
FileSystemPhysical(const char* rootPath=nullptr, const char* overlayPath=nullptr, bool allowRelativePaths=true)
|
||||
: FileSystem(), _rootPath(rootPath), _overlayPath(overlayPath), _allowRelativePaths(allowRelativePaths)
|
||||
{ }
|
||||
|
||||
bool getRealPath(const char possiblePath[MAXPATHLEN], char realPath[MAXPATHLEN]) const override;
|
||||
|
||||
bool loadFile(const char* path, LoadedFileInfo& info, char realerPath[MAXPATHLEN], void (^error)(const char* format, ...)) const override;
|
||||
|
||||
void unloadFile(const LoadedFileInfo& info) const override;
|
||||
|
||||
void unloadPartialFile(LoadedFileInfo& info, uint64_t keepStartOffset, uint64_t keepLength) const override;
|
||||
|
||||
bool fileExists(const char* path, uint64_t* inode=nullptr, uint64_t* mtime=nullptr,
|
||||
bool* issetuid=nullptr, bool* inodesMatchRuntime = nullptr) const override;
|
||||
|
||||
private:
|
||||
|
||||
void forEachPath(const char* path, void (^handler)(const char* fullPath, unsigned prefixLen, bool& stop)) const;
|
||||
|
||||
const char* _rootPath;
|
||||
const char* _overlayPath;
|
||||
bool _allowRelativePaths;
|
||||
};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* ClosureFileSystemPhysical_h */
|
773
dyld3/ClosurePrinter.cpp
Normal file
773
dyld3/ClosurePrinter.cpp
Normal file
@ -0,0 +1,773 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "ClosurePrinter.h"
|
||||
#include "JSONWriter.h"
|
||||
|
||||
#include "objc-shared-cache.h"
|
||||
|
||||
using namespace dyld3::json;
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
static std::string printTarget(const Array<const ImageArray*>& imagesArrays, Image::ResolvedSymbolTarget target)
|
||||
{
|
||||
const Image* targetImage;
|
||||
uint64_t value;
|
||||
switch ( target.image.kind ) {
|
||||
case Image::ResolvedSymbolTarget::kindImage:
|
||||
targetImage = ImageArray::findImage(imagesArrays, target.image.imageNum);
|
||||
if ( target.image.offset & 0x8000000000ULL ) {
|
||||
uint64_t signExtend = target.image.offset | 0xFFFFFF0000000000ULL;
|
||||
return std::string("bind to ") + targetImage->leafName() + " - " + hex8(-signExtend);
|
||||
}
|
||||
else
|
||||
return std::string("bind to ") + targetImage->leafName() + " + " + hex8(target.image.offset);
|
||||
break;
|
||||
case Image::ResolvedSymbolTarget::kindSharedCache:
|
||||
return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset);
|
||||
break;
|
||||
case Image::ResolvedSymbolTarget::kindAbsolute:
|
||||
value = target.absolute.value;
|
||||
if ( value & 0x2000000000000000LL )
|
||||
value |= 0xC000000000000000LL;
|
||||
return std::string("bind to absolute ") + hex(value);
|
||||
break;
|
||||
}
|
||||
return "???";
|
||||
}
|
||||
|
||||
static const char* nameForType(TypedBytes::Type type) {
|
||||
switch (type) {
|
||||
// containers
|
||||
case TypedBytes::Type::launchClosure:
|
||||
return "launchClosure";
|
||||
case TypedBytes::Type::imageArray:
|
||||
return "imageArray";
|
||||
case TypedBytes::Type::image:
|
||||
return "image";
|
||||
case TypedBytes::Type::dlopenClosure:
|
||||
return "dlopenClosure";
|
||||
// attributes for Images
|
||||
case TypedBytes::Type::imageFlags:
|
||||
return "imageFlags";
|
||||
case TypedBytes::Type::pathWithHash:
|
||||
return "pathWithHash";
|
||||
case TypedBytes::Type::fileInodeAndTime:
|
||||
return "fileInodeAndTime";
|
||||
case TypedBytes::Type::cdHash:
|
||||
return "cdHash";
|
||||
case TypedBytes::Type::uuid:
|
||||
return "uuid";
|
||||
case TypedBytes::Type::mappingInfo:
|
||||
return "mappingInfo";
|
||||
case TypedBytes::Type::diskSegment:
|
||||
return "diskSegment";
|
||||
case TypedBytes::Type::cacheSegment:
|
||||
return "cacheSegment";
|
||||
case TypedBytes::Type::dependents:
|
||||
return "dependents";
|
||||
case TypedBytes::Type::initOffsets:
|
||||
return "initOffsets";
|
||||
case TypedBytes::Type::dofOffsets:
|
||||
return "dofOffsets";
|
||||
case TypedBytes::Type::codeSignLoc:
|
||||
return "codeSignLoc";
|
||||
case TypedBytes::Type::fairPlayLoc:
|
||||
return "fairPlayLoc";
|
||||
case TypedBytes::Type::rebaseFixups:
|
||||
return "rebaseFixups";
|
||||
case TypedBytes::Type::bindFixups:
|
||||
return "bindFixups";
|
||||
case TypedBytes::Type::cachePatchInfo:
|
||||
return "cachePatchInfo";
|
||||
case TypedBytes::Type::textFixups:
|
||||
return "textFixups";
|
||||
case TypedBytes::Type::imageOverride:
|
||||
return "imageOverride";
|
||||
case TypedBytes::Type::initBefores:
|
||||
return "initBefores";
|
||||
case TypedBytes::Type::initsSection:
|
||||
return "initSection";
|
||||
case TypedBytes::Type::chainedFixupsTargets:
|
||||
return "chainedFixupsTargets";
|
||||
case TypedBytes::Type::termOffsets:
|
||||
return "termOffsets";
|
||||
case TypedBytes::Type::chainedStartsOffset:
|
||||
return "chainedStartsOffset";
|
||||
case TypedBytes::Type::objcFixups:
|
||||
return "objcFixups";
|
||||
// attributes for Closures (launch or dlopen)
|
||||
case TypedBytes::Type::closureFlags:
|
||||
return "closureFlags";
|
||||
case TypedBytes::Type::dyldCacheUUID:
|
||||
return "dyldCacheUUID";
|
||||
case TypedBytes::Type::missingFiles:
|
||||
return "missingFiles";
|
||||
case TypedBytes::Type::envVar:
|
||||
return "envVar";
|
||||
case TypedBytes::Type::topImage:
|
||||
return "topImage";
|
||||
case TypedBytes::Type::libDyldEntry:
|
||||
return "libDyldEntry";
|
||||
case TypedBytes::Type::libSystemNum:
|
||||
return "libSystemNum";
|
||||
case TypedBytes::Type::bootUUID:
|
||||
return "bootUUID";
|
||||
case TypedBytes::Type::mainEntry:
|
||||
return "mainEntry";
|
||||
case TypedBytes::Type::startEntry:
|
||||
return "startEntry";
|
||||
case TypedBytes::Type::cacheOverrides:
|
||||
return "cacheOverrides";
|
||||
case TypedBytes::Type::interposeTuples:
|
||||
return "interposeTuples";
|
||||
case TypedBytes::Type::existingFiles:
|
||||
return "existingFiles";
|
||||
case TypedBytes::Type::selectorTable:
|
||||
return "selectorTable";
|
||||
case TypedBytes::Type::classTable:
|
||||
return "classTable";
|
||||
case TypedBytes::Type::warning:
|
||||
return "warning";
|
||||
case TypedBytes::Type::duplicateClassesTable:
|
||||
return "duplicateClassesTable";
|
||||
}
|
||||
}
|
||||
|
||||
static Node buildImageNode(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups,
|
||||
bool printDependentsDetails, bool printRaw,
|
||||
const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt,
|
||||
const Array<closure::Image::ObjCSelectorImage>& selImages)
|
||||
{
|
||||
__block Node imageNode;
|
||||
|
||||
if ( image->isInvalid() )
|
||||
return imageNode;
|
||||
|
||||
imageNode.map["image-num"].value = hex4(image->imageNum());
|
||||
imageNode.map["path"].value = image->path();
|
||||
|
||||
if (printRaw) {
|
||||
__block Node attributes;
|
||||
image->forEachAttribute(^(const TypedBytes *typedBytes, bool &stop) {
|
||||
Node anAttribute;
|
||||
anAttribute.map["type"].value = decimal((uint32_t)typedBytes->type);
|
||||
anAttribute.map["type-name"].value = nameForType((TypedBytes::Type)typedBytes->type);
|
||||
anAttribute.map["length"].value = decimal(typedBytes->payloadLength);
|
||||
attributes.array.push_back(anAttribute);
|
||||
});
|
||||
imageNode.map["attributes"] = attributes;
|
||||
return imageNode;
|
||||
}
|
||||
|
||||
__block Node imageAliases;
|
||||
image->forEachAlias(^(const char* aliasPath, bool& stop) {
|
||||
Node anAlias;
|
||||
anAlias.value = aliasPath;
|
||||
imageAliases.array.push_back(anAlias);
|
||||
});
|
||||
if ( !imageAliases.array.empty() )
|
||||
imageNode.map["aliases"] = imageAliases;
|
||||
uuid_t uuid;
|
||||
if ( image->getUuid(uuid) ) {
|
||||
uuid_string_t uuidStr;
|
||||
uuid_unparse(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["has-plus-loads"].value = (image->mayHavePlusLoads() ? "true" : "false");
|
||||
imageNode.map["never-unload"].value = (image->neverUnload() ? "true" : "false");
|
||||
imageNode.map["has-precomputed-objc"].value = (image->hasPrecomputedObjC() ? "true" : "false");
|
||||
// imageNode.map["platform-binary"].value = (image->isPlatformBinary() ? "true" : "false");
|
||||
// if ( image->cwdMustBeThisDir() )
|
||||
// imageNode.map["cwd-must-be-this-dir"].value = "true";
|
||||
if ( !image->inDyldCache() ) {
|
||||
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);
|
||||
// }
|
||||
uint64_t inode;
|
||||
uint64_t mTime;
|
||||
if ( image->hasFileModTimeAndInode(inode, mTime) ) {
|
||||
imageNode.map["file-mod-time"].value = hex(inode);
|
||||
imageNode.map["file-inode"].value = hex(mTime);
|
||||
}
|
||||
image->forEachCDHash(^(const uint8_t *cdHash, bool& stop) {
|
||||
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" ) {
|
||||
Node hashNode;
|
||||
hashNode.value = cdHashStr;
|
||||
imageNode.map["cd-hashes"].array.push_back(hashNode);
|
||||
}
|
||||
});
|
||||
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 laterReadOnly, 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);
|
||||
imageNode.map["mappings"].array.push_back(segInfoNode);
|
||||
switch ( permissions ) {
|
||||
case 0:
|
||||
segInfoNode.map["permissions"].value = "--";
|
||||
break;
|
||||
case 1:
|
||||
segInfoNode.map["permissions"].value = "r";
|
||||
break;
|
||||
case 2:
|
||||
segInfoNode.map["permissions"].value = "ro"; // r/w then r/o
|
||||
break;
|
||||
case 3:
|
||||
segInfoNode.map["permissions"].value = "rw";
|
||||
break;
|
||||
case 4:
|
||||
segInfoNode.map["permissions"].value = "rx";
|
||||
break;
|
||||
default:
|
||||
segInfoNode.map["permissions"].value = "??";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if ( printFixups ) {
|
||||
image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
|
||||
// rebase
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "rebase";
|
||||
}, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
|
||||
// bind
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
|
||||
}, ^(uint64_t startsStructImageOffset, const Array<Image::ResolvedSymbolTarget>& targets, bool& stop) {
|
||||
// chain
|
||||
imageNode.map["fixups-chain-starts-offset"].value = hex8(startsStructImageOffset);
|
||||
for (const Image::ResolvedSymbolTarget& target: targets) {
|
||||
Node targetNode;
|
||||
targetNode.value = printTarget(imagesArrays, target);
|
||||
imageNode.map["fixups-targets"].array.push_back(targetNode);
|
||||
}
|
||||
|
||||
}, ^(uint64_t imageOffsetToFixup) {
|
||||
// fixupObjCImageInfo
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc image info pre-optimized by dyld flag";
|
||||
}, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
|
||||
// fixupObjCProtocol
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
|
||||
}, ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) {
|
||||
// fixupObjCSelRefs
|
||||
Image::ResolvedSymbolTarget target;
|
||||
if ( inSharedCache ) {
|
||||
const char* selectorString = dyldCache->objcOpt()->selopt()->getEntryForIndex(selectorIndex);
|
||||
target.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache;
|
||||
target.sharedCache.offset = (uint64_t)selectorString - (uint64_t)dyldCache;
|
||||
} else {
|
||||
ImageNum imageNum;
|
||||
uint64_t vmOffset;
|
||||
bool gotLocation = selOpt->getStringLocation(selectorIndex, selImages, imageNum, vmOffset);
|
||||
assert(gotLocation);
|
||||
target.image.kind = Image::ResolvedSymbolTarget::kindImage;
|
||||
target.image.imageNum = imageNum;
|
||||
target.image.offset = vmOffset;
|
||||
}
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = printTarget(imagesArrays, target);
|
||||
}, ^(uint64_t imageOffsetToFixup, bool &stop) {
|
||||
// fixupObjCStableSwift
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc set stable Swift";
|
||||
}, ^(uint64_t imageOffsetToFixup, bool &stop) {
|
||||
// fixupObjCMethodList
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc set fixed up method list";
|
||||
});
|
||||
image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool &stop) {
|
||||
// rebase
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToRebase)].value = "text rebase";
|
||||
}, ^(uint32_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
|
||||
imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = "text " + printTarget(imagesArrays, target);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( printFixups ) {
|
||||
if ( dyldCache != nullptr ) {
|
||||
uint32_t imageIndex = image->imageNum() - (uint32_t)dyldCache->cachedDylibsImageArray()->startImageNum();
|
||||
dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* name) {
|
||||
__block Node implNode;
|
||||
implNode.map["name"].value = name;
|
||||
implNode.map["impl-cache-offset"].value = hex8(cacheOffsetOfImpl);
|
||||
dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) {
|
||||
Node siteNode;
|
||||
siteNode.map["cache-offset"].value = hex8(patchLocation.cacheOffset);
|
||||
if ( patchLocation.addend != 0 )
|
||||
siteNode.map["addend"].value = hex(patchLocation.addend);
|
||||
if ( patchLocation.authenticated != 0 ) {
|
||||
siteNode.map["key"].value = DyldSharedCache::keyName(patchLocation);
|
||||
siteNode.map["address-diversity"].value = patchLocation.usesAddressDiversity ? "true" : "false";
|
||||
siteNode.map["discriminator"].value = hex4(patchLocation.discriminator);
|
||||
}
|
||||
implNode.map["usage-sites"].array.push_back(siteNode);
|
||||
});
|
||||
imageNode.map["patches"].array.push_back(implNode);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add dependents
|
||||
image->forEachDependentImage(^(uint32_t depIndex, Image::LinkKind kind, ImageNum imageNum, bool& stop) {
|
||||
Node depMapNode;
|
||||
const Image* depImage = ImageArray::findImage(imagesArrays, imageNum);
|
||||
depMapNode.map["image-num"].value = hex4(imageNum);
|
||||
if ( depImage != nullptr )
|
||||
depMapNode.map["path"].value = depImage->path();
|
||||
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 initializers
|
||||
bool usesInitsSection = image->forEachInitializerSection(^(uint32_t sectionOffset, uint32_t sectionSize) {
|
||||
Node initSectNode;
|
||||
initSectNode.map["offset"].value = hex(sectionOffset);
|
||||
initSectNode.map["size"].value = hex(sectionSize);
|
||||
imageNode.map["initializers-section"].array.push_back(initSectNode);
|
||||
});
|
||||
if ( !usesInitsSection ) {
|
||||
image->forEachInitializer(nullptr, ^(const void* initializer) {
|
||||
Node initNode;
|
||||
initNode.value = hex((long)initializer);
|
||||
imageNode.map["initializer-offsets"].array.push_back(initNode);
|
||||
});
|
||||
}
|
||||
__block Node initBeforeNode;
|
||||
image->forEachImageToInitBefore(^(ImageNum imageToInit, bool& stop) {
|
||||
Node beforeNode;
|
||||
const Image* initImage = ImageArray::findImage(imagesArrays, imageToInit);
|
||||
assert(initImage != nullptr);
|
||||
beforeNode.value = initImage->path();
|
||||
imageNode.map["initializer-order"].array.push_back(beforeNode);
|
||||
});
|
||||
|
||||
// add static terminators
|
||||
image->forEachTerminator(nullptr, ^(const void* terminator) {
|
||||
Node termNode;
|
||||
termNode.value = hex8((long)terminator);
|
||||
imageNode.map["terminator-offsets"].array.push_back(termNode);
|
||||
});
|
||||
|
||||
ImageNum cacheImageNum;
|
||||
if ( image->isOverrideOfDyldCacheImage(cacheImageNum) ) {
|
||||
imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path();
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// 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 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);
|
||||
});
|
||||
#endif
|
||||
|
||||
return imageNode;
|
||||
}
|
||||
|
||||
|
||||
static Node buildImageArrayNode(const ImageArray* imageArray, const Array<const ImageArray*>& imagesArrays,
|
||||
bool printFixups, bool printDependentsDetails, bool printRaw,
|
||||
const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt,
|
||||
const Array<closure::Image::ObjCSelectorImage>& selImages)
|
||||
{
|
||||
__block Node images;
|
||||
imageArray->forEachImage(^(const Image* image, bool& stop) {
|
||||
images.array.push_back(buildImageNode(image, imagesArrays, printFixups, printDependentsDetails, printRaw, dyldCache, selOpt, selImages));
|
||||
});
|
||||
return images;
|
||||
}
|
||||
|
||||
|
||||
static Node buildClosureNode(const DlopenClosure* closure, const Array<const ImageArray*>& imagesArrays,
|
||||
bool printFixups, bool printRaw, bool printDependentsDetails)
|
||||
{
|
||||
__block Node root;
|
||||
root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays,
|
||||
printFixups, printDependentsDetails, printRaw,
|
||||
nullptr, nullptr, {});
|
||||
|
||||
closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
|
||||
Node patchNode;
|
||||
patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
|
||||
patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
|
||||
patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
|
||||
root.map["dyld-cache-fixups"].array.push_back(patchNode);
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static Node buildClosureNode(const LaunchClosure* closure, const Array<const ImageArray*>& imagesArrays,
|
||||
bool printFixups, bool printDependentsDetails, bool printRaw,
|
||||
const DyldSharedCache* dyldCache)
|
||||
{
|
||||
__block Node root;
|
||||
|
||||
Array<Image::ObjCSelectorImage> selectorImages;
|
||||
const closure::ObjCSelectorOpt* selectorHashTable = nullptr;
|
||||
bool hasPreoptimizedObjCSelectors = closure->selectorHashTable(selectorImages, selectorHashTable);
|
||||
|
||||
root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups,
|
||||
printDependentsDetails, printRaw,
|
||||
dyldCache, selectorHashTable, selectorImages);
|
||||
|
||||
if ( printRaw ) {
|
||||
__block Node attributes;
|
||||
closure->forEachAttribute(^(const TypedBytes *typedBytes, bool &stop) {
|
||||
Node anAttribute;
|
||||
anAttribute.map["type"].value = decimal((uint32_t)typedBytes->type);
|
||||
anAttribute.map["type-name"].value = nameForType((TypedBytes::Type)typedBytes->type);
|
||||
anAttribute.map["length"].value = decimal(typedBytes->payloadLength);
|
||||
attributes.array.push_back(anAttribute);
|
||||
});
|
||||
root.map["attributes"] = attributes;
|
||||
return root;
|
||||
}
|
||||
|
||||
closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
|
||||
Node patchNode;
|
||||
patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
|
||||
patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
|
||||
patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
|
||||
root.map["dyld-cache-fixups"].array.push_back(patchNode);
|
||||
});
|
||||
|
||||
Image::ResolvedSymbolTarget entry;
|
||||
if ( closure->mainEntry(entry) )
|
||||
root.map["main"].value = printTarget(imagesArrays, entry);
|
||||
else if ( closure->startEntry(entry) )
|
||||
root.map["start"].value = printTarget(imagesArrays, entry);
|
||||
|
||||
Image::ResolvedSymbolTarget libdyldEntry;
|
||||
closure->libDyldEntry(libdyldEntry);
|
||||
root.map["libdyld-entry"].value = printTarget(imagesArrays, libdyldEntry);
|
||||
|
||||
root.map["uses-@paths"].value = (closure->usedAtPaths() ? "true" : "false");
|
||||
root.map["uses-fallback-paths"].value = (closure->usedFallbackPaths() ? "true" : "false");
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
// add skipped files array if they exist
|
||||
closure->forEachSkipIfExistsFile(^(const LaunchClosure::SkippedFile &file, bool &stop) {
|
||||
Node fileNode;
|
||||
fileNode.map["path"].value = file.path;
|
||||
fileNode.map["file-mod-time"].value = hex(file.mtime);
|
||||
fileNode.map["file-inode"].value = hex(file.inode);
|
||||
root.map["skipped-existing-files"].array.push_back(fileNode);
|
||||
});
|
||||
|
||||
// add interposing info, if any
|
||||
closure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool& stop) {
|
||||
Node tupleNode;
|
||||
tupleNode.map["stock"].value = printTarget(imagesArrays, tuple.stockImplementation);
|
||||
tupleNode.map["replace"].value = printTarget(imagesArrays, tuple.newImplementation);
|
||||
root.map["interposing-tuples"].array.push_back(tupleNode);
|
||||
});
|
||||
|
||||
closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
|
||||
Node patchNode;
|
||||
patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
|
||||
patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
|
||||
patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
|
||||
root.map["dyld-cache-fixups"].array.push_back(patchNode);
|
||||
});
|
||||
|
||||
root.map["initial-image-count"].value = decimal(closure->initialLoadCount());
|
||||
|
||||
// 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;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasPreoptimizedObjCSelectors) {
|
||||
__block Node selectorsNode;
|
||||
selectorHashTable->forEachString(selectorImages,
|
||||
^(uint64_t selVMOffset, ImageNum imageNum) {
|
||||
// Convert to a target we can get a real name for
|
||||
dyld3::closure::Image::ResolvedSymbolTarget target;
|
||||
target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
|
||||
target.image.imageNum = imageNum;
|
||||
target.image.offset = selVMOffset;
|
||||
|
||||
Node targetNode;
|
||||
targetNode.value = printTarget(imagesArrays, target);
|
||||
selectorsNode.array.push_back(targetNode);
|
||||
});
|
||||
|
||||
root.map["objc-selectors"] = selectorsNode;
|
||||
}
|
||||
|
||||
Array<Image::ObjCClassImage> classImages;
|
||||
const ObjCClassOpt* classHashTable = nullptr;
|
||||
const ObjCClassOpt* protocolHashTable = nullptr;
|
||||
if (closure->classAndProtocolHashTables(classImages, classHashTable, protocolHashTable)) {
|
||||
if ( classHashTable != nullptr ) {
|
||||
__block Node classesNode;
|
||||
|
||||
classHashTable->forEachClass(classImages,
|
||||
^(uint64_t classNameVMOffset, ImageNum imageNum) {
|
||||
// Convert to a target we can get a real name for
|
||||
dyld3::closure::Image::ResolvedSymbolTarget target;
|
||||
target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
|
||||
target.image.imageNum = imageNum;
|
||||
target.image.offset = classNameVMOffset;
|
||||
|
||||
Node targetNode;
|
||||
targetNode.value = printTarget(imagesArrays, target);
|
||||
|
||||
Node classNode;
|
||||
classNode.map["name"] = targetNode;
|
||||
classesNode.array.push_back(classNode);
|
||||
},
|
||||
^(uint64_t classVMOffset, ImageNum imageNum) {
|
||||
dyld3::closure::Image::ResolvedSymbolTarget implTarget;
|
||||
implTarget.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
|
||||
implTarget.image.imageNum = imageNum;
|
||||
implTarget.image.offset = classVMOffset;
|
||||
|
||||
Node implNode;
|
||||
implNode.value = printTarget(imagesArrays, implTarget);
|
||||
classesNode.array.back().map["implementations"].array.push_back(implNode);
|
||||
});
|
||||
|
||||
root.map["objc-classes"] = classesNode;
|
||||
}
|
||||
|
||||
if ( protocolHashTable != nullptr ) {
|
||||
__block Node protocolsNode;
|
||||
|
||||
protocolHashTable->forEachClass(classImages,
|
||||
^(uint64_t protocolNameVMOffset, ImageNum imageNum) {
|
||||
// Convert to a target we can get a real name for
|
||||
dyld3::closure::Image::ResolvedSymbolTarget target;
|
||||
target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
|
||||
target.image.imageNum = imageNum;
|
||||
target.image.offset = protocolNameVMOffset;
|
||||
|
||||
Node targetNode;
|
||||
targetNode.value = printTarget(imagesArrays, target);
|
||||
|
||||
Node protocolNode;
|
||||
protocolNode.map["name"] = targetNode;
|
||||
protocolsNode.array.push_back(protocolNode);
|
||||
},
|
||||
^(uint64_t protocolVMOffset, ImageNum imageNum) {
|
||||
dyld3::closure::Image::ResolvedSymbolTarget implTarget;
|
||||
implTarget.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
|
||||
implTarget.image.imageNum = imageNum;
|
||||
implTarget.image.offset = protocolVMOffset;
|
||||
|
||||
Node implNode;
|
||||
implNode.value = printTarget(imagesArrays, implTarget);
|
||||
protocolsNode.array.back().map["implementations"].array.push_back(implNode);
|
||||
});
|
||||
|
||||
root.map["objc-protocols"] = protocolsNode;
|
||||
}
|
||||
}
|
||||
|
||||
const ObjCClassDuplicatesOpt* duplicateClassesHashTable = nullptr;
|
||||
closure->duplicateClassesHashTable(duplicateClassesHashTable);
|
||||
if ( duplicateClassesHashTable != nullptr ) {
|
||||
__block Node duplicateClassesNode;
|
||||
duplicateClassesHashTable->forEachClass(^(Image::ObjCDuplicateClass duplicateClass) {
|
||||
objc_opt::objc_clsopt_t* clsOpt = dyldCache->objcOpt()->clsopt();
|
||||
const char* className = clsOpt->getClassNameForIndex(duplicateClass.sharedCacheClassOptIndex);
|
||||
const void* classImpl = clsOpt->getClassForIndex(duplicateClass.sharedCacheClassOptIndex, duplicateClass.sharedCacheClassDuplicateIndex);
|
||||
|
||||
// Convert to a target we can get a real name for
|
||||
dyld3::closure::Image::ResolvedSymbolTarget target;
|
||||
target.sharedCache.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache;
|
||||
target.sharedCache.offset = (uint64_t)classImpl - (uint64_t)dyldCache;
|
||||
|
||||
Node targetNode;
|
||||
targetNode.value = printTarget(imagesArrays, target);
|
||||
|
||||
Node duplicateClassNode;
|
||||
duplicateClassNode.map["name"].value = className;
|
||||
duplicateClassNode.map["implementation"] = targetNode;
|
||||
duplicateClassNode.array.push_back(targetNode);
|
||||
|
||||
duplicateClassesNode.array.push_back(duplicateClassNode);
|
||||
});
|
||||
|
||||
root.map["objc-duplicate-classes"] = duplicateClassesNode;
|
||||
}
|
||||
|
||||
// add warnings for objc if they exist
|
||||
closure->forEachWarning(Closure::Warning::duplicateObjCClass, ^(const char *warning, bool &stop) {
|
||||
Node warningNode;
|
||||
warningNode.value = warning;
|
||||
root.map["objc-duplicate-class-warnings"].array.push_back(warningNode);
|
||||
});
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
// 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["group-num"].value = decimal(closure.group().groupNum());
|
||||
|
||||
|
||||
__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;
|
||||
#endif
|
||||
return root;
|
||||
}
|
||||
|
||||
void printImageAsJSON(const Image* image, const Array<const ImageArray*>& imagesArrays,
|
||||
bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
|
||||
{
|
||||
Node root = buildImageNode(image, imagesArrays, printFixups, false, printRaw, dyldCache, nullptr, {});
|
||||
printJSON(root, 0, out);
|
||||
}
|
||||
|
||||
void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups, bool printRaw, std::ostream& out)
|
||||
{
|
||||
const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
|
||||
STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
|
||||
imagesArrays.push_back(dylibs);
|
||||
|
||||
Node root = buildImageArrayNode(dylibs, imagesArrays, printFixups, false, printRaw, dyldCache, nullptr, {});
|
||||
printJSON(root, 0, out);
|
||||
}
|
||||
|
||||
void printClosureAsJSON(const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays,
|
||||
bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
|
||||
{
|
||||
Node root = buildClosureNode(cls, imagesArrays, printFixups, false, printRaw, dyldCache);
|
||||
printJSON(root, 0, out);
|
||||
}
|
||||
|
||||
void printClosureAsJSON(const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays,
|
||||
bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
|
||||
{
|
||||
Node root = buildClosureNode(cls, imagesArrays, printFixups, printRaw, false);
|
||||
printJSON(root, 0, out);
|
||||
}
|
||||
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
54
dyld3/ClosurePrinter.h
Normal file
54
dyld3/ClosurePrinter.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 ClosurePrinter_h
|
||||
#define ClosurePrinter_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "Closure.h"
|
||||
#include "DyldSharedCache.h"
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
|
||||
void printClosureAsJSON( const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups=false,
|
||||
bool printRaw = false, const DyldSharedCache* dyldCache=nullptr, std::ostream& out = std::cout);
|
||||
void printClosureAsJSON( const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups=false,
|
||||
bool printRaw = false, const DyldSharedCache* dyldCache=nullptr, std::ostream& out = std::cout);
|
||||
void printImageAsJSON( const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups=false,
|
||||
bool printRaw = false, const DyldSharedCache* dyldCache=nullptr, std::ostream& out = std::cout);
|
||||
|
||||
void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups=false, bool printRaw = false,
|
||||
std::ostream& out = std::cout);
|
||||
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif /* ClosurePrinter_h */
|
717
dyld3/ClosureWriter.cpp
Normal file
717
dyld3/ClosureWriter.cpp
Normal file
@ -0,0 +1,717 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <mach/vm_page_size.h>
|
||||
|
||||
#include "ClosureWriter.h"
|
||||
#include "MachOFile.h"
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
|
||||
//////////////////////////// ContainerTypedBytesWriter ////////////////////////////////////////
|
||||
|
||||
|
||||
void ContainerTypedBytesWriter::setContainerType(TypedBytes::Type containerType)
|
||||
{
|
||||
assert(_vmAllocationStart == 0);
|
||||
_vmAllocationSize = 1024 * 1024;
|
||||
vm_address_t allocationAddr;
|
||||
::vm_allocate(mach_task_self(), &allocationAddr, _vmAllocationSize, VM_FLAGS_ANYWHERE);
|
||||
assert(allocationAddr != 0);
|
||||
_vmAllocationStart = (void*)allocationAddr;
|
||||
_containerTypedBytes = (TypedBytes*)_vmAllocationStart;
|
||||
_containerTypedBytes->type = containerType;
|
||||
_containerTypedBytes->payloadLength = 0;
|
||||
_end = (uint8_t*)_vmAllocationStart + sizeof(TypedBytes);
|
||||
}
|
||||
|
||||
void* ContainerTypedBytesWriter::append(TypedBytes::Type t, const void* payload, uint32_t payloadSize)
|
||||
{
|
||||
assert((payloadSize & 0x3) == 0);
|
||||
if ( (uint8_t*)_end + payloadSize >= (uint8_t*)_vmAllocationStart + _vmAllocationSize ) {
|
||||
// if current buffer too small, grow it
|
||||
size_t growth = _vmAllocationSize;
|
||||
if ( growth < payloadSize )
|
||||
growth = _vmAllocationSize*((payloadSize/_vmAllocationSize)+1);
|
||||
vm_address_t newAllocationAddr;
|
||||
size_t newAllocationSize = _vmAllocationSize+growth;
|
||||
::vm_allocate(mach_task_self(), &newAllocationAddr, newAllocationSize, VM_FLAGS_ANYWHERE);
|
||||
assert(newAllocationAddr != 0);
|
||||
size_t currentInUse = (char*)_end - (char*)_vmAllocationStart;
|
||||
memcpy((void*)newAllocationAddr, _vmAllocationStart, currentInUse);
|
||||
::vm_deallocate(mach_task_self(), (vm_address_t)_vmAllocationStart, _vmAllocationSize);
|
||||
_end = (void*)(newAllocationAddr + currentInUse);
|
||||
_vmAllocationStart = (void*)newAllocationAddr;
|
||||
_containerTypedBytes = (TypedBytes*)_vmAllocationStart;
|
||||
_vmAllocationSize = newAllocationSize;
|
||||
}
|
||||
assert( (uint8_t*)_end + payloadSize < (uint8_t*)_vmAllocationStart + _vmAllocationSize);
|
||||
TypedBytes* tb = (TypedBytes*)_end;
|
||||
tb->type = t;
|
||||
tb->payloadLength = payloadSize;
|
||||
if ( payload != nullptr )
|
||||
::memcpy(tb->payload(), payload, payloadSize);
|
||||
_end = (uint8_t*)_end + sizeof(TypedBytes) + payloadSize;
|
||||
assert((_containerTypedBytes->payloadLength + sizeof(TypedBytes) + payloadSize) < (16 * 1024 * 1024));
|
||||
_containerTypedBytes->payloadLength += sizeof(TypedBytes) + payloadSize;
|
||||
return tb->payload();
|
||||
}
|
||||
|
||||
const void* ContainerTypedBytesWriter::finalizeContainer()
|
||||
{
|
||||
// trim vm allocation down to just what is needed
|
||||
uintptr_t bufferStart = (uintptr_t)_vmAllocationStart;
|
||||
uintptr_t used = round_page((uintptr_t)_end - bufferStart);
|
||||
if ( used < _vmAllocationSize ) {
|
||||
uintptr_t deallocStart = bufferStart + used;
|
||||
::vm_deallocate(mach_task_self(), deallocStart, _vmAllocationSize - used);
|
||||
_end = nullptr;
|
||||
_vmAllocationSize = used;
|
||||
}
|
||||
// mark vm region read-only
|
||||
::vm_protect(mach_task_self(), bufferStart, used, false, VM_PROT_READ);
|
||||
return (void*)_vmAllocationStart;
|
||||
}
|
||||
|
||||
const void* ContainerTypedBytesWriter::currentTypedBytes()
|
||||
{
|
||||
return (void*)_vmAllocationStart;
|
||||
}
|
||||
|
||||
void ContainerTypedBytesWriter::deallocate()
|
||||
{
|
||||
::vm_deallocate(mach_task_self(), (long)_vmAllocationStart, _vmAllocationSize);
|
||||
}
|
||||
|
||||
//////////////////////////// ImageWriter ////////////////////////////////////////
|
||||
|
||||
|
||||
const Image* ImageWriter::finalize()
|
||||
{
|
||||
return (Image*)finalizeContainer();
|
||||
}
|
||||
|
||||
const Image* ImageWriter::currentImage()
|
||||
{
|
||||
return (Image*)currentTypedBytes();
|
||||
}
|
||||
|
||||
void ImageWriter::addPath(const char* path)
|
||||
{
|
||||
uint32_t roundedPathLen = ((uint32_t)strlen(path) + 1 + 3) & (-4);
|
||||
Image::PathAndHash* ph = (Image::PathAndHash*)append(TypedBytes::Type::pathWithHash, nullptr, sizeof(Image::PathAndHash)+roundedPathLen);
|
||||
ph->hash = Image::hashFunction(path);
|
||||
strcpy(ph->path, path);
|
||||
}
|
||||
|
||||
Image::Flags& ImageWriter::getFlags()
|
||||
{
|
||||
if ( _flagsOffset == -1 ) {
|
||||
setContainerType(TypedBytes::Type::image);
|
||||
Image::Flags flags;
|
||||
::bzero(&flags, sizeof(flags));
|
||||
uint8_t* p = (uint8_t*)append(TypedBytes::Type::imageFlags, &flags, sizeof(flags));
|
||||
_flagsOffset = (int)(p - (uint8_t*)currentTypedBytes());
|
||||
}
|
||||
return *((Image::Flags*)((uint8_t*)currentTypedBytes() + _flagsOffset));
|
||||
}
|
||||
|
||||
void ImageWriter::setImageNum(ImageNum num)
|
||||
{
|
||||
getFlags().imageNum = num;
|
||||
}
|
||||
|
||||
void ImageWriter::setHasObjC(bool value)
|
||||
{
|
||||
getFlags().hasObjC = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setIs64(bool value)
|
||||
{
|
||||
getFlags().is64 = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setHasPlusLoads(bool value)
|
||||
{
|
||||
getFlags().mayHavePlusLoads = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setIsBundle(bool value)
|
||||
{
|
||||
getFlags().isBundle = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setIsDylib(bool value)
|
||||
{
|
||||
getFlags().isDylib = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setIsExecutable(bool value)
|
||||
{
|
||||
getFlags().isExecutable = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setHasWeakDefs(bool value)
|
||||
{
|
||||
getFlags().hasWeakDefs = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setUses16KPages(bool value)
|
||||
{
|
||||
getFlags().has16KBpages = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setOverridableDylib(bool value)
|
||||
{
|
||||
getFlags().overridableDylib = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setInvalid()
|
||||
{
|
||||
getFlags().isInvalid = true;
|
||||
}
|
||||
|
||||
void ImageWriter::setInDyldCache(bool value)
|
||||
{
|
||||
getFlags().inDyldCache = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setHasPrecomputedObjC(bool value)
|
||||
{
|
||||
getFlags().hasPrecomputedObjC = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setNeverUnload(bool value)
|
||||
{
|
||||
getFlags().neverUnload = value;
|
||||
}
|
||||
|
||||
void ImageWriter::setUUID(const uuid_t uuid)
|
||||
{
|
||||
append(TypedBytes::Type::uuid, uuid, sizeof(uuid_t));
|
||||
}
|
||||
|
||||
void ImageWriter::addCDHash(const uint8_t cdHash[20])
|
||||
{
|
||||
append(TypedBytes::Type::cdHash, cdHash, 20);
|
||||
}
|
||||
|
||||
void ImageWriter::setDependents(const Array<Image::LinkedImage>& deps)
|
||||
{
|
||||
append(TypedBytes::Type::dependents, deps.begin(), (uint32_t)deps.count()*sizeof(Image::LinkedImage));
|
||||
}
|
||||
|
||||
void ImageWriter::setDofOffsets(const Array<uint32_t>& dofSectionOffsets)
|
||||
{
|
||||
append(TypedBytes::Type::dofOffsets, &dofSectionOffsets[0], (uint32_t)dofSectionOffsets.count()*sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void ImageWriter::setInitOffsets(const uint32_t initOffsets[], uint32_t count)
|
||||
{
|
||||
append(TypedBytes::Type::initOffsets, initOffsets, count*sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void ImageWriter::setTermOffsets(const uint32_t termOffsets[], uint32_t count)
|
||||
{
|
||||
getFlags().hasTerminators = true;
|
||||
append(TypedBytes::Type::termOffsets, termOffsets, count*sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void ImageWriter::setInitSectRange(uint32_t sectionOffset, uint32_t sectionSize)
|
||||
{
|
||||
Image::InitializerSectionRange range = { sectionOffset, sectionSize };
|
||||
append(TypedBytes::Type::initsSection, &range, sizeof(Image::InitializerSectionRange));
|
||||
}
|
||||
|
||||
void ImageWriter::setDiskSegments(const Image::DiskSegment segs[], uint32_t count)
|
||||
{
|
||||
append(TypedBytes::Type::diskSegment, segs, count*sizeof(Image::DiskSegment));
|
||||
for (uint32_t i=0; i < count; ++i) {
|
||||
if ( segs[i].permissions == Image::DiskSegment::kReadOnlyDataPermissions )
|
||||
getFlags().hasReadOnlyData = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageWriter::setCachedSegments(const Image::DyldCacheSegment segs[], uint32_t count)
|
||||
{
|
||||
append(TypedBytes::Type::cacheSegment, segs, count*sizeof(Image::DyldCacheSegment));
|
||||
}
|
||||
|
||||
void ImageWriter::setCodeSignatureLocation(uint32_t fileOffset, uint32_t size)
|
||||
{
|
||||
Image::CodeSignatureLocation loc;
|
||||
loc.fileOffset = fileOffset;
|
||||
loc.fileSize = size;
|
||||
append(TypedBytes::Type::codeSignLoc, &loc, sizeof(loc));
|
||||
}
|
||||
|
||||
void ImageWriter::setFairPlayEncryptionRange(uint32_t fileOffset, uint32_t size)
|
||||
{
|
||||
Image::FairPlayRange loc;
|
||||
loc.rangeStart = fileOffset;
|
||||
loc.rangeLength = size;
|
||||
append(TypedBytes::Type::fairPlayLoc, &loc, sizeof(loc));
|
||||
}
|
||||
|
||||
void ImageWriter::setMappingInfo(uint64_t sliceOffset, uint64_t vmSize)
|
||||
{
|
||||
const uint32_t pageSize = getFlags().has16KBpages ? 0x4000 : 0x1000;
|
||||
Image::MappingInfo info;
|
||||
info.sliceOffsetIn4K = (uint32_t)(sliceOffset / 0x1000);
|
||||
info.totalVmPages = (uint32_t)(vmSize / pageSize);
|
||||
append(TypedBytes::Type::mappingInfo, &info, sizeof(info));
|
||||
}
|
||||
|
||||
void ImageWriter::setFileInfo(uint64_t inode, uint64_t mTime)
|
||||
{
|
||||
Image::FileInfo info = { inode, mTime };
|
||||
append(TypedBytes::Type::fileInodeAndTime, &info, sizeof(info));
|
||||
}
|
||||
|
||||
void ImageWriter::setRebaseInfo(const Array<Image::RebasePattern>& fixups)
|
||||
{
|
||||
append(TypedBytes::Type::rebaseFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::RebasePattern));
|
||||
}
|
||||
|
||||
void ImageWriter::setTextRebaseInfo(const Array<Image::TextFixupPattern>& fixups)
|
||||
{
|
||||
append(TypedBytes::Type::textFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::TextFixupPattern));
|
||||
}
|
||||
|
||||
void ImageWriter::setBindInfo(const Array<Image::BindPattern>& fixups)
|
||||
{
|
||||
append(TypedBytes::Type::bindFixups, fixups.begin(), (uint32_t)fixups.count()*sizeof(Image::BindPattern));
|
||||
}
|
||||
|
||||
void ImageWriter::setChainedFixups(uint64_t runtimeStartsStructOffset, const Array<Image::ResolvedSymbolTarget>& targets)
|
||||
{
|
||||
getFlags().hasChainedFixups = true;
|
||||
append(TypedBytes::Type::chainedStartsOffset, &runtimeStartsStructOffset, sizeof(uint64_t));
|
||||
append(TypedBytes::Type::chainedFixupsTargets, targets.begin(), (uint32_t)targets.count()*sizeof(Image::ResolvedSymbolTarget));
|
||||
}
|
||||
|
||||
void ImageWriter::setObjCFixupInfo(const Image::ResolvedSymbolTarget& objcProtocolClassTarget,
|
||||
uint64_t objcImageInfoVMOffset,
|
||||
const Array<Image::ProtocolISAFixup>& protocolISAFixups,
|
||||
const Array<Image::SelectorReferenceFixup>& selRefFixups,
|
||||
const Array<Image::ClassStableSwiftFixup>& classStableSwiftFixups,
|
||||
const Array<Image::MethodListFixup>& methodListFixups)
|
||||
{
|
||||
// The layout here is:
|
||||
// ResolvedSymbolTarget
|
||||
// uint64_t vmOffset to objc_imageinfo
|
||||
// uint32_t protocol count
|
||||
// uint32_t selector reference count
|
||||
// array of ProtocolISAFixup
|
||||
// array of SelectorReferenceFixup
|
||||
// optional uint32_t stable swift fixup count
|
||||
// optional uint32_t method list fixup count
|
||||
// optional array of ClassStableSwiftFixup
|
||||
// optional array of MethodListFixup
|
||||
|
||||
uint64_t headerSize = sizeof(Image::ResolvedSymbolTarget) + sizeof(uint64_t) + (sizeof(uint32_t) * 4);
|
||||
uint64_t protocolsSize = (sizeof(Image::ProtocolISAFixup) * protocolISAFixups.count());
|
||||
uint64_t selRefsSize = (sizeof(Image::SelectorReferenceFixup) * selRefFixups.count());
|
||||
uint64_t stableSwiftSize = (sizeof(Image::ClassStableSwiftFixup) * classStableSwiftFixups.count());
|
||||
uint64_t methodListSize = (sizeof(Image::MethodListFixup) * methodListFixups.count());
|
||||
|
||||
uint64_t totalSize = headerSize + protocolsSize + selRefsSize + stableSwiftSize + methodListSize;
|
||||
assert( (totalSize & 3) == 0);
|
||||
uint8_t* buffer = (uint8_t*)append(TypedBytes::Type::objcFixups, nullptr, (uint32_t)totalSize);
|
||||
|
||||
// Set the statically sized data
|
||||
uint32_t protocolFixupCount = (uint32_t)protocolISAFixups.count();
|
||||
uint32_t selRefFixupCount = (uint32_t)selRefFixups.count();
|
||||
memcpy(buffer, &objcProtocolClassTarget, sizeof(Image::ResolvedSymbolTarget));
|
||||
buffer += sizeof(Image::ResolvedSymbolTarget);
|
||||
memcpy(buffer, &objcImageInfoVMOffset, sizeof(uint64_t));
|
||||
buffer += sizeof(uint64_t);
|
||||
memcpy(buffer, &protocolFixupCount, sizeof(uint32_t));
|
||||
buffer += sizeof(uint32_t);
|
||||
memcpy(buffer, &selRefFixupCount, sizeof(uint32_t));
|
||||
buffer += sizeof(uint32_t);
|
||||
|
||||
// Set the protocol fixups
|
||||
if ( protocolFixupCount != 0 ) {
|
||||
memcpy(buffer, protocolISAFixups.begin(), (size_t)protocolsSize);
|
||||
buffer += protocolsSize;
|
||||
}
|
||||
|
||||
// Set the selector reference fixups
|
||||
if ( selRefFixupCount != 0 ) {
|
||||
memcpy(buffer, selRefFixups.begin(), (size_t)selRefsSize);
|
||||
buffer += selRefsSize;
|
||||
}
|
||||
|
||||
// New closures get additional fixups. These are ignored by old dyld's
|
||||
uint32_t stableSwiftFixupCount = (uint32_t)classStableSwiftFixups.count();
|
||||
uint32_t methodListFixupCount = (uint32_t)methodListFixups.count();
|
||||
memcpy(buffer, &stableSwiftFixupCount, sizeof(uint32_t));
|
||||
buffer += sizeof(uint32_t);
|
||||
memcpy(buffer, &methodListFixupCount, sizeof(uint32_t));
|
||||
buffer += sizeof(uint32_t);
|
||||
|
||||
// Set the stable swift fixups
|
||||
if ( stableSwiftFixupCount != 0 ) {
|
||||
memcpy(buffer, classStableSwiftFixups.begin(), (size_t)stableSwiftSize);
|
||||
buffer += stableSwiftSize;
|
||||
}
|
||||
|
||||
// Set the method list fixups
|
||||
if ( methodListFixupCount != 0 ) {
|
||||
memcpy(buffer, methodListFixups.begin(), (size_t)methodListSize);
|
||||
buffer += methodListSize;
|
||||
}
|
||||
}
|
||||
|
||||
void ImageWriter::setAsOverrideOf(ImageNum imageNum)
|
||||
{
|
||||
uint32_t temp = imageNum;
|
||||
append(TypedBytes::Type::imageOverride, &temp, sizeof(temp));
|
||||
}
|
||||
|
||||
void ImageWriter::setInitsOrder(const ImageNum images[], uint32_t count)
|
||||
{
|
||||
append(TypedBytes::Type::initBefores, images, count*sizeof(ImageNum));
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////// ImageArrayWriter ////////////////////////////////////////
|
||||
|
||||
|
||||
ImageArrayWriter::ImageArrayWriter(ImageNum startImageNum, unsigned count, bool hasRoots) : _index(0)
|
||||
{
|
||||
setContainerType(TypedBytes::Type::imageArray);
|
||||
_end = (void*)((uint8_t*)_end + sizeof(ImageArray) - sizeof(TypedBytes) + sizeof(uint32_t)*count);
|
||||
_containerTypedBytes->payloadLength = sizeof(ImageArray) - sizeof(TypedBytes) + sizeof(uint32_t)*count;
|
||||
ImageArray* ia = (ImageArray*)_containerTypedBytes;
|
||||
ia->firstImageNum = startImageNum;
|
||||
ia->count = count;
|
||||
ia->hasRoots = hasRoots;
|
||||
}
|
||||
|
||||
void ImageArrayWriter::appendImage(const Image* image)
|
||||
{
|
||||
ImageArray* ia = (ImageArray*)_containerTypedBytes;
|
||||
ia->offsets[_index++] = _containerTypedBytes->payloadLength;
|
||||
append(TypedBytes::Type::image, image->payload(), image->payloadLength);
|
||||
}
|
||||
|
||||
const ImageArray* ImageArrayWriter::finalize()
|
||||
{
|
||||
return (ImageArray*)finalizeContainer();
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////// ClosureWriter ////////////////////////////////////////
|
||||
|
||||
void ClosureWriter::setTopImageNum(ImageNum imageNum)
|
||||
{
|
||||
append(TypedBytes::Type::topImage, &imageNum, sizeof(ImageNum));
|
||||
}
|
||||
|
||||
void ClosureWriter::addCachePatches(const Array<Closure::PatchEntry>& patches)
|
||||
{
|
||||
append(TypedBytes::Type::cacheOverrides, patches.begin(), (uint32_t)patches.count()*sizeof(Closure::PatchEntry));
|
||||
}
|
||||
|
||||
void ClosureWriter::applyInterposing(const LaunchClosure* launchClosure)
|
||||
{
|
||||
const Closure* currentClosure = (Closure*)currentTypedBytes();
|
||||
const ImageArray* images = currentClosure->images();
|
||||
launchClosure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool&) {
|
||||
images->forEachImage(^(const dyld3::closure::Image* image, bool&) {
|
||||
for (const Image::BindPattern& bindPat : image->bindFixups()) {
|
||||
if ( (bindPat.target == tuple.stockImplementation) && (tuple.newImplementation.image.imageNum != image->imageNum()) ) {
|
||||
Image::BindPattern* writePat = const_cast<Image::BindPattern*>(&bindPat);
|
||||
writePat->target = tuple.newImplementation;
|
||||
}
|
||||
}
|
||||
|
||||
// Chained fixups may also be interposed. We can't change elements in the chain, but we can change
|
||||
// the target list.
|
||||
for (const Image::ResolvedSymbolTarget& symbolTarget : image->chainedTargets()) {
|
||||
if ( (symbolTarget == tuple.stockImplementation) && (tuple.newImplementation.image.imageNum != image->imageNum()) ) {
|
||||
Image::ResolvedSymbolTarget* writeTarget = const_cast<Image::ResolvedSymbolTarget*>(&symbolTarget);
|
||||
*writeTarget = tuple.newImplementation;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ClosureWriter::addWarning(Closure::Warning::Type warningType, const char* warning)
|
||||
{
|
||||
uint32_t roundedMessageLen = ((uint32_t)strlen(warning) + 1 + 3) & (-4);
|
||||
Closure::Warning* ph = (Closure::Warning*)append(TypedBytes::Type::warning, nullptr, sizeof(Closure::Warning)+roundedMessageLen);
|
||||
ph->type = warningType;
|
||||
strcpy(ph->message, warning);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////// LaunchClosureWriter ////////////////////////////////////////
|
||||
|
||||
LaunchClosureWriter::LaunchClosureWriter(const ImageArray* images)
|
||||
{
|
||||
setContainerType(TypedBytes::Type::launchClosure);
|
||||
append(TypedBytes::Type::imageArray, images->payload(), images->payloadLength);
|
||||
}
|
||||
|
||||
const LaunchClosure* LaunchClosureWriter::finalize()
|
||||
{
|
||||
return (LaunchClosure*)finalizeContainer();
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setLibSystemImageNum(ImageNum imageNum)
|
||||
{
|
||||
append(TypedBytes::Type::libSystemNum, &imageNum, sizeof(ImageNum));
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setLibDyldEntry(Image::ResolvedSymbolTarget entry)
|
||||
{
|
||||
append(TypedBytes::Type::libDyldEntry, &entry, sizeof(entry));
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setMainEntry(Image::ResolvedSymbolTarget main)
|
||||
{
|
||||
append(TypedBytes::Type::mainEntry, &main, sizeof(main));
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setStartEntry(Image::ResolvedSymbolTarget start)
|
||||
{
|
||||
append(TypedBytes::Type::startEntry, &start, sizeof(start));
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setUsedFallbackPaths(bool value)
|
||||
{
|
||||
getFlags().usedFallbackPaths = value;
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setUsedAtPaths(bool value)
|
||||
{
|
||||
getFlags().usedAtPaths = value;
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setHasInsertedLibraries(bool value)
|
||||
{
|
||||
getFlags().hasInsertedLibraries = value;
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setInitImageCount(uint32_t count)
|
||||
{
|
||||
getFlags().initImageCount = count;
|
||||
}
|
||||
|
||||
LaunchClosure::Flags& LaunchClosureWriter::getFlags()
|
||||
{
|
||||
if ( _flagsOffset == -1 ) {
|
||||
LaunchClosure::Flags flags;
|
||||
::bzero(&flags, sizeof(flags));
|
||||
uint8_t* p = (uint8_t*)append(TypedBytes::Type::closureFlags, &flags, sizeof(flags));
|
||||
_flagsOffset = (int)(p - (uint8_t*)currentTypedBytes());
|
||||
}
|
||||
return *((LaunchClosure::Flags*)((uint8_t*)currentTypedBytes() + _flagsOffset));
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setMustBeMissingFiles(const Array<const char*>& paths)
|
||||
{
|
||||
uint32_t totalSize = 0;
|
||||
for (const char* s : paths)
|
||||
totalSize += (strlen(s) +1);
|
||||
totalSize = (totalSize + 3) & (-4); // align
|
||||
|
||||
char* buffer = (char*)append(TypedBytes::Type::missingFiles, nullptr, totalSize);
|
||||
char* t = buffer;
|
||||
for (const char* path : paths) {
|
||||
for (const char* s=path; *s != '\0'; ++s)
|
||||
*t++ = *s;
|
||||
*t++ = '\0';
|
||||
}
|
||||
while (t < &buffer[totalSize])
|
||||
*t++ = '\0';
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setMustExistFiles(const Array<LaunchClosure::SkippedFile>& files)
|
||||
{
|
||||
// Start the structure with a count
|
||||
uint32_t totalSize = sizeof(uint64_t);
|
||||
|
||||
// Then make space for the array of mod times and inode numbers
|
||||
totalSize += files.count() * sizeof(uint64_t) * 2;
|
||||
|
||||
// Then the array of paths on the end
|
||||
for (const LaunchClosure::SkippedFile& file : files)
|
||||
totalSize += (strlen(file.path) + 1);
|
||||
totalSize = (totalSize + 3) & (-4); // align
|
||||
|
||||
char* buffer = (char*)append(TypedBytes::Type::existingFiles, nullptr, totalSize);
|
||||
|
||||
// Set the size
|
||||
uint64_t* bufferPtr = (uint64_t*)buffer;
|
||||
*bufferPtr++ = (uint64_t)files.count();
|
||||
|
||||
// And the array of mod times and inode numbers
|
||||
for (const LaunchClosure::SkippedFile& file : files) {
|
||||
*bufferPtr++ = file.inode;
|
||||
*bufferPtr++ = file.mtime;
|
||||
}
|
||||
|
||||
char* t = (char*)bufferPtr;
|
||||
for (const LaunchClosure::SkippedFile& file : files) {
|
||||
for (const char* s=file.path; *s != '\0'; ++s)
|
||||
*t++ = *s;
|
||||
*t++ = '\0';
|
||||
}
|
||||
while (t < &buffer[totalSize])
|
||||
*t++ = '\0';
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::addEnvVar(const char* envVar)
|
||||
{
|
||||
unsigned len = (unsigned)strlen(envVar);
|
||||
char temp[len+8];
|
||||
strcpy(temp, envVar);
|
||||
unsigned paddedSize = len+1;
|
||||
while ( (paddedSize % 4) != 0 )
|
||||
temp[paddedSize++] = '\0';
|
||||
append(TypedBytes::Type::envVar, temp, paddedSize);
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::addInterposingTuples(const Array<InterposingTuple>& tuples)
|
||||
{
|
||||
append(TypedBytes::Type::interposeTuples, tuples.begin(), (uint32_t)tuples.count()*sizeof(InterposingTuple));
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setDyldCacheUUID(const uuid_t uuid)
|
||||
{
|
||||
append(TypedBytes::Type::dyldCacheUUID, uuid, sizeof(uuid_t));
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setBootUUID(const char* uuid)
|
||||
{
|
||||
unsigned len = (unsigned)strlen(uuid);
|
||||
char temp[len+8];
|
||||
strcpy(temp, uuid);
|
||||
unsigned paddedSize = len+1;
|
||||
while ( (paddedSize % 4) != 0 )
|
||||
temp[paddedSize++] = '\0';
|
||||
append(TypedBytes::Type::bootUUID, temp, paddedSize);
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setObjCSelectorInfo(const Array<uint8_t>& hashTable, const Array<Image::ObjCSelectorImage>& hashTableImages) {
|
||||
uint32_t count = (uint32_t)hashTableImages.count();
|
||||
uint32_t totalSize = (uint32_t)(sizeof(count) + (sizeof(Image::ObjCSelectorImage) * count) + hashTable.count());
|
||||
totalSize = (totalSize + 3) & (-4); // align
|
||||
uint8_t* buffer = (uint8_t*)append(TypedBytes::Type::selectorTable, nullptr, totalSize);
|
||||
|
||||
// Write out out the image count
|
||||
memcpy(buffer, &count, sizeof(count));
|
||||
buffer += sizeof(count);
|
||||
|
||||
// Write out out the image nums
|
||||
memcpy(buffer, hashTableImages.begin(), sizeof(Image::ObjCSelectorImage) * count);
|
||||
buffer += sizeof(Image::ObjCSelectorImage) * count;
|
||||
|
||||
// Write out out the image count
|
||||
memcpy(buffer, hashTable.begin(), hashTable.count());
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setObjCClassAndProtocolInfo(const Array<uint8_t>& classHashTable, const Array<uint8_t>& protocolHashTable,
|
||||
const Array<Image::ObjCClassImage>& hashTableImages) {
|
||||
// The layout here is:
|
||||
// uint32_t offset to class table (note this is 0 if there are no classes)
|
||||
// uint32_t offset to protocol table (note this is 0 if there are no protocols)
|
||||
// uint32_t num images
|
||||
// ObjCClassImage[num images]
|
||||
// class hash table
|
||||
// [ padding to 4-byte alignment if needed
|
||||
// protocol hash table
|
||||
// [ padding to 4-byte alignment if needed
|
||||
|
||||
uint32_t numImages = (uint32_t)hashTableImages.count();
|
||||
|
||||
uint32_t headerSize = sizeof(uint32_t) * 3;
|
||||
uint32_t imagesSize = (sizeof(Image::ObjCClassImage) * numImages);
|
||||
uint32_t classTableSize = ((uint32_t)classHashTable.count() + 3) & (-4); // pad to 4-byte multiple
|
||||
uint32_t protocolTableSize = ((uint32_t)protocolHashTable.count() + 3) & (-4); // pad to 4-byte multiple
|
||||
uint32_t offsetToClassTable = (classTableSize == 0) ? 0 : (headerSize + imagesSize);
|
||||
uint32_t offsetToProtocolTable = (protocolTableSize == 0) ? 0 : (headerSize + imagesSize + classTableSize);
|
||||
|
||||
uint32_t totalSize = headerSize + imagesSize + classTableSize + protocolTableSize;
|
||||
assert( (totalSize & 3) == 0);
|
||||
uint8_t* buffer = (uint8_t*)append(TypedBytes::Type::classTable, nullptr, totalSize);
|
||||
|
||||
// Write out out the header
|
||||
memcpy(buffer + 0, &offsetToClassTable, sizeof(uint32_t));
|
||||
memcpy(buffer + 4, &offsetToProtocolTable, sizeof(uint32_t));
|
||||
memcpy(buffer + 8, &numImages, sizeof(uint32_t));
|
||||
|
||||
// Write out out the image nums
|
||||
memcpy(buffer + headerSize, hashTableImages.begin(), imagesSize);
|
||||
|
||||
// Write out out the class hash table
|
||||
if ( offsetToClassTable != 0 )
|
||||
memcpy(buffer + offsetToClassTable, classHashTable.begin(), classHashTable.count());
|
||||
|
||||
// Write out out the protocol hash table
|
||||
if ( offsetToProtocolTable != 0 )
|
||||
memcpy(buffer + offsetToProtocolTable, protocolHashTable.begin(), protocolHashTable.count());
|
||||
}
|
||||
|
||||
void LaunchClosureWriter::setObjCDuplicateClassesInfo(const Array<uint8_t>& hashTable) {
|
||||
uint32_t totalSize = (uint32_t)hashTable.count();
|
||||
totalSize = (totalSize + 3) & (-4); // align
|
||||
uint8_t* buffer = (uint8_t*)append(TypedBytes::Type::duplicateClassesTable, nullptr, totalSize);
|
||||
|
||||
// Write out out the hash table
|
||||
memcpy(buffer, hashTable.begin(), hashTable.count());
|
||||
}
|
||||
|
||||
//////////////////////////// DlopenClosureWriter ////////////////////////////////////////
|
||||
|
||||
DlopenClosureWriter::DlopenClosureWriter(const ImageArray* images)
|
||||
{
|
||||
setContainerType(TypedBytes::Type::dlopenClosure);
|
||||
append(TypedBytes::Type::imageArray, images->payload(), images->payloadLength);
|
||||
}
|
||||
|
||||
const DlopenClosure* DlopenClosureWriter::finalize()
|
||||
{
|
||||
return (DlopenClosure*)finalizeContainer();
|
||||
}
|
||||
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
|
191
dyld3/ClosureWriter.h
Normal file
191
dyld3/ClosureWriter.h
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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 ClosureWriter_h
|
||||
#define ClosureWriter_h
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include "Closure.h"
|
||||
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
namespace closure {
|
||||
|
||||
|
||||
class VIS_HIDDEN ContainerTypedBytesWriter
|
||||
{
|
||||
public:
|
||||
void deallocate();
|
||||
|
||||
protected:
|
||||
void setContainerType(TypedBytes::Type containerType);
|
||||
void* append(TypedBytes::Type t, const void* payload, uint32_t payloadSize);
|
||||
|
||||
const void* currentTypedBytes();
|
||||
const void* finalizeContainer();
|
||||
|
||||
void* _vmAllocationStart = nullptr;
|
||||
size_t _vmAllocationSize = 0;
|
||||
TypedBytes* _containerTypedBytes = nullptr;
|
||||
void* _end = nullptr;
|
||||
};
|
||||
|
||||
|
||||
class VIS_HIDDEN ImageWriter : public ContainerTypedBytesWriter
|
||||
{
|
||||
public:
|
||||
|
||||
void setImageNum(ImageNum num);
|
||||
void addPath(const char* path); // first is canonical, others are aliases
|
||||
void setInvalid();
|
||||
void setInDyldCache(bool);
|
||||
void setHasPrecomputedObjC(bool);
|
||||
void setIs64(bool);
|
||||
void setHasObjC(bool);
|
||||
void setHasPlusLoads(bool);
|
||||
void setIsBundle(bool);
|
||||
void setIsDylib(bool);
|
||||
void setIsExecutable(bool);
|
||||
void setIsRestricted(bool);
|
||||
void setHasWeakDefs(bool);
|
||||
void setUses16KPages(bool);
|
||||
void setOverridableDylib(bool);
|
||||
void setNeverUnload(bool);
|
||||
void setHasTerminators(bool);
|
||||
void setUUID(const uuid_t uuid);
|
||||
void addCDHash(const uint8_t cdHash[20]);
|
||||
void setDependents(const Array<Image::LinkedImage>& deps);
|
||||
void setDofOffsets(const Array<uint32_t>& dofSectionOffsets);
|
||||
void setInitOffsets(const uint32_t initOffsets[], uint32_t count);
|
||||
void setInitSectRange(uint32_t sectionOffset, uint32_t sectionSize);
|
||||
void setTermOffsets(const uint32_t termOffsets[], uint32_t count);
|
||||
void setDiskSegments(const Image::DiskSegment segs[], uint32_t count);
|
||||
void setCachedSegments(const Image::DyldCacheSegment segs[], uint32_t count);
|
||||
void setCodeSignatureLocation(uint32_t fileOffset, uint32_t size);
|
||||
void setFairPlayEncryptionRange(uint32_t fileOffset, uint32_t size);
|
||||
void setMappingInfo(uint64_t sliceOffset, uint64_t vmSize);
|
||||
void setFileInfo(uint64_t inode, uint64_t modTime);
|
||||
void setRebaseInfo(const Array<Image::RebasePattern>&);
|
||||
void setTextRebaseInfo(const Array<Image::TextFixupPattern>&);
|
||||
void setBindInfo(const Array<Image::BindPattern>&);
|
||||
void setObjCFixupInfo(const Image::ResolvedSymbolTarget& objcProtocolClassTarget,
|
||||
uint64_t objcImageInfoVMOffset,
|
||||
const Array<Image::ProtocolISAFixup>& protocolISAFixups,
|
||||
const Array<Image::SelectorReferenceFixup>& selRefFixups,
|
||||
const Array<Image::ClassStableSwiftFixup>& classStableSwiftFixups,
|
||||
const Array<Image::MethodListFixup>& methodListFixups);
|
||||
void setAsOverrideOf(ImageNum);
|
||||
void setInitsOrder(const ImageNum images[], uint32_t count);
|
||||
void setChainedFixups(uint64_t runtimeStartsStructOffset, const Array<Image::ResolvedSymbolTarget>& targets);
|
||||
|
||||
const Image* currentImage();
|
||||
|
||||
const Image* finalize();
|
||||
|
||||
private:
|
||||
Image::Flags& getFlags();
|
||||
|
||||
int _flagsOffset = -1;
|
||||
};
|
||||
|
||||
|
||||
class VIS_HIDDEN ImageArrayWriter : public ContainerTypedBytesWriter
|
||||
{
|
||||
public:
|
||||
ImageArrayWriter(ImageNum startImageNum, unsigned count, bool hasRoots);
|
||||
|
||||
void appendImage(const Image*);
|
||||
const ImageArray* finalize();
|
||||
private:
|
||||
unsigned _index;
|
||||
};
|
||||
|
||||
class VIS_HIDDEN ClosureWriter : public ContainerTypedBytesWriter
|
||||
{
|
||||
public:
|
||||
void setTopImageNum(ImageNum imageNum);
|
||||
void addCachePatches(const Array<Closure::PatchEntry>&);
|
||||
void applyInterposing(const LaunchClosure* launchClosure);
|
||||
void addWarning(Closure::Warning::Type type, const char* warning);
|
||||
};
|
||||
|
||||
class VIS_HIDDEN LaunchClosureWriter : public ClosureWriter
|
||||
{
|
||||
friend class ClosureBuilder;
|
||||
public:
|
||||
LaunchClosureWriter(const ImageArray* images);
|
||||
|
||||
const LaunchClosure* finalize();
|
||||
void setLibSystemImageNum(ImageNum imageNum);
|
||||
void setInitImageCount(uint32_t count);
|
||||
void setLibDyldEntry(Image::ResolvedSymbolTarget dyldEntry);
|
||||
void setMainEntry(Image::ResolvedSymbolTarget main);
|
||||
void setStartEntry(Image::ResolvedSymbolTarget start);
|
||||
void setUsedFallbackPaths(bool);
|
||||
void setUsedAtPaths(bool);
|
||||
void setHasInsertedLibraries(bool);
|
||||
void setMustBeMissingFiles(const Array<const char*>& paths);
|
||||
void setMustExistFiles(const Array<LaunchClosure::SkippedFile>& files);
|
||||
void addInterposingTuples(const Array<InterposingTuple>& tuples);
|
||||
void setDyldCacheUUID(const uuid_t);
|
||||
void setBootUUID(const char* uuid);
|
||||
void addEnvVar(const char* envVar);
|
||||
void setObjCSelectorInfo(const Array<uint8_t>& hashTable, const Array<Image::ObjCSelectorImage>& hashTableImages);
|
||||
void setObjCClassAndProtocolInfo(const Array<uint8_t>& classHashTable, const Array<uint8_t>& protocolHashTable,
|
||||
const Array<Image::ObjCClassImage>& hashTableImages);
|
||||
void setObjCDuplicateClassesInfo(const Array<uint8_t>& hashTable);
|
||||
|
||||
private:
|
||||
LaunchClosure::Flags& getFlags();
|
||||
|
||||
int _flagsOffset = -1;
|
||||
};
|
||||
|
||||
|
||||
class VIS_HIDDEN DlopenClosureWriter : public ClosureWriter
|
||||
{
|
||||
public:
|
||||
DlopenClosureWriter(const ImageArray* images);
|
||||
|
||||
const DlopenClosure* finalize();
|
||||
|
||||
};
|
||||
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif // ClosureWriter_h
|
||||
|
158
dyld3/CodeSigningTypes.h
Normal file
158
dyld3/CodeSigningTypes.h
Normal file
@ -0,0 +1,158 @@
|
||||
/* -*- 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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
//
|
||||
// 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_4K = 4096,
|
||||
CS_PAGE_SIZE_16K = 16384,
|
||||
|
||||
CS_HASHTYPE_SHA1 = 1,
|
||||
CS_HASHTYPE_SHA256 = 2,
|
||||
CS_HASHTYPE_SHA256_TRUNCATED = 3,
|
||||
CS_HASHTYPE_SHA384 = 4,
|
||||
|
||||
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_
|
||||
|
||||
|
||||
|
217
dyld3/Diagnostics.cpp
Normal file
217
dyld3/Diagnostics.cpp
Normal file
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <_simple.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <dirent.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/machine.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <pthread.h>
|
||||
#include <libc_private.h>
|
||||
|
||||
#include "Diagnostics.h"
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
#include <dispatch/dispatch.h>
|
||||
dispatch_queue_t sWarningQueue = dispatch_queue_create("com.apple.dyld.cache-builder.warnings", NULL);
|
||||
#endif
|
||||
|
||||
Diagnostics::Diagnostics(bool verbose)
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
: _verbose(verbose)
|
||||
, _prefix("")
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
Diagnostics::Diagnostics(const std::string& prefix, bool verbose)
|
||||
: _verbose(verbose)
|
||||
, _prefix(prefix)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
Diagnostics::~Diagnostics()
|
||||
{
|
||||
clearError();
|
||||
}
|
||||
|
||||
void Diagnostics::error(const char* format, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, format);
|
||||
error(format, list);
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
void Diagnostics::error(const char* format, va_list list)
|
||||
{
|
||||
//FIXME: this should be assertNoError(), but we currently overwrite some errors
|
||||
//assertNoError();
|
||||
_buffer = _simple_salloc();
|
||||
_simple_vsprintf(_buffer, format, list);
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
if ( !_verbose )
|
||||
return;
|
||||
|
||||
if (_prefix.empty()) {
|
||||
fprintf(stderr, "%s", _simple_string(_buffer));
|
||||
} else {
|
||||
fprintf(stderr, "[%s] %s", _prefix.c_str(), _simple_string(_buffer));
|
||||
}
|
||||
#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 !BUILDING_CACHE_BUILDER
|
||||
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);
|
||||
}
|
||||
free(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 "";
|
||||
}
|
||||
|
||||
const std::set<std::string> Diagnostics::warnings() const
|
||||
{
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
__block std::set<std::string> 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
|
||||
|
82
dyld3/Diagnostics.h
Normal file
82
dyld3/Diagnostics.h
Normal file
@ -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 Diagnostics_h
|
||||
#define Diagnostics_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
#include "Logging.h"
|
||||
|
||||
|
||||
class VIS_HIDDEN Diagnostics
|
||||
{
|
||||
public:
|
||||
Diagnostics(bool verbose=false);
|
||||
~Diagnostics();
|
||||
|
||||
void error(const char* format, ...) __attribute__((format(printf, 2, 3)));
|
||||
void error(const char* format, va_list list);
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
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 !BUILDING_CACHE_BUILDER
|
||||
const char* errorMessage() const;
|
||||
#else
|
||||
const std::string prefix() const;
|
||||
std::string errorMessage() const;
|
||||
const std::set<std::string> warnings() const;
|
||||
void clearWarnings();
|
||||
#endif
|
||||
|
||||
private:
|
||||
void* _buffer = nullptr;
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
std::string _prefix;
|
||||
std::set<std::string> _warnings;
|
||||
bool _verbose = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // Diagnostics_h
|
50
dyld3/JSON.h
Normal file
50
dyld3/JSON.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 __JSON_H__
|
||||
#define __JSON_H__
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace dyld3 {
|
||||
namespace json {
|
||||
|
||||
struct Node
|
||||
{
|
||||
std::string value;
|
||||
std::map<std::string, Node> map;
|
||||
std::vector<Node> array;
|
||||
};
|
||||
|
||||
|
||||
} // namespace json
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif // __JSON_H__
|
57
dyld3/JSONReader.h
Normal file
57
dyld3/JSONReader.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 __JSON_READER_H__
|
||||
#define __JSON_READER_H__
|
||||
|
||||
#include "JSON.h"
|
||||
|
||||
class Diagnostics;
|
||||
|
||||
namespace dyld3 {
|
||||
namespace json {
|
||||
|
||||
Node readJSON(Diagnostics& diags, const char* filePath);
|
||||
|
||||
// Given a map node, returns the node representing the given value.
|
||||
// If it is missing, returns a sentinel node and sets an error on the diagnostic
|
||||
const Node& getRequiredValue(Diagnostics& diags, const Node& node, const char* key);
|
||||
|
||||
// Given a map node, returns the node representing the given value.
|
||||
// If it is missing, return nullptr.
|
||||
const Node* getOptionalValue(Diagnostics& diags, const Node& node, const char* key);
|
||||
|
||||
// Parses an int from the given node, or throws an error if its not an integer payload
|
||||
uint64_t parseRequiredInt(Diagnostics& diags, const Node& node);
|
||||
|
||||
// Parses a string from the given node, or throws an error if its not a string payload
|
||||
const std::string& parseRequiredString(Diagnostics& diags, const Node& node);
|
||||
|
||||
|
||||
} // namespace json
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif // __JSON_READER_H__
|
197
dyld3/JSONReader.mm
Normal file
197
dyld3/JSONReader.mm
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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@
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "JSONReader.h"
|
||||
#include "Diagnostics.h"
|
||||
|
||||
namespace dyld3 {
|
||||
namespace json {
|
||||
|
||||
static Node gSentinelNode;
|
||||
|
||||
|
||||
const Node& getRequiredValue(Diagnostics& diags, const Node& node, const char* key) {
|
||||
if (diags.hasError())
|
||||
return gSentinelNode;
|
||||
|
||||
if (!node.array.empty()) {
|
||||
diags.error("Cannot get key '%s' from array node\n", key);
|
||||
return gSentinelNode;
|
||||
}
|
||||
if (!node.value.empty()) {
|
||||
diags.error("Cannot get key '%s' from value node\n", key);
|
||||
return gSentinelNode;
|
||||
}
|
||||
|
||||
auto it = node.map.find(key);
|
||||
if (it == node.map.end()) {
|
||||
diags.error("Map node doesn't have element for key '%s'\n", key);
|
||||
return gSentinelNode;
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
const Node* getOptionalValue(Diagnostics& diags, const Node& node, const char* key) {
|
||||
if (diags.hasError())
|
||||
return nullptr;
|
||||
|
||||
if (!node.array.empty()) {
|
||||
diags.error("Cannot get key '%s' from array node\n", key);
|
||||
return nullptr;
|
||||
}
|
||||
if (!node.value.empty()) {
|
||||
diags.error("Cannot get key '%s' from value node\n", key);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = node.map.find(key);
|
||||
if (it == node.map.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
uint64_t parseRequiredInt(Diagnostics& diags, const Node& node) {
|
||||
if (diags.hasError())
|
||||
return 0;
|
||||
|
||||
if (!node.array.empty()) {
|
||||
diags.error("Cannot get integer value from array node\n");
|
||||
return 0;
|
||||
}
|
||||
if (!node.map.empty()) {
|
||||
diags.error("Cannot get integer value from value node\n");
|
||||
return 0;
|
||||
}
|
||||
if (node.value.empty()) {
|
||||
diags.error("Cannot get integer value from empty node\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return atoi(node.value.c_str());
|
||||
}
|
||||
|
||||
const std::string& parseRequiredString(Diagnostics& diags, const Node& node) {
|
||||
static std::string sentinelString = "";
|
||||
|
||||
if (diags.hasError())
|
||||
return sentinelString;
|
||||
|
||||
if (!node.array.empty()) {
|
||||
diags.error("Cannot get string value from array node\n");
|
||||
return sentinelString;
|
||||
}
|
||||
if (!node.map.empty()) {
|
||||
diags.error("Cannot get string value from value node\n");
|
||||
return sentinelString;
|
||||
}
|
||||
if (node.value.empty()) {
|
||||
diags.error("Cannot get string value from empty node\n");
|
||||
return sentinelString;
|
||||
}
|
||||
return node.value;
|
||||
}
|
||||
|
||||
|
||||
Node parseNode(Diagnostics& diags, id jsonObject) {
|
||||
__block Node node;
|
||||
|
||||
// NSDictionary -> map
|
||||
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
|
||||
NSDictionary* dict = (NSDictionary*)jsonObject;
|
||||
|
||||
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
|
||||
if (![key isKindOfClass:[NSString class]]) {
|
||||
diags.error("JSON map key is not of string type\n");
|
||||
*stop = true;
|
||||
return;
|
||||
}
|
||||
Node childNode = parseNode(diags, value);
|
||||
if (diags.hasError()) {
|
||||
*stop = true;
|
||||
return;
|
||||
}
|
||||
node.map[[key UTF8String]] = childNode;
|
||||
}];
|
||||
|
||||
if (diags.hasError())
|
||||
return Node();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// NSArray -> array
|
||||
if ([jsonObject isKindOfClass:[NSArray class]]) {
|
||||
NSArray* array = (NSArray*)jsonObject;
|
||||
|
||||
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
|
||||
Node childNode = parseNode(diags, obj);
|
||||
if (diags.hasError()) {
|
||||
*stop = true;
|
||||
return;
|
||||
}
|
||||
node.array.push_back(childNode);
|
||||
}];
|
||||
|
||||
if (diags.hasError())
|
||||
return Node();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// NSString -> value
|
||||
if ([jsonObject isKindOfClass:[NSString class]]) {
|
||||
node.value = [(NSString*)jsonObject UTF8String];
|
||||
return node;
|
||||
}
|
||||
|
||||
diags.error("Unknown json deserialized type\n");
|
||||
return Node();
|
||||
}
|
||||
|
||||
Node readJSON(Diagnostics& diags, const char* filePath) {
|
||||
NSInputStream* inputStream = [NSInputStream inputStreamWithFileAtPath:[NSString stringWithUTF8String:filePath]];
|
||||
if (!inputStream) {
|
||||
diags.error("Could not option json file: '%s'\n", filePath);
|
||||
return Node();
|
||||
}
|
||||
[inputStream open];
|
||||
|
||||
NSError* error = nil;
|
||||
id jsonObject = [NSJSONSerialization JSONObjectWithStream:inputStream options:NSJSONReadingMutableContainers error:&error];
|
||||
if (!jsonObject) {
|
||||
diags.error("Could not deserializer json file: '%s' because '%s'\n", filePath, [[error localizedFailureReason] UTF8String]);
|
||||
[inputStream close];
|
||||
return Node();
|
||||
}
|
||||
|
||||
Node node = parseNode(diags, jsonObject);
|
||||
[inputStream close];
|
||||
return node;
|
||||
}
|
||||
|
||||
} //namespace json
|
||||
} //namespace dyld3
|
116
dyld3/JSONWriter.h
Normal file
116
dyld3/JSONWriter.h
Normal file
@ -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 __JSON_WRITER_H__
|
||||
#define __JSON_WRITER_H__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "JSON.h"
|
||||
|
||||
namespace dyld3 {
|
||||
namespace json {
|
||||
|
||||
static inline std::string hex(uint64_t value) {
|
||||
char buff[64];
|
||||
sprintf(buff, "0x%llX", value);
|
||||
return buff;
|
||||
}
|
||||
|
||||
static inline std::string hex4(uint64_t value) {
|
||||
char buff[64];
|
||||
sprintf(buff, "0x%04llX", value);
|
||||
return buff;
|
||||
}
|
||||
|
||||
static inline std::string hex8(uint64_t value) {
|
||||
char buff[64];
|
||||
sprintf(buff, "0x%08llX", value);
|
||||
return buff;
|
||||
}
|
||||
|
||||
static inline std::string decimal(uint64_t value) {
|
||||
char buff[64];
|
||||
sprintf(buff, "%llu", value);
|
||||
return buff;
|
||||
}
|
||||
|
||||
static inline void indentBy(uint32_t spaces, std::ostream& out) {
|
||||
for (uint32_t i=0; i < spaces; ++i) {
|
||||
out << " ";
|
||||
}
|
||||
}
|
||||
|
||||
static inline void printJSON(const Node& node, uint32_t indent, std::ostream& out)
|
||||
{
|
||||
if ( !node.map.empty() ) {
|
||||
out << "{";
|
||||
bool needComma = false;
|
||||
for (const auto& entry : node.map) {
|
||||
if ( needComma )
|
||||
out << ",";
|
||||
out << "\n";
|
||||
indentBy(indent+2, out);
|
||||
out << "\"" << entry.first << "\": ";
|
||||
printJSON(entry.second, indent+2, out);
|
||||
needComma = true;
|
||||
}
|
||||
out << "\n";
|
||||
indentBy(indent, out);
|
||||
out << "}";
|
||||
}
|
||||
else if ( !node.array.empty() ) {
|
||||
out << "[";
|
||||
bool needComma = false;
|
||||
for (const auto& entry : node.array) {
|
||||
if ( needComma )
|
||||
out << ",";
|
||||
out << "\n";
|
||||
indentBy(indent+2, out);
|
||||
printJSON(entry, indent+2, out);
|
||||
needComma = true;
|
||||
}
|
||||
out << "\n";
|
||||
indentBy(indent, out);
|
||||
out << "]";
|
||||
}
|
||||
else {
|
||||
std::string escapedString;
|
||||
escapedString.reserve(node.value.size());
|
||||
for (char c : node.value) {
|
||||
if (c == '"')
|
||||
escapedString += '\\';
|
||||
escapedString += c;
|
||||
}
|
||||
out << "\"" << escapedString << "\"";
|
||||
}
|
||||
if ( indent == 0 )
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
|
||||
} // namespace json
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif // __JSON_WRITER_H__
|
927
dyld3/Loading.cpp
Normal file
927
dyld3/Loading.cpp
Normal file
@ -0,0 +1,927 @@
|
||||
/*
|
||||
* 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 <bitset>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <mach/mach.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/dtrace.h>
|
||||
#include <sys/errno.h>
|
||||
#include <unistd.h>
|
||||
#include <System/sys/mman.h>
|
||||
#include <System/sys/csr.h>
|
||||
#include <System/machine/cpu_capabilities.h>
|
||||
#if !TARGET_OS_SIMULATOR && !TARGET_OS_DRIVERKIT
|
||||
#include <sandbox.h>
|
||||
#include <sandbox/private.h>
|
||||
#endif
|
||||
//#include <dispatch/dispatch.h>
|
||||
#include <mach/vm_page_size.h>
|
||||
|
||||
#include "MachOFile.h"
|
||||
#include "MachOLoaded.h"
|
||||
#include "MachOAnalyzer.h"
|
||||
#include "Logging.h"
|
||||
#include "Loading.h"
|
||||
#include "Tracing.h"
|
||||
#include "dyld2.h"
|
||||
#include "dyld_cache_format.h"
|
||||
|
||||
#include "objc-shared-cache.h"
|
||||
|
||||
namespace dyld {
|
||||
void log(const char* m, ...);
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// utility to track a set of ImageNum's in use
|
||||
class VIS_HIDDEN ImageNumSet
|
||||
{
|
||||
public:
|
||||
void add(dyld3::closure::ImageNum num);
|
||||
bool contains(dyld3::closure::ImageNum num) const;
|
||||
|
||||
private:
|
||||
std::bitset<5120> _bitmap;
|
||||
dyld3::OverflowSafeArray<dyld3::closure::ImageNum> _overflowArray;
|
||||
};
|
||||
|
||||
void ImageNumSet::add(dyld3::closure::ImageNum num)
|
||||
{
|
||||
if ( num < 5120 )
|
||||
_bitmap.set(num);
|
||||
else
|
||||
_overflowArray.push_back(num);
|
||||
}
|
||||
|
||||
bool ImageNumSet::contains(dyld3::closure::ImageNum num) const
|
||||
{
|
||||
if ( num < 5120 )
|
||||
return _bitmap.test(num);
|
||||
|
||||
for (dyld3::closure::ImageNum existingNum : _overflowArray) {
|
||||
if ( existingNum == num )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace anonymous
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
Loader::Loader(const Array<LoadedImage>& existingImages, Array<LoadedImage>& newImagesStorage,
|
||||
const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
|
||||
const closure::ObjCSelectorOpt* selOpt, const Array<closure::Image::ObjCSelectorImage>& selImages,
|
||||
LogFunc logLoads, LogFunc logSegments, LogFunc logFixups, LogFunc logDofs)
|
||||
: _existingImages(existingImages), _newImages(newImagesStorage),
|
||||
_imagesArrays(imagesArrays), _dyldCacheAddress(cacheAddress), _dyldCacheSelectorOpt(nullptr),
|
||||
_closureSelectorOpt(selOpt), _closureSelectorImages(selImages),
|
||||
_logLoads(logLoads), _logSegments(logSegments), _logFixups(logFixups), _logDofs(logDofs)
|
||||
{
|
||||
#if BUILDING_DYLD
|
||||
// This is only needed for dyld and the launch closure, not the dlopen closures
|
||||
if ( _dyldCacheAddress != nullptr ) {
|
||||
_dyldCacheSelectorOpt = ((const DyldSharedCache*)_dyldCacheAddress)->objcOpt()->selopt();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Loader::addImage(const LoadedImage& li)
|
||||
{
|
||||
_newImages.push_back(li);
|
||||
}
|
||||
|
||||
LoadedImage* Loader::findImage(closure::ImageNum targetImageNum)
|
||||
{
|
||||
#if BUILDING_DYLD
|
||||
// The launch images are different in dyld vs libdyld. In dyld, the new images are
|
||||
// the launch images, while in libdyld, the existing images are the launch images
|
||||
if (LoadedImage* info = _launchImagesCache.findImage(targetImageNum, _newImages)) {
|
||||
return info;
|
||||
}
|
||||
|
||||
for (uint64_t index = 0; index != _newImages.count(); ++index) {
|
||||
LoadedImage& info = _newImages[index];
|
||||
if ( info.image()->representsImageNum(targetImageNum) ) {
|
||||
// Try cache this entry for next time
|
||||
_launchImagesCache.tryAddImage(targetImageNum, index);
|
||||
return &info;
|
||||
}
|
||||
}
|
||||
#elif BUILDING_LIBDYLD
|
||||
for (const LoadedImage& info : _existingImages) {
|
||||
if ( info.image()->representsImageNum(targetImageNum) )
|
||||
return (LoadedImage*)&info;
|
||||
}
|
||||
for (LoadedImage& info : _newImages) {
|
||||
if ( info.image()->representsImageNum(targetImageNum) )
|
||||
return &info;
|
||||
}
|
||||
#else
|
||||
#error Must be building dyld or libdyld
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uintptr_t Loader::resolveTarget(closure::Image::ResolvedSymbolTarget target)
|
||||
{
|
||||
const LoadedImage* info;
|
||||
switch ( target.sharedCache.kind ) {
|
||||
case closure::Image::ResolvedSymbolTarget::kindSharedCache:
|
||||
assert(_dyldCacheAddress != nullptr);
|
||||
return (uintptr_t)_dyldCacheAddress + (uintptr_t)target.sharedCache.offset;
|
||||
|
||||
case closure::Image::ResolvedSymbolTarget::kindImage:
|
||||
info = findImage(target.image.imageNum);
|
||||
assert(info != nullptr);
|
||||
return (uintptr_t)(info->loadedAddress()) + (uintptr_t)target.image.offset;
|
||||
|
||||
case closure::Image::ResolvedSymbolTarget::kindAbsolute:
|
||||
if ( target.absolute.value & (1ULL << 62) )
|
||||
return (uintptr_t)(target.absolute.value | 0xC000000000000000ULL);
|
||||
else
|
||||
return (uintptr_t)target.absolute.value;
|
||||
}
|
||||
assert(0 && "malformed ResolvedSymbolTarget");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void Loader::completeAllDependents(Diagnostics& diag, bool& someCacheImageOverridden)
|
||||
{
|
||||
// accumulate all image overrides
|
||||
STACK_ALLOC_ARRAY(ImageOverride, overrides, _existingImages.maxCount() + _newImages.maxCount());
|
||||
for (const auto anArray : _imagesArrays) {
|
||||
// ignore prebuilt Image* in dyld cache
|
||||
if ( anArray->startImageNum() < dyld3::closure::kFirstLaunchClosureImageNum )
|
||||
continue;
|
||||
anArray->forEachImage(^(const dyld3::closure::Image* image, bool& stop) {
|
||||
ImageOverride overrideEntry;
|
||||
if ( image->isOverrideOfDyldCacheImage(overrideEntry.inCache) ) {
|
||||
someCacheImageOverridden = true;
|
||||
overrideEntry.replacement = image->imageNum();
|
||||
overrides.push_back(overrideEntry);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// make cache for fast lookup of already loaded images
|
||||
__block ImageNumSet alreadyLoaded;
|
||||
for (const LoadedImage& info : _existingImages) {
|
||||
alreadyLoaded.add(info.image()->imageNum());
|
||||
}
|
||||
alreadyLoaded.add(_newImages.begin()->image()->imageNum());
|
||||
|
||||
// for each image in _newImages, starting at the top image, make sure its dependents are in _allImages
|
||||
uintptr_t index = 0;
|
||||
while ( (index < _newImages.count()) && diag.noError() ) {
|
||||
const closure::Image* image = _newImages[index].image();
|
||||
//fprintf(stderr, "completeAllDependents(): looking at dependents of %s\n", image->path());
|
||||
image->forEachDependentImage(^(uint32_t depIndex, closure::Image::LinkKind kind, closure::ImageNum depImageNum, bool& stop) {
|
||||
// check if imageNum needs to be changed to an override
|
||||
for (const ImageOverride& entry : overrides) {
|
||||
if ( entry.inCache == depImageNum ) {
|
||||
depImageNum = entry.replacement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// check if this dependent is already loaded
|
||||
if ( !alreadyLoaded.contains(depImageNum) ) {
|
||||
// if not, look in imagesArrays
|
||||
const closure::Image* depImage = closure::ImageArray::findImage(_imagesArrays, depImageNum);
|
||||
if ( depImage != nullptr ) {
|
||||
//dyld::log(" load imageNum=0x%05X, image path=%s\n", depImageNum, depImage->path());
|
||||
if ( _newImages.freeCount() == 0 ) {
|
||||
diag.error("too many initial images");
|
||||
stop = true;
|
||||
}
|
||||
else {
|
||||
_newImages.push_back(LoadedImage::make(depImage));
|
||||
}
|
||||
alreadyLoaded.add(depImageNum);
|
||||
}
|
||||
else {
|
||||
diag.error("unable to locate imageNum=0x%04X, depIndex=%d of %s", depImageNum, depIndex, image->path());
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI)
|
||||
{
|
||||
// scan array and map images not already loaded
|
||||
for (LoadedImage& info : _newImages) {
|
||||
if ( info.loadedAddress() != nullptr ) {
|
||||
// log main executable's segments
|
||||
if ( (info.loadedAddress()->filetype == MH_EXECUTE) && (info.state() == LoadedImage::State::mapped) ) {
|
||||
if ( _logSegments("dyld: mapped by kernel %s\n", info.image()->path()) ) {
|
||||
info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& stop) {
|
||||
uint64_t start = (long)info.loadedAddress() + vmOffset;
|
||||
uint64_t end = start+vmSize-1;
|
||||
if ( (segIndex == 0) && (permissions == 0) ) {
|
||||
start = 0;
|
||||
}
|
||||
_logSegments("%14s (%c%c%c) 0x%012llX->0x%012llX \n", info.loadedAddress()->segmentName(segIndex),
|
||||
(permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
|
||||
start, end);
|
||||
});
|
||||
}
|
||||
}
|
||||
// skip over ones already loaded
|
||||
continue;
|
||||
}
|
||||
if ( info.image()->inDyldCache() ) {
|
||||
if ( info.image()->overridableDylib() ) {
|
||||
struct stat statBuf;
|
||||
if ( stat(info.image()->path(), &statBuf) == 0 ) {
|
||||
// verify file has not changed since closure was built
|
||||
uint64_t inode;
|
||||
uint64_t mtime;
|
||||
if ( info.image()->hasFileModTimeAndInode(inode, mtime) ) {
|
||||
if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
|
||||
diag.error("dylib file mtime/inode changed since closure was built for '%s'", info.image()->path());
|
||||
}
|
||||
}
|
||||
else {
|
||||
diag.error("dylib file not expected on disk, must be a root '%s'", info.image()->path());
|
||||
}
|
||||
}
|
||||
else if ( (_dyldCacheAddress != nullptr) && ((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk ) {
|
||||
diag.error("dylib file missing, was in dyld shared cache '%s'", info.image()->path());
|
||||
}
|
||||
}
|
||||
if ( diag.noError() ) {
|
||||
info.setLoadedAddress((MachOLoaded*)((uintptr_t)_dyldCacheAddress + info.image()->cacheOffset()));
|
||||
info.setState(LoadedImage::State::fixedUp);
|
||||
if ( _logSegments("dyld: Using from dyld cache %s\n", info.image()->path()) ) {
|
||||
info.image()->forEachCacheSegment(^(uint32_t segIndex, uint64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
|
||||
_logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", info.loadedAddress()->segmentName(segIndex),
|
||||
(permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
|
||||
(long)info.loadedAddress()+(long)vmOffset, (long)info.loadedAddress()+(long)vmOffset+(long)vmSize-1);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
mapImage(diag, info, fromOFI);
|
||||
if ( diag.hasError() )
|
||||
break; // out of for loop
|
||||
}
|
||||
|
||||
}
|
||||
if ( diag.hasError() ) {
|
||||
// bummer, need to clean up by unmapping any images just mapped
|
||||
for (LoadedImage& info : _newImages) {
|
||||
if ( (info.state() == LoadedImage::State::mapped) && !info.image()->inDyldCache() && !info.leaveMapped() ) {
|
||||
_logSegments("dyld: unmapping %s\n", info.image()->path());
|
||||
unmapImage(info);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// apply fixups
|
||||
for (LoadedImage& info : _newImages) {
|
||||
// images in shared cache do not need fixups applied
|
||||
if ( info.image()->inDyldCache() )
|
||||
continue;
|
||||
// previously loaded images were previously fixed up
|
||||
if ( info.state() < LoadedImage::State::fixedUp ) {
|
||||
applyFixupsToImage(diag, info);
|
||||
if ( diag.hasError() )
|
||||
break;
|
||||
info.setState(LoadedImage::State::fixedUp);
|
||||
}
|
||||
}
|
||||
|
||||
// find and register dtrace DOFs
|
||||
if ( processDOFs ) {
|
||||
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(DOFInfo, dofImages, _newImages.count());
|
||||
for (LoadedImage& info : _newImages) {
|
||||
info.image()->forEachDOF(info.loadedAddress(), ^(const void* section) {
|
||||
DOFInfo dofInfo;
|
||||
dofInfo.dof = section;
|
||||
dofInfo.imageHeader = info.loadedAddress();
|
||||
dofInfo.imageShortName = info.image()->leafName();
|
||||
dofImages.push_back(dofInfo);
|
||||
});
|
||||
}
|
||||
registerDOFs(dofImages);
|
||||
}
|
||||
}
|
||||
|
||||
bool Loader::sandboxBlocked(const char* path, const char* kind)
|
||||
{
|
||||
#if TARGET_OS_SIMULATOR || TARGET_OS_DRIVERKIT
|
||||
// sandbox calls not yet supported in dyld_sim
|
||||
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 Loader::sandboxBlockedMmap(const char* path)
|
||||
{
|
||||
return sandboxBlocked(path, "file-map-executable");
|
||||
}
|
||||
|
||||
bool Loader::sandboxBlockedOpen(const char* path)
|
||||
{
|
||||
return sandboxBlocked(path, "file-read-data");
|
||||
}
|
||||
|
||||
bool Loader::sandboxBlockedStat(const char* path)
|
||||
{
|
||||
return sandboxBlocked(path, "file-read-metadata");
|
||||
}
|
||||
|
||||
void Loader::mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI)
|
||||
{
|
||||
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_MAP_IMAGE, info.image()->path(), 0, 0);
|
||||
|
||||
const closure::Image* image = info.image();
|
||||
uint64_t sliceOffset = image->sliceOffsetInFile();
|
||||
const uint64_t totalVMSize = image->vmSizeToMap();
|
||||
uint32_t codeSignFileOffset;
|
||||
uint32_t codeSignFileSize;
|
||||
bool isCodeSigned = image->hasCodeSignature(codeSignFileOffset, codeSignFileSize);
|
||||
|
||||
// open file
|
||||
#if BUILDING_DYLD
|
||||
int fd = dyld::my_open(info.image()->path(), O_RDONLY, 0);
|
||||
#else
|
||||
int fd = ::open(info.image()->path(), O_RDONLY, 0);
|
||||
#endif
|
||||
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;
|
||||
}
|
||||
|
||||
// get file info
|
||||
struct stat statBuf;
|
||||
#if TARGET_OS_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;
|
||||
}
|
||||
|
||||
// verify file has not changed since closure was built
|
||||
uint64_t inode;
|
||||
uint64_t mtime;
|
||||
if ( image->hasFileModTimeAndInode(inode, mtime) ) {
|
||||
if ( (statBuf.st_mtime != mtime) || (statBuf.st_ino != inode) ) {
|
||||
diag.error("file mtime/inode changed since closure was built for '%s'", image->path());
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// handle case on iOS where sliceOffset in closure is wrong because file was thinned after cache was built
|
||||
if ( (_dyldCacheAddress != nullptr) && !(((dyld_cache_header*)_dyldCacheAddress)->dylibsExpectedOnDisk) ) {
|
||||
if ( sliceOffset != 0 ) {
|
||||
if ( round_page_kernel(codeSignFileOffset+codeSignFileSize) == round_page_kernel(statBuf.st_size) ) {
|
||||
// file is now thin
|
||||
sliceOffset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// register code signature
|
||||
uint64_t coveredCodeLength = UINT64_MAX;
|
||||
if ( isCodeSigned ) {
|
||||
auto sigTimer = ScopedTimer(DBG_DYLD_TIMING_ATTACH_CODESIGNATURE, 0, 0, 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;
|
||||
}
|
||||
coveredCodeLength = siginfo.fs_file_start;
|
||||
if ( coveredCodeLength < codeSignFileOffset ) {
|
||||
diag.error("code signature does not cover entire file up to signature");
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// <rdar://problem/41015217> dyld should use F_CHECK_LV even on unsigned binaries
|
||||
{
|
||||
// <rdar://problem/32684903> 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if ( sliceOffset != 0 )
|
||||
_logSegments("dyld: Mapping %s (slice offset=%llu)\n", image->path(), sliceOffset);
|
||||
else
|
||||
_logSegments("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;
|
||||
__block uint64_t maxFileOffset = 0;
|
||||
image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& stop) {
|
||||
// <rdar://problem/32363581> 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;
|
||||
const MachOFile* mf = (MachOFile*)segAddress;
|
||||
if ( !mf->isMachO(diag, fileSize) ) {
|
||||
mmapFailure = true;
|
||||
stop = true;
|
||||
}
|
||||
}
|
||||
if ( !mmapFailure ) {
|
||||
const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
|
||||
_logSegments("%14s (%c%c%c) 0x%012lX->0x%012lX \n", lmo->segmentName(segIndex),
|
||||
(permissions & PROT_READ) ? 'r' : '.', (permissions & PROT_WRITE) ? 'w' : '.', (permissions & PROT_EXEC) ? 'x' : '.' ,
|
||||
(long)segAddress, (long)segAddress+(long)vmSize-1);
|
||||
}
|
||||
maxFileOffset = fileOffset + fileSize;
|
||||
});
|
||||
if ( mmapFailure ) {
|
||||
::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
|
||||
::close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
// <rdar://problem/47163421> speculatively read whole slice
|
||||
fspecread_t specread = {} ;
|
||||
specread.fsr_offset = sliceOffset;
|
||||
specread.fsr_length = maxFileOffset;
|
||||
specread.fsr_flags = 0;
|
||||
fcntl(fd, F_SPECULATIVE_READ, &specread);
|
||||
_logSegments("dyld: Speculatively read offset=0x%08llX, len=0x%08llX, path=%s\n", sliceOffset, maxFileOffset, image->path());
|
||||
|
||||
// close file
|
||||
close(fd);
|
||||
|
||||
#if BUILDING_LIBDYLD
|
||||
// verify file has not changed since closure was built by checking code signature has not changed
|
||||
struct CDHashWrapper {
|
||||
uint8_t cdHash[20];
|
||||
};
|
||||
|
||||
// Get all the hashes for the image
|
||||
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(CDHashWrapper, expectedCDHashes, 1);
|
||||
image->forEachCDHash(^(const uint8_t *cdHash, bool &stop) {
|
||||
CDHashWrapper cdHashWrapper;
|
||||
memcpy(cdHashWrapper.cdHash, cdHash, sizeof(CDHashWrapper::cdHash));
|
||||
expectedCDHashes.push_back(cdHashWrapper);
|
||||
});
|
||||
|
||||
if (!expectedCDHashes.empty()) {
|
||||
if (expectedCDHashes.count() != 1) {
|
||||
// We should only see a single hash for dylibs
|
||||
diag.error("code signature count invalid");
|
||||
} else if ( codeSignatureStartAddress == nullptr ) {
|
||||
diag.error("code signature missing");
|
||||
}
|
||||
else if ( codeSignatureStartAddress+codeSignFileSize > linkeditEndAddress ) {
|
||||
diag.error("code signature extends beyond end of __LINKEDIT");
|
||||
}
|
||||
else {
|
||||
// Get all the cd hashes for the macho
|
||||
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(CDHashWrapper, foundCDHashes, 1);
|
||||
const MachOLoaded* lmo = (MachOLoaded*)loadAddress;
|
||||
lmo->forEachCDHashOfCodeSignature(codeSignatureStartAddress, codeSignFileSize,
|
||||
^(const uint8_t *cdHash) {
|
||||
CDHashWrapper cdHashWrapper;
|
||||
memcpy(cdHashWrapper.cdHash, cdHash, sizeof(CDHashWrapper::cdHash));
|
||||
foundCDHashes.push_back(cdHashWrapper);
|
||||
});
|
||||
|
||||
if (foundCDHashes.empty()) {
|
||||
diag.error("code signature format invalid");
|
||||
} else if (expectedCDHashes.count() != foundCDHashes.count()) {
|
||||
diag.error("code signature count invalid");
|
||||
} else {
|
||||
// We found a hash, so make sure its equal.
|
||||
if ( ::memcmp(foundCDHashes[0].cdHash, expectedCDHashes[0].cdHash, 20) != 0 )
|
||||
diag.error("code signature changed since closure was built");
|
||||
}
|
||||
}
|
||||
if ( diag.hasError() ) {
|
||||
::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED && !TARGET_OS_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);
|
||||
if ( result != 0 ) {
|
||||
diag.error("could not register fairplay decryption, mremap_encrypted() => %d", result);
|
||||
::vm_deallocate(mach_task_self(), loadAddress, (vm_size_t)totalVMSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_logLoads("dyld: load %s\n", image->path());
|
||||
|
||||
timer.setData4((uint64_t)loadAddress);
|
||||
info.setLoadedAddress((MachOLoaded*)loadAddress);
|
||||
info.setState(LoadedImage::State::mapped);
|
||||
}
|
||||
|
||||
void Loader::unmapImage(LoadedImage& info)
|
||||
{
|
||||
assert(info.loadedAddress() != nullptr);
|
||||
::vm_deallocate(mach_task_self(), (vm_address_t)info.loadedAddress(), (vm_size_t)(info.image()->vmSizeToMap()));
|
||||
info.setLoadedAddress(nullptr);
|
||||
}
|
||||
|
||||
void Loader::registerDOFs(const Array<DOFInfo>& dofs)
|
||||
{
|
||||
if ( dofs.empty() )
|
||||
return;
|
||||
|
||||
int fd = open("/dev/" DTRACEMNR_HELPER, O_RDWR);
|
||||
if ( fd < 0 ) {
|
||||
_logDofs("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) + dofs.count()*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 = dofs.count();
|
||||
for (unsigned int i=0; i < dofs.count(); ++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 < dofs.count(); ++i) {
|
||||
_logDofs("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 {
|
||||
_logDofs("dyld: ioctl to register dtrace DOF section failed\n");
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
bool Loader::dtraceUserProbesEnabled()
|
||||
{
|
||||
#ifdef DARLING
|
||||
return false;
|
||||
#else
|
||||
uint8_t dofEnabled = *((uint8_t*)_COMM_PAGE_DTRACE_DOF_ENABLED);
|
||||
return ( (dofEnabled & 1) );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Loader::vmAccountingSetSuspended(bool suspend, LogFunc logger)
|
||||
{
|
||||
#if __arm__ || __arm64__
|
||||
// <rdar://problem/29099600> dyld should tell the kernel when it is doing fix-ups caused by roots
|
||||
logger("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 Loader::applyFixupsToImage(Diagnostics& diag, LoadedImage& info)
|
||||
{
|
||||
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_APPLY_FIXUPS, (uint64_t)info.loadedAddress(), 0, 0);
|
||||
closure::ImageNum cacheImageNum;
|
||||
const char* leafName = info.image()->leafName();
|
||||
const closure::Image* image = info.image();
|
||||
const uint8_t* imageLoadAddress = (uint8_t*)info.loadedAddress();
|
||||
uintptr_t slide = info.loadedAddress()->getSlide();
|
||||
bool overrideOfCache = info.image()->isOverrideOfDyldCacheImage(cacheImageNum);
|
||||
|
||||
if ( overrideOfCache )
|
||||
vmAccountingSetSuspended(true, _logFixups);
|
||||
image->forEachFixup(^(uint64_t imageOffsetToRebase, bool& stop) {
|
||||
// this is a rebase, add slide
|
||||
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
|
||||
*fixUpLoc += slide;
|
||||
_logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
|
||||
},
|
||||
^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
|
||||
// this is a bind, set to target
|
||||
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
|
||||
uintptr_t value = resolveTarget(bindTarget);
|
||||
_logFixups("dyld: fixup: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
|
||||
*fixUpLoc = value;
|
||||
},
|
||||
^(uint64_t imageOffsetToStartsInfo, const Array<closure::Image::ResolvedSymbolTarget>& targets, bool& stop) {
|
||||
// this is a chain of fixups, fix up all
|
||||
STACK_ALLOC_OVERFLOW_SAFE_ARRAY(const void*, targetAddrs, 128);
|
||||
targetAddrs.reserve(targets.count());
|
||||
for (uint32_t i=0; i < targets.count(); ++i)
|
||||
targetAddrs.push_back((void*)resolveTarget(targets[i]));
|
||||
((dyld3::MachOAnalyzer*)(info.loadedAddress()))->withChainStarts(diag, imageOffsetToStartsInfo, ^(const dyld_chained_starts_in_image* starts) {
|
||||
info.loadedAddress()->fixupAllChainedFixups(diag, starts, slide, targetAddrs, ^(void* loc, void* newValue) {
|
||||
_logFixups("dyld: fixup: %s:%p = %p\n", leafName, loc, newValue);
|
||||
});
|
||||
});
|
||||
},
|
||||
^(uint64_t imageOffsetToFixup) {
|
||||
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
|
||||
_logFixups("dyld: fixup objc image info: %s Setting objc image info for precomputed objc\n", leafName);
|
||||
|
||||
MachOAnalyzer::ObjCImageInfo *imageInfo = (MachOAnalyzer::ObjCImageInfo *)fixUpLoc;
|
||||
((MachOAnalyzer::ObjCImageInfo *)imageInfo)->flags |= MachOAnalyzer::ObjCImageInfo::dyldPreoptimized;
|
||||
},
|
||||
^(uint64_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
|
||||
// this is a bind, set to target
|
||||
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToBind);
|
||||
uintptr_t value = resolveTarget(bindTarget);
|
||||
_logFixups("dyld: fixup objc protocol: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
|
||||
*fixUpLoc = value;
|
||||
},
|
||||
^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) {
|
||||
// fixupObjCSelRefs
|
||||
closure::Image::ResolvedSymbolTarget fixupTarget;
|
||||
if ( inSharedCache ) {
|
||||
const char* selectorString = _dyldCacheSelectorOpt->getEntryForIndex(selectorIndex);
|
||||
fixupTarget.sharedCache.kind = closure::Image::ResolvedSymbolTarget::kindSharedCache;
|
||||
fixupTarget.sharedCache.offset = (uint64_t)selectorString - (uint64_t)_dyldCacheAddress;
|
||||
} else {
|
||||
closure::ImageNum imageNum;
|
||||
uint64_t vmOffset;
|
||||
bool gotLocation = _closureSelectorOpt->getStringLocation(selectorIndex, _closureSelectorImages, imageNum, vmOffset);
|
||||
assert(gotLocation);
|
||||
fixupTarget.image.kind = closure::Image::ResolvedSymbolTarget::kindImage;
|
||||
fixupTarget.image.imageNum = imageNum;
|
||||
fixupTarget.image.offset = vmOffset;
|
||||
}
|
||||
|
||||
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
|
||||
uintptr_t value = resolveTarget(fixupTarget);
|
||||
_logFixups("dyld: fixup objc selector: %s:%p(was '%s') = %p(now '%s')\n", leafName, fixUpLoc, (const char*)*fixUpLoc, (void*)value, (const char*)value);
|
||||
*fixUpLoc = value;
|
||||
}, ^(uint64_t imageOffsetToFixup, bool &stop) {
|
||||
// fixupObjCStableSwift
|
||||
// Class really is stable Swift, pretending to be pre-stable.
|
||||
// Fix its lie.
|
||||
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToFixup);
|
||||
uintptr_t value = ((*fixUpLoc) | MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_STABLE) & ~MachOAnalyzer::ObjCClassInfo::FAST_IS_SWIFT_LEGACY;
|
||||
_logFixups("dyld: fixup objc stable Swift: %s:%p = %p\n", leafName, fixUpLoc, (void*)value);
|
||||
*fixUpLoc = value;
|
||||
}, ^(uint64_t imageOffsetToFixup, bool &stop) {
|
||||
// TODO: Implement this
|
||||
});
|
||||
|
||||
#if __i386__
|
||||
__block bool segmentsMadeWritable = false;
|
||||
image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool& stop) {
|
||||
if ( !segmentsMadeWritable )
|
||||
setSegmentProtects(info, true);
|
||||
uintptr_t* fixUpLoc = (uintptr_t*)(imageLoadAddress + imageOffsetToRebase);
|
||||
*fixUpLoc += slide;
|
||||
_logFixups("dyld: fixup: %s:%p += %p\n", leafName, fixUpLoc, (void*)slide);
|
||||
},
|
||||
^(uint32_t imageOffsetToBind, closure::Image::ResolvedSymbolTarget bindTarget, bool& stop) {
|
||||
// FIXME
|
||||
});
|
||||
if ( segmentsMadeWritable )
|
||||
setSegmentProtects(info, false);
|
||||
#endif
|
||||
|
||||
// make any read-only data segments read-only
|
||||
if ( image->hasReadOnlyData() && !image->inDyldCache() ) {
|
||||
image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& segStop) {
|
||||
if ( laterReadOnly ) {
|
||||
::mprotect((void*)(imageLoadAddress+vmOffset), (size_t)vmSize, VM_PROT_READ);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ( overrideOfCache )
|
||||
vmAccountingSetSuspended(false, _logFixups);
|
||||
}
|
||||
|
||||
#if __i386__
|
||||
void Loader::setSegmentProtects(const LoadedImage& info, bool write)
|
||||
{
|
||||
info.image()->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t protections, bool laterReadOnly, bool& segStop) {
|
||||
if ( protections & VM_PROT_WRITE )
|
||||
return;
|
||||
uint32_t regionProt = protections;
|
||||
if ( write )
|
||||
regionProt = VM_PROT_WRITE | VM_PROT_READ;
|
||||
kern_return_t r = vm_protect(mach_task_self(), ((uintptr_t)info.loadedAddress())+(uintptr_t)vmOffset, (uintptr_t)vmSize, false, regionProt);
|
||||
assert( r == KERN_SUCCESS );
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BUILDING_DYLD
|
||||
LoadedImage* Loader::LaunchImagesCache::findImage(closure::ImageNum imageNum,
|
||||
Array<LoadedImage>& images) const {
|
||||
if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) )
|
||||
return nullptr;
|
||||
|
||||
uint64_t cacheIndex = imageNum - _firstImageNum;
|
||||
uint32_t imagesIndex = _imageIndices[cacheIndex];
|
||||
if ( imagesIndex == 0 )
|
||||
return nullptr;
|
||||
|
||||
// Note the index is offset by 1 so that 0's are not yet set
|
||||
return &images[imagesIndex - 1];
|
||||
}
|
||||
|
||||
void Loader::LaunchImagesCache::tryAddImage(closure::ImageNum imageNum,
|
||||
uint64_t allImagesIndex) {
|
||||
if ( (imageNum < _firstImageNum) || (imageNum >= _lastImageNum) )
|
||||
return;
|
||||
|
||||
uint64_t cacheIndex = imageNum - _firstImageNum;
|
||||
|
||||
// Note the index is offset by 1 so that 0's are not yet set
|
||||
_imageIndices[cacheIndex] = (uint32_t)allImagesIndex + 1;
|
||||
}
|
||||
|
||||
void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop))
|
||||
{
|
||||
bool stop = false;
|
||||
const char* const eof = &buffer[bufferLen];
|
||||
for (const char* s = buffer; 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 ) {
|
||||
forEachLineInFile(lines, (size_t)statBuf.st_size, lineHandler);
|
||||
munmap((void*)lines, (size_t)statBuf.st_size);
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if (BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
bool internalInstall()
|
||||
{
|
||||
#if TARGET_OS_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
|
||||
}
|
||||
#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
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
|
||||
|
||||
|
181
dyld3/Loading.h
Normal file
181
dyld3/Loading.h
Normal file
@ -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@
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifndef __DYLD_LOADING_H__
|
||||
#define __DYLD_LOADING_H__
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <mach/mach.h>
|
||||
#include <_simple.h>
|
||||
|
||||
#include "Closure.h"
|
||||
#include "MachOLoaded.h"
|
||||
|
||||
namespace objc_opt {
|
||||
struct objc_clsopt_t;
|
||||
struct objc_selopt_t;
|
||||
}
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
|
||||
//
|
||||
// Tuple of info about a loaded image. Contains the loaded address, Image*, and state.
|
||||
//
|
||||
class VIS_HIDDEN LoadedImage {
|
||||
public:
|
||||
enum class State { unmapped=0, mapped=1, fixedUp=2, beingInited=3, inited=4 };
|
||||
|
||||
static LoadedImage make(const closure::Image* img) { LoadedImage result; result._image = img; return result; }
|
||||
static LoadedImage make(const closure::Image* img, const MachOLoaded* mh)
|
||||
{ LoadedImage result; result._image = img; result.setLoadedAddress(mh); return result; }
|
||||
|
||||
const closure::Image* image() const { return _image; }
|
||||
const MachOLoaded* loadedAddress() const { return (MachOLoaded*)(_loadAddr & (-4096)); }
|
||||
void setLoadedAddress(const MachOLoaded* a) { _loadAddr |= ((uintptr_t)a & (-4096)); }
|
||||
State state() const { return (State)(asBits().state); }
|
||||
void setState(State s) { asBits().state = (int)s; }
|
||||
bool hideFromFlatSearch() const { return asBits().hide; }
|
||||
void setHideFromFlatSearch(bool h) { asBits().hide = h; }
|
||||
bool leaveMapped() const { return asBits().leaveMapped; }
|
||||
void markLeaveMapped() { asBits().leaveMapped = true; }
|
||||
|
||||
private:
|
||||
// since loaded MachO files are always page aligned, that means at least low 12-bits are always zero
|
||||
// so we don't need to record the low 12 bits, instead those bits hold various flags in the _loadeAddr field
|
||||
struct AddrBits {
|
||||
uintptr_t state : 3,
|
||||
hide : 1,
|
||||
leaveMapped : 1,
|
||||
extra : 7,
|
||||
#if __LP64__
|
||||
addr : 52;
|
||||
#else
|
||||
addr : 20;
|
||||
#endif
|
||||
};
|
||||
AddrBits& asBits() { return *((AddrBits*)&_loadAddr); }
|
||||
const AddrBits& asBits() const { return *((AddrBits*)&_loadAddr); }
|
||||
|
||||
// Note: this must be statically initializable so as to not cause static initializers
|
||||
const closure::Image* _image = nullptr;
|
||||
uintptr_t _loadAddr = 0; // really AddrBits
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Utility class to recursively load dependents
|
||||
//
|
||||
class VIS_HIDDEN Loader {
|
||||
public:
|
||||
typedef bool (*LogFunc)(const char*, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
Loader(const Array<LoadedImage>& existingImages, Array<LoadedImage>& newImagesStorage,
|
||||
const void* cacheAddress, const Array<const dyld3::closure::ImageArray*>& imagesArrays,
|
||||
const closure::ObjCSelectorOpt* selOpt, const Array<closure::Image::ObjCSelectorImage>& selImages,
|
||||
LogFunc log_loads, LogFunc log_segments, LogFunc log_fixups, LogFunc log_dofs);
|
||||
|
||||
void addImage(const LoadedImage&);
|
||||
void completeAllDependents(Diagnostics& diag, bool& someCacheImageOverridden);
|
||||
void mapAndFixupAllImages(Diagnostics& diag, bool processDOFs, bool fromOFI=false);
|
||||
uintptr_t resolveTarget(closure::Image::ResolvedSymbolTarget target);
|
||||
LoadedImage* findImage(closure::ImageNum targetImageNum);
|
||||
|
||||
static void unmapImage(LoadedImage& info);
|
||||
static bool dtraceUserProbesEnabled();
|
||||
static void vmAccountingSetSuspended(bool suspend, LogFunc);
|
||||
|
||||
private:
|
||||
|
||||
struct ImageOverride
|
||||
{
|
||||
closure::ImageNum inCache;
|
||||
closure::ImageNum replacement;
|
||||
};
|
||||
|
||||
struct DOFInfo {
|
||||
const void* dof;
|
||||
const mach_header* imageHeader;
|
||||
const char* imageShortName;
|
||||
};
|
||||
|
||||
#if BUILDING_DYLD
|
||||
struct LaunchImagesCache {
|
||||
LoadedImage* findImage(closure::ImageNum targetImageNum,
|
||||
Array<LoadedImage>& images) const;
|
||||
void tryAddImage(closure::ImageNum targetImageNum, uint64_t allImagesIndex);
|
||||
|
||||
static const uint64_t _cacheSize = 128;
|
||||
static const closure::ImageNum _firstImageNum = closure::kFirstLaunchClosureImageNum;
|
||||
static const closure::ImageNum _lastImageNum = closure::kFirstLaunchClosureImageNum + _cacheSize;
|
||||
|
||||
// Note, the cache stores "indices + 1" into the _allImages array.
|
||||
// 0 means we haven't cached an entry yet
|
||||
uint32_t _cacheStorage[_cacheSize] = { 0 };
|
||||
Array<uint32_t> _imageIndices = { &_cacheStorage[0], _cacheSize, _cacheSize };
|
||||
};
|
||||
#endif
|
||||
|
||||
void mapImage(Diagnostics& diag, LoadedImage& info, bool fromOFI);
|
||||
void applyFixupsToImage(Diagnostics& diag, LoadedImage& info);
|
||||
void registerDOFs(const Array<DOFInfo>& dofs);
|
||||
void setSegmentProtects(const LoadedImage& info, bool write);
|
||||
bool sandboxBlockedMmap(const char* path);
|
||||
bool sandboxBlockedOpen(const char* path);
|
||||
bool sandboxBlockedStat(const char* path);
|
||||
bool sandboxBlocked(const char* path, const char* kind);
|
||||
|
||||
const Array<LoadedImage>& _existingImages;
|
||||
Array<LoadedImage>& _newImages;
|
||||
const Array<const closure::ImageArray*>& _imagesArrays;
|
||||
const void* _dyldCacheAddress;
|
||||
const objc_opt::objc_selopt_t* _dyldCacheSelectorOpt;
|
||||
const closure::ObjCSelectorOpt* _closureSelectorOpt;
|
||||
const Array<closure::Image::ObjCSelectorImage>& _closureSelectorImages;
|
||||
#if BUILDING_DYLD
|
||||
LaunchImagesCache _launchImagesCache;
|
||||
#endif
|
||||
LogFunc _logLoads;
|
||||
LogFunc _logSegments;
|
||||
LogFunc _logFixups;
|
||||
LogFunc _logDofs;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#if (BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
bool internalInstall();
|
||||
#endif
|
||||
#if BUILDING_DYLD
|
||||
void forEachLineInFile(const char* path, void (^lineHandler)(const char* line, bool& stop));
|
||||
void forEachLineInFile(const char* buffer, size_t bufferLen, void (^lineHandler)(const char* line, bool& stop));
|
||||
#endif
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif // __DYLD_LOADING_H__
|
||||
|
||||
|
188
dyld3/Logging.cpp
Normal file
188
dyld3/Logging.cpp
Normal file
@ -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 <string.h>
|
||||
#include <stdint.h>
|
||||
#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
|
||||
|
62
dyld3/Logging.h
Normal file
62
dyld3/Logging.h
Normal file
@ -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 <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#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__
|
||||
|
||||
|
3938
dyld3/MachOAnalyzer.cpp
Normal file
3938
dyld3/MachOAnalyzer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
353
dyld3/MachOAnalyzer.h
Normal file
353
dyld3/MachOAnalyzer.h
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* 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 MachOAnalyzer_h
|
||||
#define MachOAnalyzer_h
|
||||
|
||||
|
||||
#include "MachOLoaded.h"
|
||||
#include "Array.h"
|
||||
#include "ClosureFileSystem.h"
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
// Extra functionality on loaded mach-o files only used during closure building
|
||||
struct VIS_HIDDEN MachOAnalyzer : public MachOLoaded
|
||||
{
|
||||
// protected members of subclass promoted to public here
|
||||
using MachOLoaded::SegmentInfo;
|
||||
using MachOLoaded::SectionInfo;
|
||||
using MachOLoaded::forEachSegment;
|
||||
using MachOLoaded::forEachSection;
|
||||
using MachOLoaded::forEachDependentDylib;
|
||||
using MachOLoaded::getDylibInstallName;
|
||||
using MachOLoaded::FoundSymbol;
|
||||
using MachOLoaded::findExportedSymbol;
|
||||
|
||||
static closure::LoadedFileInfo load(Diagnostics& diag, const closure::FileSystem& fileSystem,
|
||||
const char* logicalPath, const GradedArchs& archs, Platform platform, char realerPath[MAXPATHLEN]);
|
||||
static const MachOAnalyzer* validMainExecutable(Diagnostics& diag, const mach_header* mh, const char* path, uint64_t sliceLength,
|
||||
const GradedArchs& archs, Platform platform);
|
||||
|
||||
typedef void (^ExportsCallback)(const char* symbolName, uint64_t imageOffset, uint64_t flags,
|
||||
uint64_t other, const char* importName, bool& stop);
|
||||
bool validMachOForArchAndPlatform(Diagnostics& diag, size_t mappedSize, const char* path, const GradedArchs& archs, Platform platform) const;
|
||||
uint64_t mappedSize() const;
|
||||
bool hasObjC() const;
|
||||
bool hasPlusLoadMethod(Diagnostics& diag) const;
|
||||
uint64_t preferredLoadAddress() 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;
|
||||
|
||||
bool isEncrypted() const;
|
||||
void forEachCDHash(void (^handler)(const uint8_t cdHash[20])) const;
|
||||
bool hasCodeSignature(uint32_t& fileOffset, uint32_t& size) const;
|
||||
bool usesLibraryValidation() const;
|
||||
bool isRestricted() const;
|
||||
bool getEntry(uint32_t& offset, bool& usesCRT) const;
|
||||
bool isSlideable() const;
|
||||
bool hasInitializer(Diagnostics& diag, bool contentRebased, const void* dyldCache=nullptr) const;
|
||||
void forEachInitializerPointerSection(Diagnostics& diag, void (^callback)(uint32_t sectionOffset, uint32_t sectionSize, const uint8_t* content, bool& stop)) const;
|
||||
void forEachInitializer(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset), const void* dyldCache=nullptr) const;
|
||||
bool hasTerminators(Diagnostics& diag, bool contentRebased) const;
|
||||
void forEachTerminator(Diagnostics& diag, bool contentRebased, void (^callback)(uint32_t offset)) const;
|
||||
void forEachDOFSection(Diagnostics& diag, void (^callback)(uint32_t offset)) const;
|
||||
uint32_t segmentCount() const;
|
||||
void forEachExportedSymbol(Diagnostics& diag, ExportsCallback callback) 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)(uint64_t pointerAddress, bool bind, int bindLibOrdinal,
|
||||
const char* bindSymbolName, bool bindWeakImport, bool bindLazy, bool selfModifyingStub, bool& stop)) const;
|
||||
void forEachInterposingSection(Diagnostics& diag, void (^handler)(uint64_t vmOffset, uint64_t vmSize, bool& stop)) const;
|
||||
const void* content(uint64_t vmOffset);
|
||||
void forEachLocalReloc(void (^handler)(uint64_t runtimeOffset, bool& stop)) const;
|
||||
void forEachExternalReloc(void (^handler)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName, bool& stop)) const;
|
||||
|
||||
const void* getRebaseOpcodes(uint32_t& size) const;
|
||||
const void* getBindOpcodes(uint32_t& size) const;
|
||||
const void* getLazyBindOpcodes(uint32_t& size) const;
|
||||
const void* getSplitSeg(uint32_t& size) const;
|
||||
uint64_t segAndOffsetToRuntimeOffset(uint8_t segIndex, uint64_t segOffset) const;
|
||||
bool hasLazyPointers(uint32_t& runtimeOffset, uint32_t& size) const;
|
||||
void forEachRebase(Diagnostics& diag, bool ignoreLazyPointer, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
|
||||
void forEachTextRebase(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, bool& stop)) const;
|
||||
void forEachBind(Diagnostics& diag, void (^callback)(uint64_t runtimeOffset, int libOrdinal, const char* symbolName,
|
||||
bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
|
||||
void (^strongHandler)(const char* symbolName),
|
||||
void (^missingLazyBindHandler)()) const;
|
||||
void forEachChainedFixupTarget(Diagnostics& diag, void (^callback)(int libOrdinal, const char* symbolName, uint64_t addend, bool weakImport, bool& stop)) const;
|
||||
bool canHavePrecomputedDlopenClosure(const char* path, void (^failureReason)(const char*)) const;
|
||||
void forEachRebase(Diagnostics& diag, void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
|
||||
bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type, bool& stop)) const;
|
||||
void forEachBind(Diagnostics& diag, void (^handler)(const char* opcodeName, const LinkEditInfo& leInfo, const SegmentInfo segments[],
|
||||
bool segIndexSet, bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal,
|
||||
uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset,
|
||||
uint8_t type, const char* symbolName, bool weakImport, bool lazyBind, uint64_t addend, bool& stop),
|
||||
void (^strongHandler)(const char* symbolName),
|
||||
void (^missingLazyBindHandler)()) const;
|
||||
bool canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
|
||||
uint32_t loadCommandsFreeSpace() const;
|
||||
|
||||
#if DEBUG
|
||||
void validateDyldCacheDylib(Diagnostics& diag, const char* path) const;
|
||||
#endif
|
||||
void withChainStarts(Diagnostics& diag, uint64_t startsStructOffsetHint, void (^callback)(const dyld_chained_starts_in_image*)) const;
|
||||
uint64_t chainStartsOffset() const;
|
||||
uint16_t chainedPointerFormat() const;
|
||||
bool hasUnalignedPointerFixups() const;
|
||||
|
||||
const MachOAnalyzer* remapIfZeroFill(Diagnostics& diag, const closure::FileSystem& fileSystem, closure::LoadedFileInfo& info) const;
|
||||
|
||||
struct ObjCInfo {
|
||||
uint32_t selRefCount;
|
||||
uint32_t classDefCount;
|
||||
uint32_t protocolDefCount;
|
||||
};
|
||||
ObjCInfo getObjCInfo() const;
|
||||
|
||||
// This optionally caches a list of sections for lookup
|
||||
struct SectionCache {
|
||||
private:
|
||||
SectionInfo buffer[2];
|
||||
|
||||
public:
|
||||
SectionCache(const MachOAnalyzer* ma) : ma(ma) { }
|
||||
|
||||
bool findSectionForVMAddr(uint64_t vmAddr, bool (^sectionHandler)(const SectionInfo& sectionInfo));
|
||||
|
||||
const MachOAnalyzer* ma = nullptr;
|
||||
dyld3::OverflowSafeArray<SectionInfo> sectionInfos = { buffer, sizeof(buffer) / sizeof(buffer[0]) };
|
||||
};
|
||||
|
||||
// Caches data useful for converting from raw data to VM addresses
|
||||
struct VMAddrConverter {
|
||||
uint64_t preferredLoadAddress = 0;
|
||||
intptr_t slide = 0;
|
||||
uint16_t chainedPointerFormat = 0;
|
||||
bool contentRebased = false;
|
||||
};
|
||||
|
||||
struct ObjCClassInfo {
|
||||
// These fields are all present on the objc_class_t struct
|
||||
uint64_t isaVMAddr = 0;
|
||||
uint64_t superclassVMAddr = 0;
|
||||
//uint64_t methodCacheBuckets;
|
||||
//uint64_t methodCacheProperties;
|
||||
uint64_t dataVMAddr = 0;
|
||||
|
||||
// This field is only present if this is a Swift object, ie, has the Swift
|
||||
// fast bits set
|
||||
uint32_t swiftClassFlags = 0;
|
||||
|
||||
// These are taken from the low bits of the dataVMAddr value
|
||||
bool isSwiftLegacy = false;
|
||||
bool isSwiftStable = false;
|
||||
|
||||
// Cache the data to convert vmAddr's
|
||||
MachOAnalyzer::VMAddrConverter vmAddrConverter;
|
||||
|
||||
// These are from the class_ro_t which data points to
|
||||
enum class ReadOnlyDataField {
|
||||
name,
|
||||
baseMethods
|
||||
};
|
||||
|
||||
uint64_t getReadOnlyDataField(ReadOnlyDataField field, uint32_t pointerSize) const;
|
||||
uint64_t nameVMAddr(uint32_t pointerSize) const {
|
||||
return getReadOnlyDataField(ReadOnlyDataField::name, pointerSize);
|
||||
}
|
||||
uint64_t baseMethodsVMAddr(uint32_t pointerSize) const {
|
||||
return getReadOnlyDataField(ReadOnlyDataField::baseMethods, pointerSize);
|
||||
}
|
||||
|
||||
// These are embedded in the Mach-O itself by the compiler
|
||||
enum FastDataBits {
|
||||
FAST_IS_SWIFT_LEGACY = 0x1,
|
||||
FAST_IS_SWIFT_STABLE = 0x2
|
||||
};
|
||||
|
||||
// These are embedded by the Swift compiler in the swiftClassFlags field
|
||||
enum SwiftClassFlags {
|
||||
isSwiftPreStableABI = 0x1
|
||||
};
|
||||
|
||||
// Note this is taken from the objc runtime
|
||||
bool isUnfixedBackwardDeployingStableSwift() const {
|
||||
// Only classes marked as Swift legacy need apply.
|
||||
if (!isSwiftLegacy) return false;
|
||||
|
||||
// Check the true legacy vs stable distinguisher.
|
||||
// The low bit of Swift's ClassFlags is SET for true legacy
|
||||
// and UNSET for stable pretending to be legacy.
|
||||
bool isActuallySwiftLegacy = (swiftClassFlags & isSwiftPreStableABI) != 0;
|
||||
return !isActuallySwiftLegacy;
|
||||
}
|
||||
};
|
||||
|
||||
struct ObjCImageInfo {
|
||||
uint32_t version;
|
||||
uint32_t flags;
|
||||
|
||||
// FIXME: Put this somewhere objc can see it.
|
||||
enum : uint32_t {
|
||||
dyldPreoptimized = 1 << 7
|
||||
};
|
||||
};
|
||||
|
||||
struct ObjCMethod {
|
||||
uint64_t nameVMAddr; // & SEL
|
||||
uint64_t typesVMAddr; // & const char *
|
||||
uint64_t impVMAddr; // & IMP
|
||||
|
||||
// We also need to know where the reference to the nameVMAddr was
|
||||
// This is so that we know how to rebind that location
|
||||
uint64_t nameLocationVMAddr;
|
||||
};
|
||||
|
||||
struct ObjCCategory {
|
||||
uint64_t nameVMAddr;
|
||||
uint64_t clsVMAddr;
|
||||
uint64_t instanceMethodsVMAddr;
|
||||
uint64_t classMethodsVMAddr;
|
||||
uint64_t protocolsVMAddr;
|
||||
uint64_t instancePropertiesVMAddr;
|
||||
};
|
||||
|
||||
struct ObjCProtocol {
|
||||
uint64_t isaVMAddr;
|
||||
uint64_t nameVMAddr;
|
||||
//uint64_t protocolsVMAddr;
|
||||
uint64_t instanceMethodsVMAddr;
|
||||
uint64_t classMethodsVMAddr;
|
||||
uint64_t optionalInstanceMethodsVMAddr;
|
||||
uint64_t optionalClassMethodsVMAddr;
|
||||
//uint64_t instancePropertiesVMAddr;
|
||||
//uint32_t size;
|
||||
//uint32_t flags;
|
||||
// Fields below this point are not always present on disk.
|
||||
//uint64_t extendedMethodTypesVMAddr;
|
||||
//uint64_t demangledNameVMAddr;
|
||||
//uint64_t classPropertiesVMAddr;
|
||||
|
||||
// Note this isn't in a protocol, but we use it in dyld to track if the protocol
|
||||
// is large enough to avoid a reallocation in objc.
|
||||
bool requiresObjCReallocation;
|
||||
};
|
||||
|
||||
enum class PrintableStringResult {
|
||||
CanPrint,
|
||||
FairPlayEncrypted,
|
||||
ProtectedSection,
|
||||
UnknownSection
|
||||
};
|
||||
|
||||
const char* getPrintableString(uint64_t stringVMAddr, PrintableStringResult& result,
|
||||
SectionCache* sectionCache = nullptr,
|
||||
bool (^sectionHandler)(const SectionInfo& sectionInfo) = nullptr) const;
|
||||
|
||||
void forEachObjCClass(Diagnostics& diag, bool contentRebased,
|
||||
void (^handler)(Diagnostics& diag, uint64_t classVMAddr,
|
||||
uint64_t classSuperclassVMAddr, uint64_t classDataVMAddr,
|
||||
const ObjCClassInfo& objcClass, bool isMetaClass)) const;
|
||||
|
||||
void forEachObjCCategory(Diagnostics& diag, bool contentRebased,
|
||||
void (^handler)(Diagnostics& diag, uint64_t categoryVMAddr,
|
||||
const dyld3::MachOAnalyzer::ObjCCategory& objcCategory)) const;
|
||||
|
||||
void forEachObjCProtocol(Diagnostics& diag, bool contentRebased,
|
||||
void (^handler)(Diagnostics& diag, uint64_t protocolVMAddr,
|
||||
const dyld3::MachOAnalyzer::ObjCProtocol& objCProtocol)) const;
|
||||
|
||||
// Walk a method list starting from its vmAddr.
|
||||
// Note, classes, categories, protocols, etc, all share the same method list struture so can all use this.
|
||||
void forEachObjCMethod(uint64_t methodListVMAddr, bool contentRebased,
|
||||
void (^handler)(uint64_t methodVMAddr, const ObjCMethod& method)) const;
|
||||
|
||||
void forEachObjCSelectorReference(Diagnostics& diag, bool contentRebased,
|
||||
void (^handler)(uint64_t selRefVMAddr, uint64_t selRefTargetVMAddr)) const;
|
||||
|
||||
void forEachObjCMethodName(void (^handler)(const char* methodName)) const;
|
||||
|
||||
bool hasObjCMessageReferences() const;
|
||||
|
||||
const ObjCImageInfo* objcImageInfo() const;
|
||||
|
||||
void forEachWeakDef(Diagnostics& diag, void (^handler)(const char* symbolName, uintptr_t imageOffset, bool isFromExportTrie)) const;
|
||||
|
||||
private:
|
||||
|
||||
struct SegmentStuff
|
||||
{
|
||||
uint64_t fileOffset;
|
||||
uint64_t fileSize;
|
||||
uint64_t writable : 1,
|
||||
executable : 1,
|
||||
textRelocsAllowed : 1, // segment supports text relocs (i386 only)
|
||||
segSize : 61;
|
||||
};
|
||||
|
||||
enum class Malformed { linkeditOrder, linkeditAlignment, linkeditPermissions, dyldInfoAndlocalRelocs, segmentOrder, textPermissions, executableData, codeSigAlignment };
|
||||
bool enforceFormat(Malformed) const;
|
||||
|
||||
const uint8_t* getContentForVMAddr(const LayoutInfo& info, uint64_t vmAddr) const;
|
||||
bool validLoadCommands(Diagnostics& diag, const char* path, size_t fileLen) const;
|
||||
bool validEmbeddedPaths(Diagnostics& diag, Platform platform, const char* path) const;
|
||||
bool validSegments(Diagnostics& diag, const char* path, size_t fileLen) const;
|
||||
bool validLinkedit(Diagnostics& diag, const char* path) const;
|
||||
bool validLinkeditLayout(Diagnostics& diag, const char* path) const;
|
||||
bool validRebaseInfo(Diagnostics& diag, const char* path) const;
|
||||
bool validBindInfo(Diagnostics& diag, const char* path) const;
|
||||
bool validMain(Diagnostics& diag, const char* path) const;
|
||||
bool validChainedFixupsInfo(Diagnostics& diag, const char* path) const;
|
||||
|
||||
bool invalidRebaseState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
|
||||
bool segIndexSet, uint32_t pointerSize, uint8_t segmentIndex, uint64_t segmentOffset, uint8_t type) const;
|
||||
bool invalidBindState(Diagnostics& diag, const char* opcodeName, const char* path, const LinkEditInfo& leInfo, const SegmentInfo segments[],
|
||||
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 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;
|
||||
|
||||
void getAllSegmentsInfos(Diagnostics& diag, SegmentInfo segments[]) const;
|
||||
bool segmentHasTextRelocs(uint32_t segIndex) const;
|
||||
uint64_t relocBaseAddress(const SegmentInfo segmentsInfos[], uint32_t segCount) const;
|
||||
bool segIndexAndOffsetForAddress(uint64_t addr, const SegmentInfo segmentsInfos[], uint32_t segCount, uint32_t& segIndex, uint64_t& segOffset) const;
|
||||
void parseOrgArm64eChainedFixups(Diagnostics& diag, void (^targetCount)(uint32_t totalTargets, bool& stop),
|
||||
void (^addTarget)(const LinkEditInfo& leInfo, const SegmentInfo segments[], bool libraryOrdinalSet, uint32_t dylibCount, int libOrdinal, uint8_t type, const char* symbolName, uint64_t addend, bool weakImport, bool& stop),
|
||||
void (^addChainStart)(const LinkEditInfo& leInfo, const SegmentInfo segments[], uint8_t segmentIndex, bool segIndexSet, uint64_t segmentOffset, uint16_t format, bool& stop)) const;
|
||||
bool contentIsRegularStub(const uint8_t* helperContent) const;
|
||||
uint64_t entryAddrFromThreadCmd(const thread_command* cmd) const;
|
||||
void recurseTrie(Diagnostics& diag, const uint8_t* const start, const uint8_t* p, const uint8_t* const end,
|
||||
OverflowSafeArray<char>& cummulativeString, int curStrOffset, bool& stop, MachOAnalyzer::ExportsCallback callback) const;
|
||||
void analyzeSegmentsLayout(uint64_t& vmSpace, bool& hasZeroFill) const;
|
||||
|
||||
};
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* MachOAnalyzer_h */
|
1205
dyld3/MachOFile.cpp
Normal file
1205
dyld3/MachOFile.cpp
Normal file
File diff suppressed because it is too large
Load Diff
273
dyld3/MachOFile.h
Normal file
273
dyld3/MachOFile.h
Normal file
@ -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@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MachOFile_h
|
||||
#define MachOFile_h
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include "Diagnostics.h"
|
||||
#include "SupportedArchs.h"
|
||||
#include <mach-o/fixup-chains.h>
|
||||
#include <mach-o/loader.h>
|
||||
|
||||
// needed until dyld builds with a newer SDK
|
||||
#ifndef CPU_SUBTYPE_ARM64E
|
||||
#define CPU_SUBTYPE_ARM64E 2
|
||||
#endif
|
||||
#ifndef CPU_TYPE_ARM64_32
|
||||
#define CPU_TYPE_ARM64_32 0x0200000C
|
||||
#endif
|
||||
#ifndef CPU_SUBTYPE_ARM64_32_V8
|
||||
#define CPU_SUBTYPE_ARM64_32_V8 1
|
||||
#endif
|
||||
#ifndef BIND_OPCODE_THREADED
|
||||
#define BIND_OPCODE_THREADED 0xD0
|
||||
#endif
|
||||
#ifndef BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB
|
||||
#define BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB 0x00
|
||||
#endif
|
||||
#ifndef BIND_SUBOPCODE_THREADED_APPLY
|
||||
#define BIND_SUBOPCODE_THREADED_APPLY 0x01
|
||||
#endif
|
||||
#ifndef BIND_SPECIAL_DYLIB_WEAK_LOOKUP
|
||||
#define BIND_SPECIAL_DYLIB_WEAK_LOOKUP (-3)
|
||||
#endif
|
||||
#ifndef EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE
|
||||
#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE 0x02
|
||||
#endif
|
||||
#ifndef SG_READ_ONLY
|
||||
#define SG_READ_ONLY 0x10
|
||||
#endif
|
||||
|
||||
#ifndef LC_DYLD_EXPORTS_TRIE
|
||||
#define LC_DYLD_EXPORTS_TRIE 0x80000033
|
||||
#endif
|
||||
#ifndef LC_DYLD_CHAINED_FIXUPS
|
||||
#define LC_DYLD_CHAINED_FIXUPS 0x80000034
|
||||
#endif
|
||||
|
||||
#ifndef S_INIT_FUNC_OFFSETS
|
||||
#define S_INIT_FUNC_OFFSETS 0x16
|
||||
#endif
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
|
||||
|
||||
/// Returns true if (addLHS + addRHS) > b, or if the add overflowed
|
||||
template<typename T>
|
||||
inline 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<typename T>
|
||||
inline bool greaterThanAddOrOverflow(uint64_t addLHS, uint64_t addRHS, T b) {
|
||||
return (addLHS > b) || (addRHS > (b-addLHS));
|
||||
}
|
||||
|
||||
|
||||
// Note, this should match PLATFORM_* values in <mach-o/loader.h>
|
||||
enum class Platform {
|
||||
unknown = 0,
|
||||
macOS = 1, // PLATFORM_MACOS
|
||||
iOS = 2, // PLATFORM_IOS
|
||||
tvOS = 3, // PLATFORM_TVOS
|
||||
watchOS = 4, // PLATFORM_WATCHOS
|
||||
bridgeOS = 5, // PLATFORM_BRIDGEOS
|
||||
iOSMac = 6, // PLATFORM_IOSMAC
|
||||
iOS_simulator = 7, // PLATFORM_IOSSIMULATOR
|
||||
tvOS_simulator = 8, // PLATFORM_TVOSSIMULATOR
|
||||
watchOS_simulator = 9, // PLATFORM_WATCHOSSIMULATOR
|
||||
driverKit = 10, // PLATFORM_DRIVERKIT
|
||||
};
|
||||
|
||||
struct MachOFile; // forward ref
|
||||
|
||||
// A prioritized list of architectures
|
||||
class VIS_HIDDEN GradedArchs {
|
||||
public:
|
||||
// never construct new ones - just use existing static instances
|
||||
GradedArchs() = delete;
|
||||
GradedArchs(const GradedArchs&) = delete;
|
||||
|
||||
static const GradedArchs& forCurrentOS(const MachOFile* mainExecutable);
|
||||
static const GradedArchs& forName(const char* archName, bool forMainExecutable = false);
|
||||
|
||||
int grade(uint32_t cputype, uint32_t cpusubtype) const;
|
||||
const char* name() const;
|
||||
|
||||
// pre-built lists for existing hardware
|
||||
static const GradedArchs i386; // 32-bit Mac
|
||||
static const GradedArchs x86_64; // older Mac
|
||||
static const GradedArchs x86_64h; // haswell Mac
|
||||
static const GradedArchs arm64; // A11 or earlier iPhone or iPad
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
static const GradedArchs arm64e; // A12 or later iPhone or iPad
|
||||
static const GradedArchs arm64e_compat; // A12 running arm64 main executable
|
||||
#endif
|
||||
static const GradedArchs armv7k; // watch thru series 3
|
||||
static const GradedArchs armv7s; // deprecated
|
||||
static const GradedArchs armv7; // deprecated
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
static const GradedArchs arm64_32; // watch series 4 and later
|
||||
#endif
|
||||
|
||||
// private:
|
||||
// should be private, but compiler won't statically initialize static members above
|
||||
struct CpuGrade { uint32_t type; uint32_t subtype; uint32_t grade; };
|
||||
const CpuGrade _orderedCpuTypes[3]; // zero terminated
|
||||
};
|
||||
|
||||
|
||||
// A file read/mapped into memory
|
||||
struct VIS_HIDDEN FatFile : fat_header
|
||||
{
|
||||
static const FatFile* isFatFile(const void* fileContent);
|
||||
void forEachSlice(Diagnostics& diag, uint64_t fileLen, void (^callback)(uint32_t sliceCpuType, uint32_t sliceCpuSubType, const void* sliceStart, uint64_t sliceSize, bool& stop)) const;
|
||||
bool isFatFileWithSlice(Diagnostics& diag, uint64_t fileLen, const GradedArchs& archs, uint64_t& sliceOffset, uint64_t& sliceLen, bool& missingSlice) const;
|
||||
|
||||
private:
|
||||
bool isValidSlice(Diagnostics& diag, uint64_t fileLen, uint32_t sliceIndex,
|
||||
uint32_t sliceCpuType, uint32_t sliceCpuSubType, uint64_t sliceOffset, uint64_t sliceLen) const;
|
||||
};
|
||||
|
||||
|
||||
// A mach-o file read/mapped into memory
|
||||
// Only info from mach_header or load commands is accessible (no LINKEDIT info)
|
||||
struct VIS_HIDDEN MachOFile : mach_header
|
||||
{
|
||||
static const char* archName(uint32_t cputype, uint32_t cpusubtype);
|
||||
static const char* platformName(Platform platform);
|
||||
static uint32_t cpuTypeFromArchName(const char* archName);
|
||||
static uint32_t cpuSubtypeFromArchName(const char* archName);
|
||||
static void packedVersionToString(uint32_t packedVersion, char versionString[32]);
|
||||
static const char* currentArchName();
|
||||
static Platform currentPlatform();
|
||||
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 isSimulatorPlatform(Platform platform);
|
||||
static bool isSharedCacheEligiblePath(const char* path);
|
||||
|
||||
bool hasMachOMagic() const;
|
||||
bool isMachO(Diagnostics& diag, uint64_t fileSize) const;
|
||||
bool isDylib() const;
|
||||
bool isBundle() const;
|
||||
bool isMainExecutable() const;
|
||||
bool isDynamicExecutable() const;
|
||||
bool isStaticExecutable() const;
|
||||
bool isPreload() const;
|
||||
bool isPIE() const;
|
||||
bool isArch(const char* archName) const;
|
||||
const char* archName() const;
|
||||
bool is64() const;
|
||||
size_t machHeaderSize() const;
|
||||
uint32_t pointerSize() const;
|
||||
bool uses16KPages() const;
|
||||
bool supportsPlatform(Platform) const;
|
||||
bool isZippered() const;
|
||||
bool inDyldCache() const;
|
||||
bool isSimulatorBinary() const;
|
||||
bool getUuid(uuid_t uuid) const;
|
||||
bool hasWeakDefs() const;
|
||||
bool hasThreadLocalVariables() const;
|
||||
bool getDylibInstallName(const char** installName, uint32_t* compatVersion, uint32_t* currentVersion) const;
|
||||
void forEachSupportedPlatform(void (^callback)(Platform platform, uint32_t minOS, uint32_t sdk)) const;
|
||||
const char* installName() const; // returns nullptr is no install name
|
||||
void forEachDependentDylib(void (^callback)(const char* loadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool& stop)) const;
|
||||
bool canBePlacedInDyldCache(const char* path, void (^failureReason)(const char*)) const;
|
||||
bool canBeFairPlayEncrypted() const;
|
||||
bool isFairPlayEncrypted(uint32_t& textOffset, uint32_t& size) const;
|
||||
bool allowsAlternatePlatform() const;
|
||||
bool hasChainedFixups() const;
|
||||
void forDyldEnv(void (^callback)(const char* envVar, bool& stop)) const;
|
||||
bool enforceCompatVersion() const;
|
||||
|
||||
struct SegmentInfo
|
||||
{
|
||||
uint64_t fileOffset;
|
||||
uint64_t fileSize;
|
||||
uint64_t vmAddr;
|
||||
uint64_t vmSize;
|
||||
uint64_t sizeOfSections;
|
||||
const char* segName;
|
||||
uint32_t loadCommandOffset;
|
||||
uint32_t protections;
|
||||
uint32_t textRelocs : 1, // segment has text relocs (i386 only)
|
||||
readOnlyData : 1,
|
||||
isProtected : 1, // segment is protected
|
||||
segIndex : 13,
|
||||
p2align : 16;
|
||||
bool readable() const { return protections & VM_PROT_READ; }
|
||||
bool writable() const { return protections & VM_PROT_WRITE; }
|
||||
bool executable() const { return protections & VM_PROT_EXECUTE; }
|
||||
};
|
||||
|
||||
struct SectionInfo
|
||||
{
|
||||
SegmentInfo segInfo;
|
||||
uint64_t sectAddr;
|
||||
uint64_t sectSize;
|
||||
const char* sectName;
|
||||
uint32_t sectFileOffset;
|
||||
uint32_t sectFlags;
|
||||
uint32_t sectAlignP2;
|
||||
uint32_t reserved1;
|
||||
uint32_t reserved2;
|
||||
};
|
||||
|
||||
void forEachSegment(void (^callback)(const SegmentInfo& info, bool& stop)) const;
|
||||
void forEachSection(void (^callback)(const SectionInfo& sectInfo, bool malformedSectionRange, bool& stop)) const;
|
||||
|
||||
protected:
|
||||
bool hasMachOBigEndianMagic() const;
|
||||
void forEachLoadCommand(Diagnostics& diag, void (^callback)(const load_command* cmd, bool& stop)) const;
|
||||
bool hasLoadCommand(uint32_t) const;
|
||||
|
||||
const encryption_info_command* findFairPlayEncryptionLoadCommand() const;
|
||||
|
||||
struct ArchInfo
|
||||
{
|
||||
const char* name;
|
||||
uint32_t cputype;
|
||||
uint32_t cpusubtype;
|
||||
};
|
||||
static const ArchInfo _s_archInfos[];
|
||||
|
||||
struct PlatformInfo
|
||||
{
|
||||
const char* name;
|
||||
Platform platform;
|
||||
uint32_t loadCommand;
|
||||
};
|
||||
static const PlatformInfo _s_platformInfos[];
|
||||
};
|
||||
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* MachOFile_h */
|
1268
dyld3/MachOLoaded.cpp
Normal file
1268
dyld3/MachOLoaded.cpp
Normal file
File diff suppressed because it is too large
Load Diff
201
dyld3/MachOLoaded.h
Normal file
201
dyld3/MachOLoaded.h
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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 MachOLoaded_h
|
||||
#define MachOLoaded_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Array.h"
|
||||
#include "MachOFile.h"
|
||||
|
||||
|
||||
class CacheBuilder;
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
|
||||
// A mach-o mapped into memory with zero-fill expansion
|
||||
// Can be used in dyld at runtime or during closure building
|
||||
struct VIS_HIDDEN MachOLoaded : public MachOFile
|
||||
{
|
||||
typedef const MachOLoaded* (^DependentToMachOLoaded)(const MachOLoaded* image, uint32_t depIndex);
|
||||
|
||||
// for dlsym()
|
||||
bool hasExportedSymbol(const char* symbolName, DependentToMachOLoaded finder, void** result,
|
||||
bool* resultPointsToInstructions) const;
|
||||
|
||||
// for DYLD_PRINT_SEGMENTS
|
||||
const char* segmentName(uint32_t segIndex) const;
|
||||
|
||||
// used to see if main executable overlaps shared region
|
||||
bool intersectsRange(uintptr_t start, uintptr_t length) const;
|
||||
|
||||
// for _dyld_get_image_slide()
|
||||
intptr_t getSlide() const;
|
||||
|
||||
// for dladdr()
|
||||
bool findClosestSymbol(uint64_t unSlidAddr, const char** symbolName, uint64_t* symbolUnslidAddr) const;
|
||||
|
||||
// for _dyld_find_unwind_sections()
|
||||
const void* findSectionContent(const char* segName, const char* sectName, uint64_t& size) const;
|
||||
|
||||
// used at runtime to validate loaded image matches closure
|
||||
void forEachCDHashOfCodeSignature(const void* codeSigStart, size_t codeSignLen,
|
||||
void (^callback)(const uint8_t cdHash[20])) const;
|
||||
|
||||
// used by DyldSharedCache to find closure
|
||||
static const uint8_t* trieWalk(Diagnostics& diag, const uint8_t* start, const uint8_t* end, const char* symbol);
|
||||
|
||||
// used by cache builder during error handling in chain bind processing
|
||||
const char* dependentDylibLoadPath(uint32_t depIndex) const;
|
||||
|
||||
// used by closure builder to find the offset and size of the trie.
|
||||
bool hasExportTrie(uint32_t& runtimeOffset, uint32_t& size) const;
|
||||
|
||||
// used by dyld/libdyld to apply fixups
|
||||
#if BUILDING_DYLD || BUILDING_LIBDYLD
|
||||
void fixupAllChainedFixups(Diagnostics& diag, const dyld_chained_starts_in_image* starts, uintptr_t slide,
|
||||
Array<const void*> bindTargets, void (^fixupLogger)(void* loc, void* newValue)) const;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// For use with new rebase/bind scheme were each fixup location on disk contains info on what
|
||||
// fix up it needs plus the offset to the next fixup.
|
||||
union ChainedFixupPointerOnDisk
|
||||
{
|
||||
union Arm64e {
|
||||
dyld_chained_ptr_arm64e_auth_rebase authRebase;
|
||||
dyld_chained_ptr_arm64e_auth_bind authBind;
|
||||
dyld_chained_ptr_arm64e_rebase rebase;
|
||||
dyld_chained_ptr_arm64e_bind bind;
|
||||
|
||||
uint64_t signExtendedAddend() const;
|
||||
uint64_t unpackTarget() const;
|
||||
const char* keyName() const;
|
||||
uint64_t signPointer(void* loc, uint64_t target) const;
|
||||
};
|
||||
|
||||
union Generic64 {
|
||||
dyld_chained_ptr_64_rebase rebase;
|
||||
dyld_chained_ptr_64_bind bind;
|
||||
|
||||
uint64_t signExtendedAddend() const;
|
||||
uint64_t unpackedTarget() const;
|
||||
};
|
||||
|
||||
union Generic32 {
|
||||
dyld_chained_ptr_32_rebase rebase;
|
||||
dyld_chained_ptr_32_bind bind;
|
||||
|
||||
uint64_t signExtendedAddend() const;
|
||||
};
|
||||
|
||||
typedef dyld_chained_ptr_32_cache_rebase Cache32;
|
||||
|
||||
uint64_t raw64;
|
||||
Arm64e arm64e;
|
||||
Generic64 generic64;
|
||||
|
||||
uint32_t raw32;
|
||||
Generic32 generic32;
|
||||
Cache32 cache32;
|
||||
|
||||
|
||||
bool isRebase(uint16_t pointerFormat, uint64_t preferedLoadAddress, uint64_t& targetRuntimeOffset) const;
|
||||
bool isBind(uint16_t pointerFormat, uint32_t& bindOrdinal) const;
|
||||
};
|
||||
|
||||
|
||||
struct LayoutInfo {
|
||||
uintptr_t slide;
|
||||
uintptr_t textUnslidVMAddr;
|
||||
uintptr_t linkeditUnslidVMAddr;
|
||||
uint32_t linkeditFileOffset;
|
||||
uint32_t linkeditFileSize;
|
||||
uint32_t linkeditSegIndex;
|
||||
};
|
||||
|
||||
struct LinkEditInfo
|
||||
{
|
||||
const dyld_info_command* dyldInfo;
|
||||
const linkedit_data_command* exportsTrie;
|
||||
const linkedit_data_command* chainedFixups;
|
||||
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;
|
||||
|
||||
// use by dyldinfo
|
||||
void forEachFixupInAllChains(Diagnostics& diag, const dyld_chained_starts_in_image* starts, bool notifyNonPointers,
|
||||
void (^callback)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const;
|
||||
protected:
|
||||
friend CacheBuilder;
|
||||
|
||||
struct FoundSymbol {
|
||||
enum class Kind { headerOffset, absolute, resolverOffset };
|
||||
Kind kind;
|
||||
bool isThreadLocal;
|
||||
bool isWeakDef;
|
||||
const MachOLoaded* foundInDylib;
|
||||
uint64_t value;
|
||||
uint32_t resolverFuncOffset;
|
||||
const char* foundSymbolName;
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
friend CacheBuilder;
|
||||
|
||||
bool findExportedSymbol(Diagnostics& diag, const char* symbolName, bool weakImport, FoundSymbol& foundInfo, DependentToMachOLoaded finder) const;
|
||||
|
||||
void getLinkEditLoadCommands(Diagnostics& diag, LinkEditInfo& result) const;
|
||||
void getLayoutInfo(LayoutInfo&) const;
|
||||
const uint8_t* getLinkEditContent(const LayoutInfo& info, uint32_t fileOffset) const;
|
||||
const uint8_t* getExportsTrie(const LinkEditInfo& info, uint64_t& trieSize) 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;
|
||||
uint32_t dependentDylibCount() const;
|
||||
bool findClosestFunctionStart(uint64_t address, uint64_t* functionStartAddress) const;
|
||||
|
||||
// This calls the callback for all code directories required for a given platform/binary combination.
|
||||
// On watchOS main executables this is all cd hashes.
|
||||
// On watchOS dylibs this is only the single cd hash we need (by rank defined by dyld, not the kernel).
|
||||
// On all other platforms this always returns a single best cd hash (ranked to match the kernel).
|
||||
// Note the callback parameter is really a CS_CodeDirectory.
|
||||
void forEachCodeDirectoryBlob(const void* codeSigStart, size_t codeSignLen, void (^callback)(const void* cd)) const;
|
||||
bool walkChain(Diagnostics& diag, const dyld_chained_starts_in_segment* segInfo, uint32_t pageIndex, uint16_t offsetInPage,
|
||||
bool notifyNonPointers, void (^handler)(ChainedFixupPointerOnDisk* fixupLocation, const dyld_chained_starts_in_segment* segInfo, bool& stop)) const;
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* MachOLoaded_h */
|
431
dyld3/Map.h
Normal file
431
dyld3/Map.h
Normal file
@ -0,0 +1,431 @@
|
||||
/*
|
||||
* 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 Map_h
|
||||
#define Map_h
|
||||
|
||||
#include "Array.h"
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
struct Hash {
|
||||
static size_t hash(const T&);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Equal {
|
||||
static bool equal(const T&a, const T& b);
|
||||
};
|
||||
|
||||
template<typename KeyT, typename ValueT, class GetHash = Hash<KeyT>, class IsEqual = Equal<KeyT>>
|
||||
class Map {
|
||||
typedef std::pair<KeyT, ValueT> NodeT;
|
||||
typedef NodeT* iterator;
|
||||
typedef const NodeT* const_iterator;
|
||||
|
||||
enum : size_t {
|
||||
SentinelHash = (size_t)-1
|
||||
};
|
||||
|
||||
public:
|
||||
Map() {
|
||||
// Keep the hash buffer about 75% full
|
||||
nextHashBufferGrowth = 768;
|
||||
hashBufferUseCount = 0;
|
||||
hashBuffer.reserve(1024);
|
||||
for (size_t i = 0; i != 1024; ++i) {
|
||||
hashBuffer.push_back(SentinelHash);
|
||||
}
|
||||
nodeBuffer.reserve(1024);
|
||||
}
|
||||
|
||||
iterator find(const KeyT& key) {
|
||||
// Find the index to look up in the hash buffer
|
||||
size_t hashIndex = GetHash::hash(key) & (hashBuffer.count() - 1);
|
||||
|
||||
// Note we'll use a quadratic probe to look past identical hashes until we find our node or a sentinel
|
||||
size_t probeAmount = 1;
|
||||
while (true) {
|
||||
size_t nodeBufferIndex = hashBuffer[hashIndex];
|
||||
|
||||
if (nodeBufferIndex == SentinelHash) {
|
||||
// This node is unused, so we don't have this element
|
||||
return end();
|
||||
}
|
||||
|
||||
// If that hash is in use, then check if that node is actually the one we are trying to find
|
||||
if (IsEqual::equal(nodeBuffer[nodeBufferIndex].first, key)) {
|
||||
// Keys match so we found this element
|
||||
return &nodeBuffer[nodeBufferIndex];
|
||||
}
|
||||
|
||||
// We didn't find this node, so try with a later one
|
||||
hashIndex += probeAmount;
|
||||
hashIndex &= (hashBuffer.count() - 1);
|
||||
++probeAmount;
|
||||
}
|
||||
|
||||
assert(0 && "unreachable");
|
||||
}
|
||||
|
||||
const_iterator find(const KeyT& key) const {
|
||||
// Find the index to look up in the hash buffer
|
||||
size_t hashIndex = GetHash::hash(key) & (hashBuffer.count() - 1);
|
||||
|
||||
// Note we'll use a quadratic probe to look past identical hashes until we find our node or a sentinel
|
||||
size_t probeAmount = 1;
|
||||
while (true) {
|
||||
size_t nodeBufferIndex = hashBuffer[hashIndex];
|
||||
|
||||
if (nodeBufferIndex == SentinelHash) {
|
||||
// This node is unused, so we don't have this element
|
||||
return end();
|
||||
}
|
||||
|
||||
// If that hash is in use, then check if that node is actually the one we are trying to find
|
||||
if (IsEqual::equal(nodeBuffer[nodeBufferIndex].first, key)) {
|
||||
// Keys match so we found this element
|
||||
return &nodeBuffer[nodeBufferIndex];
|
||||
}
|
||||
|
||||
// We didn't find this node, so try with a later one
|
||||
hashIndex += probeAmount;
|
||||
hashIndex &= (hashBuffer.count() - 1);
|
||||
++probeAmount;
|
||||
}
|
||||
|
||||
assert(0 && "unreachable");
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return nodeBuffer.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return nodeBuffer.end();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return nodeBuffer.begin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return nodeBuffer.end();
|
||||
}
|
||||
|
||||
const Array<NodeT>& array() const {
|
||||
return nodeBuffer;
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> insert(NodeT&& v) {
|
||||
// First see if we have enough space. We don't want the hash buffer to get too full.
|
||||
if (hashBufferUseCount == nextHashBufferGrowth) {
|
||||
// Grow and rehash everything.
|
||||
size_t newHashTableSize = hashBuffer.count() * 2;
|
||||
nextHashBufferGrowth *= 2;
|
||||
|
||||
dyld3::OverflowSafeArray<size_t> newHashBuffer;
|
||||
newHashBuffer.reserve(newHashTableSize);
|
||||
for (size_t i = 0; i != newHashTableSize; ++i) {
|
||||
newHashBuffer.push_back(SentinelHash);
|
||||
}
|
||||
|
||||
// Walk the existing nodes trying to populate the new hash buffer and looking for collisions
|
||||
for (size_t i = 0; i != nodeBuffer.count(); ++i) {
|
||||
const KeyT& key = nodeBuffer[i].first;
|
||||
size_t newHashIndex = GetHash::hash(key) & (newHashBuffer.count() - 1);
|
||||
|
||||
// Note we'll use a quadratic probe to look past identical hashes until we find our node or a sentinel
|
||||
size_t probeAmount = 1;
|
||||
while (true) {
|
||||
size_t newNodeBufferIndex = newHashBuffer[newHashIndex];
|
||||
|
||||
if (newNodeBufferIndex == SentinelHash) {
|
||||
// This node is unused, so we don't have this element. Lets add it
|
||||
newHashBuffer[newHashIndex] = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't bother checking for matching keys here. We know we are adding elements with different keys
|
||||
// Just probe to find the next sentinel
|
||||
|
||||
// We didn't find this node, so try with a later one
|
||||
newHashIndex += probeAmount;
|
||||
newHashIndex &= (newHashBuffer.count() - 1);
|
||||
++probeAmount;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the new buffer
|
||||
hashBuffer = std::move(newHashBuffer);
|
||||
}
|
||||
|
||||
// Find the index to look up in the hash buffer
|
||||
size_t hashIndex = GetHash::hash(v.first) & (hashBuffer.count() - 1);
|
||||
|
||||
// Note we'll use a quadratic probe to look past identical hashes until we find our node or a sentinel
|
||||
size_t probeAmount = 1;
|
||||
while (true) {
|
||||
size_t nodeBufferIndex = hashBuffer[hashIndex];
|
||||
|
||||
if (nodeBufferIndex == SentinelHash) {
|
||||
// This node is unused, so we don't have this element. Lets add it
|
||||
hashBuffer[hashIndex] = nodeBuffer.count();
|
||||
++hashBufferUseCount;
|
||||
nodeBuffer.push_back(v);
|
||||
return { &nodeBuffer.back(), true };
|
||||
}
|
||||
|
||||
// If that hash is in use, then check if that node is actually the one we are trying to insert
|
||||
if (IsEqual::equal(nodeBuffer[nodeBufferIndex].first, v.first)) {
|
||||
// Keys match. We already have this element
|
||||
return { &nodeBuffer[nodeBufferIndex], false };
|
||||
}
|
||||
|
||||
// We didn't find this node, so try with a later one
|
||||
hashIndex += probeAmount;
|
||||
hashIndex &= (hashBuffer.count() - 1);
|
||||
++probeAmount;
|
||||
}
|
||||
|
||||
assert(0 && "unreachable");
|
||||
}
|
||||
|
||||
|
||||
ValueT& operator[](KeyT idx) {
|
||||
auto itAndInserted = insert({ idx, ValueT() });
|
||||
return itAndInserted.first->second;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t nextHashBufferGrowth;
|
||||
size_t hashBufferUseCount;
|
||||
dyld3::OverflowSafeArray<size_t> hashBuffer;
|
||||
dyld3::OverflowSafeArray<NodeT> nodeBuffer;
|
||||
};
|
||||
|
||||
template<typename KeyT, typename ValueT, class GetHash = Hash<KeyT>, class IsEqual = Equal<KeyT>>
|
||||
class MultiMap {
|
||||
|
||||
struct NextNode {
|
||||
size_t isDuplicateHead : 1;
|
||||
size_t isDuplicateEntry : 1;
|
||||
size_t isDuplicateTail : 1;
|
||||
size_t nextIndex : 29;
|
||||
|
||||
bool hasAnyDuplicates() const {
|
||||
return isDuplicateHead || isDuplicateEntry || isDuplicateTail;
|
||||
}
|
||||
|
||||
bool hasMoreDuplicates() const {
|
||||
return isDuplicateHead || isDuplicateEntry;
|
||||
}
|
||||
|
||||
static NextNode makeNoDuplicates() {
|
||||
return { 0, 0, 0, 0 };
|
||||
}
|
||||
|
||||
static NextNode makeDuplicateTailNode() {
|
||||
return { 0, 0, 1, 0 };
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(NextNode) == sizeof(size_t), "Invalid size");
|
||||
|
||||
typedef std::pair<KeyT, ValueT> NodeT;
|
||||
typedef std::tuple<KeyT, ValueT, NextNode> NodeEntryT;
|
||||
typedef NodeT* iterator;
|
||||
typedef const NodeT* const_iterator;
|
||||
|
||||
enum : size_t {
|
||||
SentinelHash = (size_t)-1
|
||||
};
|
||||
|
||||
public:
|
||||
MultiMap() {
|
||||
// Keep the hash buffer about 75% full
|
||||
nextHashBufferGrowth = 768;
|
||||
hashBufferUseCount = 0;
|
||||
hashBuffer.reserve(1024);
|
||||
for (size_t i = 0; i != 1024; ++i) {
|
||||
hashBuffer.push_back(SentinelHash);
|
||||
}
|
||||
nodeBuffer.reserve(1024);
|
||||
}
|
||||
|
||||
void forEachEntry(void (^handler)(const KeyT& key, const ValueT** values, uint64_t valuesCount)) const {
|
||||
// Walk the top level nodes, skipping dupes
|
||||
for (const NodeEntryT& headNode : nodeBuffer) {
|
||||
NextNode nextNode = std::get<2>(headNode);
|
||||
if (!nextNode.hasAnyDuplicates()) {
|
||||
const ValueT* value[1] = { &std::get<1>(headNode) };
|
||||
handler(std::get<0>(headNode), value, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!nextNode.isDuplicateHead)
|
||||
continue;
|
||||
|
||||
// This is the head of a list. Work out how long the list is
|
||||
uint64_t valuesCount = 1;
|
||||
while (std::get<2>(nodeBuffer[nextNode.nextIndex]).hasMoreDuplicates()) {
|
||||
nextNode = std::get<2>(nodeBuffer[nextNode.nextIndex]);
|
||||
++valuesCount;
|
||||
}
|
||||
|
||||
// Add one more for the last node
|
||||
++valuesCount;
|
||||
|
||||
// Now make an array with that many value for the callback
|
||||
const ValueT* values[valuesCount];
|
||||
// Copy in the head
|
||||
values[0] = &std::get<1>(headNode);
|
||||
|
||||
// And copy the remainder
|
||||
nextNode = std::get<2>(headNode);
|
||||
valuesCount = 1;
|
||||
while (std::get<2>(nodeBuffer[nextNode.nextIndex]).hasMoreDuplicates()) {
|
||||
values[valuesCount] = &std::get<1>(nodeBuffer[nextNode.nextIndex]);
|
||||
nextNode = std::get<2>(nodeBuffer[nextNode.nextIndex]);
|
||||
++valuesCount;
|
||||
}
|
||||
|
||||
// Add in the last node
|
||||
values[valuesCount] = &std::get<1>(nodeBuffer[nextNode.nextIndex]);
|
||||
++valuesCount;
|
||||
|
||||
// Finally call the handler with a whole array of values.
|
||||
handler(std::get<0>(headNode), values, valuesCount);
|
||||
}
|
||||
}
|
||||
|
||||
void insert(NodeT&& v) {
|
||||
// First see if we have enough space. We don't want the hash buffer to get too full.
|
||||
if (hashBufferUseCount == nextHashBufferGrowth) {
|
||||
// Grow and rehash everything.
|
||||
size_t newHashTableSize = hashBuffer.count() * 2;
|
||||
nextHashBufferGrowth *= 2;
|
||||
|
||||
dyld3::OverflowSafeArray<size_t> newHashBuffer;
|
||||
newHashBuffer.reserve(newHashTableSize);
|
||||
for (size_t i = 0; i != newHashTableSize; ++i) {
|
||||
newHashBuffer.push_back(SentinelHash);
|
||||
}
|
||||
|
||||
// Walk the existing nodes trying to populate the new hash buffer and looking for collisions
|
||||
for (size_t i = 0; i != nodeBuffer.count(); ++i) {
|
||||
// Skip nodes which are not the head of the list
|
||||
// They aren't moving the buffer anyway
|
||||
NextNode nextNode = std::get<2>(nodeBuffer[i]);
|
||||
if (nextNode.isDuplicateEntry || nextNode.isDuplicateTail)
|
||||
continue;
|
||||
const KeyT& key = std::get<0>(nodeBuffer[i]);
|
||||
size_t newHashIndex = GetHash::hash(key) & (newHashBuffer.count() - 1);
|
||||
|
||||
// Note we'll use a quadratic probe to look past identical hashes until we find our node or a sentinel
|
||||
size_t probeAmount = 1;
|
||||
while (true) {
|
||||
size_t newNodeBufferIndex = newHashBuffer[newHashIndex];
|
||||
|
||||
if (newNodeBufferIndex == SentinelHash) {
|
||||
// This node is unused, so we don't have this element. Lets add it
|
||||
newHashBuffer[newHashIndex] = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't bother checking for matching keys here. We know we are adding elements with different keys
|
||||
// Just probe to find the next sentinel
|
||||
|
||||
// We didn't find this node, so try with a later one
|
||||
newHashIndex += probeAmount;
|
||||
newHashIndex &= (newHashBuffer.count() - 1);
|
||||
++probeAmount;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the new buffer
|
||||
hashBuffer = std::move(newHashBuffer);
|
||||
}
|
||||
|
||||
// Find the index to look up in the hash buffer
|
||||
size_t hashIndex = GetHash::hash(v.first) & (hashBuffer.count() - 1);
|
||||
|
||||
// Note we'll use a quadratic probe to look past identical hashes until we find our node or a sentinel
|
||||
size_t probeAmount = 1;
|
||||
while (true) {
|
||||
size_t nodeBufferIndex = hashBuffer[hashIndex];
|
||||
|
||||
if (nodeBufferIndex == SentinelHash) {
|
||||
// This node is unused, so we don't have this element. Lets add it
|
||||
hashBuffer[hashIndex] = nodeBuffer.count();
|
||||
++hashBufferUseCount;
|
||||
nodeBuffer.push_back({ v.first, v.second, NextNode::makeNoDuplicates() } );
|
||||
return;
|
||||
}
|
||||
|
||||
// If that hash is in use, then check if that node is actually the one we are trying to insert
|
||||
if (IsEqual::equal(std::get<0>(nodeBuffer[nodeBufferIndex]), v.first)) {
|
||||
// Keys match. We already have this element
|
||||
// But this is a multimap so add the new element too
|
||||
// Walk from this node to find the end of the chain
|
||||
while (std::get<2>(nodeBuffer[nodeBufferIndex]).hasMoreDuplicates()) {
|
||||
nodeBufferIndex = std::get<2>(nodeBuffer[nodeBufferIndex]).nextIndex;
|
||||
}
|
||||
NextNode& tailNode = std::get<2>(nodeBuffer[nodeBufferIndex]);
|
||||
if (!tailNode.hasAnyDuplicates()) {
|
||||
// If the previous node has no duplicates then its now the new head of a list
|
||||
tailNode.isDuplicateHead = 1;
|
||||
tailNode.nextIndex = nodeBuffer.count();
|
||||
} else {
|
||||
// This must be a tail node. Update it to be an entry node
|
||||
assert(tailNode.isDuplicateTail);
|
||||
tailNode.isDuplicateTail = 0;
|
||||
tailNode.isDuplicateEntry = 1;
|
||||
tailNode.nextIndex = nodeBuffer.count();
|
||||
}
|
||||
//.nextIndex = nodeBuffer.count();
|
||||
nodeBuffer.push_back({ v.first, v.second, NextNode::makeDuplicateTailNode() } );
|
||||
return;
|
||||
}
|
||||
|
||||
// We didn't find this node, so try with a later one
|
||||
hashIndex += probeAmount;
|
||||
hashIndex &= (hashBuffer.count() - 1);
|
||||
++probeAmount;
|
||||
}
|
||||
|
||||
assert(0 && "unreachable");
|
||||
}
|
||||
|
||||
private:
|
||||
size_t nextHashBufferGrowth;
|
||||
size_t hashBufferUseCount;
|
||||
dyld3::OverflowSafeArray<size_t> hashBuffer;
|
||||
dyld3::OverflowSafeArray<NodeEntryT> nodeBuffer;
|
||||
};
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif /* Map_h */
|
555
dyld3/PathOverrides.cpp
Normal file
555
dyld3/PathOverrides.cpp
Normal file
@ -0,0 +1,555 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <mach/mach.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <sys/errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
#include "PathOverrides.h"
|
||||
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
void PathOverrides::setFallbackPathHandling(FallbackPathMode mode)
|
||||
{
|
||||
_fallbackPathMode = mode;
|
||||
}
|
||||
|
||||
void PathOverrides::setEnvVars(const char* envp[], const MachOFile* mainExe, const char* mainExePath)
|
||||
{
|
||||
for (const char** p = envp; *p != NULL; p++) {
|
||||
addEnvVar(*p, false);
|
||||
}
|
||||
if ( mainExe != nullptr )
|
||||
setMainExecutable(mainExe, mainExePath);
|
||||
}
|
||||
|
||||
void PathOverrides::setMainExecutable(const dyld3::MachOFile* mainExe, const char* mainExePath)
|
||||
{
|
||||
assert(mainExe != nullptr);
|
||||
assert(mainExe->isMainExecutable());
|
||||
// process any LC_DYLD_ENVIRONMENT load commands in main executable
|
||||
mainExe->forDyldEnv(^(const char* envVar, bool& stop) {
|
||||
addEnvVar(envVar, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#if !BUILDING_LIBDYLD
|
||||
// libdyld is never unloaded
|
||||
PathOverrides::~PathOverrides()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void PathOverrides::forEachInsertedDylib(void (^handler)(const char* dylibPath, bool &stop)) const
|
||||
{
|
||||
if ( _insertedDylibs != nullptr ) {
|
||||
forEachInColonList(_insertedDylibs, nullptr, ^(const char* path, bool &stop) {
|
||||
handler(path, stop);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Note, this method only returns variables set on the environment, not those from the load command
|
||||
void PathOverrides::forEachEnvVar(void (^handler)(const char* envVar)) const
|
||||
{
|
||||
handleEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverridesEnv, handler);
|
||||
handleEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverridesEnv, handler);
|
||||
handleEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacksEnv, handler);
|
||||
handleEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacksEnv, handler);
|
||||
handleEnvVar("DYLD_INSERT_LIBRARIES", _insertedDylibs, handler);
|
||||
handleEnvVar("DYLD_IMAGE_SUFFIX", _imageSuffix, handler);
|
||||
handleEnvVar("DYLD_ROOT_PATH", _rootPath, handler);
|
||||
}
|
||||
|
||||
// Note, this method only returns variables set on the executable load command, not those from the environment
|
||||
void PathOverrides::forEachExecutableEnvVar(void (^handler)(const char* envVar)) const
|
||||
{
|
||||
handleEnvVar("DYLD_LIBRARY_PATH", _dylibPathOverridesExeLC, handler);
|
||||
handleEnvVar("DYLD_FRAMEWORK_PATH", _frameworkPathOverridesExeLC, handler);
|
||||
handleEnvVar("DYLD_FALLBACK_FRAMEWORK_PATH", _frameworkPathFallbacksExeLC, handler);
|
||||
handleEnvVar("DYLD_FALLBACK_LIBRARY_PATH", _dylibPathFallbacksExeLC, handler);
|
||||
}
|
||||
|
||||
const char* PathOverrides::addString(const char* str)
|
||||
{
|
||||
if ( _pathPool == nullptr )
|
||||
_pathPool = PathPool::allocate();
|
||||
return _pathPool->add(str);
|
||||
}
|
||||
|
||||
void PathOverrides::setString(const char*& var, const char* value)
|
||||
{
|
||||
if ( var == nullptr ) {
|
||||
var = addString(value);
|
||||
return;
|
||||
}
|
||||
// string already in use, build new appended string
|
||||
char tmp[strlen(var)+strlen(value)+2];
|
||||
strcpy(tmp, var);
|
||||
strcat(tmp, ":");
|
||||
strcat(tmp, value);
|
||||
var = addString(tmp);
|
||||
}
|
||||
|
||||
void PathOverrides::addEnvVar(const char* keyEqualsValue, bool forExecutable)
|
||||
{
|
||||
// We have to make a copy of the env vars because the dyld
|
||||
// semantics is that the env vars are only looked at once
|
||||
// at launch (using setenv() at runtime does not change dyld behavior).
|
||||
const char* equals = strchr(keyEqualsValue, '=');
|
||||
if ( equals != NULL ) {
|
||||
if ( strncmp(keyEqualsValue, "DYLD_LIBRARY_PATH", 17) == 0 ) {
|
||||
setString(forExecutable ? _dylibPathOverridesExeLC : _dylibPathOverridesEnv, &keyEqualsValue[18]);
|
||||
}
|
||||
else if ( strncmp(keyEqualsValue, "DYLD_FRAMEWORK_PATH", 19) == 0 ) {
|
||||
setString(forExecutable ? _frameworkPathOverridesExeLC : _frameworkPathOverridesEnv, &keyEqualsValue[20]);
|
||||
}
|
||||
else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_FRAMEWORK_PATH", 28) == 0 ) {
|
||||
setString(forExecutable ? _frameworkPathFallbacksExeLC : _frameworkPathFallbacksEnv, &keyEqualsValue[29]);
|
||||
}
|
||||
else if ( strncmp(keyEqualsValue, "DYLD_FALLBACK_LIBRARY_PATH", 26) == 0 ) {
|
||||
setString(forExecutable ? _dylibPathFallbacksExeLC : _dylibPathFallbacksEnv, &keyEqualsValue[27]);
|
||||
}
|
||||
else if ( strncmp(keyEqualsValue, "DYLD_INSERT_LIBRARIES", 21) == 0 ) {
|
||||
setString(_insertedDylibs, &keyEqualsValue[22]);
|
||||
}
|
||||
else if ( strncmp(keyEqualsValue, "DYLD_IMAGE_SUFFIX", 17) == 0 ) {
|
||||
setString(_imageSuffix, &keyEqualsValue[18]);
|
||||
}
|
||||
else if ( strncmp(keyEqualsValue, "DYLD_ROOT_PATH", 14) == 0 ) {
|
||||
setString(_rootPath, &keyEqualsValue[15]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PathOverrides::forEachInColonList(const char* list1, const char* list2, void (^handler)(const char* path, bool& stop))
|
||||
{
|
||||
for (const char* list : { list1, list2 }) {
|
||||
if (list == nullptr)
|
||||
continue;
|
||||
char buffer[strlen(list)+1];
|
||||
const char* t = list;
|
||||
bool stop = false;
|
||||
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, stop);
|
||||
if ( stop )
|
||||
return;
|
||||
t = s+1;
|
||||
}
|
||||
handler(t, stop);
|
||||
if (stop)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PathOverrides::forEachDylibFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
|
||||
{
|
||||
__block bool stop = false;
|
||||
if ( (_dylibPathFallbacksEnv != nullptr) || (_dylibPathFallbacksExeLC != nullptr) ) {
|
||||
forEachInColonList(_dylibPathFallbacksEnv, _dylibPathFallbacksExeLC, ^(const char* pth, bool& innerStop) {
|
||||
handler(pth, innerStop);
|
||||
if ( innerStop )
|
||||
stop = true;
|
||||
});
|
||||
}
|
||||
else {
|
||||
switch ( platform ) {
|
||||
case Platform::macOS:
|
||||
switch ( _fallbackPathMode ) {
|
||||
case FallbackPathMode::classic:
|
||||
// "$HOME/lib"
|
||||
handler("/usr/local/lib", stop);
|
||||
if ( stop )
|
||||
break;
|
||||
[[clang::fallthrough]];
|
||||
case FallbackPathMode::restricted:
|
||||
handler("/usr/lib", stop);
|
||||
break;
|
||||
case FallbackPathMode::none:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Platform::iOS:
|
||||
case Platform::watchOS:
|
||||
case Platform::tvOS:
|
||||
case Platform::bridgeOS:
|
||||
case Platform::driverKit:
|
||||
case Platform::unknown:
|
||||
if ( _fallbackPathMode != FallbackPathMode::none ) {
|
||||
handler("/usr/local/lib", stop);
|
||||
if ( stop )
|
||||
break;
|
||||
}
|
||||
// fall into /usr/lib case
|
||||
[[clang::fallthrough]];
|
||||
case Platform::iOSMac:
|
||||
case Platform::iOS_simulator:
|
||||
case Platform::watchOS_simulator:
|
||||
case Platform::tvOS_simulator:
|
||||
if ( _fallbackPathMode != FallbackPathMode::none )
|
||||
handler("/usr/lib", stop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PathOverrides::forEachFrameworkFallback(Platform platform, void (^handler)(const char* fallbackDir, bool& stop)) const
|
||||
{
|
||||
__block bool stop = false;
|
||||
if ( (_frameworkPathFallbacksEnv != nullptr) || (_frameworkPathFallbacksExeLC != nullptr) ) {
|
||||
forEachInColonList(_frameworkPathFallbacksEnv, _frameworkPathFallbacksExeLC, ^(const char* pth, bool& innerStop) {
|
||||
handler(pth, innerStop);
|
||||
if ( innerStop )
|
||||
stop = true;
|
||||
});
|
||||
}
|
||||
else {
|
||||
switch ( platform ) {
|
||||
case Platform::macOS:
|
||||
switch ( _fallbackPathMode ) {
|
||||
case FallbackPathMode::classic:
|
||||
// "$HOME/Library/Frameworks"
|
||||
handler("/Library/Frameworks", stop);
|
||||
if ( stop )
|
||||
break;
|
||||
// "/Network/Library/Frameworks"
|
||||
// fall thru
|
||||
[[clang::fallthrough]];
|
||||
case FallbackPathMode::restricted:
|
||||
handler("/System/Library/Frameworks", stop);
|
||||
break;
|
||||
case FallbackPathMode::none:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Platform::iOS:
|
||||
case Platform::watchOS:
|
||||
case Platform::tvOS:
|
||||
case Platform::bridgeOS:
|
||||
case Platform::iOSMac:
|
||||
case Platform::iOS_simulator:
|
||||
case Platform::watchOS_simulator:
|
||||
case Platform::tvOS_simulator:
|
||||
case Platform::driverKit:
|
||||
case Platform::unknown:
|
||||
if ( _fallbackPathMode != FallbackPathMode::none )
|
||||
handler("/System/Library/Frameworks", stop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 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 PathOverrides::addSuffix(const char* path, const char* suffix, char* result) const
|
||||
{
|
||||
strcpy(result, path);
|
||||
|
||||
// find last slash
|
||||
char* start = strrchr(result, '/');
|
||||
if ( start != NULL )
|
||||
start++;
|
||||
else
|
||||
start = result;
|
||||
|
||||
// find last dot after last slash
|
||||
char* dot = strrchr(start, '.');
|
||||
if ( dot != NULL ) {
|
||||
strcpy(dot, suffix);
|
||||
strcat(&dot[strlen(suffix)], &path[dot-result]);
|
||||
}
|
||||
else {
|
||||
strcat(result, suffix);
|
||||
}
|
||||
}
|
||||
|
||||
void PathOverrides::forEachImageSuffix(const char* path, bool isFallbackPath, bool pathIsInDyldCacheWhichCannotBeOverridden, bool& stop, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop)) const
|
||||
{
|
||||
if ( (_imageSuffix == nullptr) || pathIsInDyldCacheWhichCannotBeOverridden ) {
|
||||
handler(path, isFallbackPath, stop);
|
||||
}
|
||||
else {
|
||||
forEachInColonList(_imageSuffix, nullptr, ^(const char* suffix, bool& innerStop) {
|
||||
char npath[strlen(path)+strlen(suffix)+8];
|
||||
addSuffix(path, suffix, npath);
|
||||
handler(npath, isFallbackPath, innerStop);
|
||||
if ( innerStop )
|
||||
stop = true;
|
||||
});
|
||||
if ( !stop )
|
||||
handler(path, isFallbackPath, stop);
|
||||
}
|
||||
}
|
||||
|
||||
void PathOverrides::forEachPathVariant(const char* initialPath, bool pathIsInDyldCacheWhichCannotBeOverridden, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop), Platform platform) const
|
||||
{
|
||||
__block bool stop = false;
|
||||
|
||||
|
||||
if ( !pathIsInDyldCacheWhichCannotBeOverridden ) {
|
||||
// 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 ( (_frameworkPathOverridesEnv != nullptr) || (_frameworkPathOverridesExeLC != nullptr) ) {
|
||||
forEachInColonList(_frameworkPathOverridesEnv, _frameworkPathOverridesExeLC, ^(const char* frDir, bool &innerStop) {
|
||||
char npath[strlen(frDir)+frameworkPartialPathLen+8];
|
||||
strcpy(npath, frDir);
|
||||
strcat(npath, "/");
|
||||
strcat(npath, frameworkPartialPath);
|
||||
forEachImageSuffix(npath, false, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
|
||||
if ( innerStop )
|
||||
stop = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char* libraryLeafName = getLibraryLeafName(initialPath);
|
||||
const size_t libraryLeafNameLen = strlen(libraryLeafName);
|
||||
// look at each DYLD_LIBRARY_PATH directory
|
||||
if ( (_dylibPathOverridesEnv != nullptr) || (_dylibPathOverridesExeLC != nullptr) ) {
|
||||
forEachInColonList(_dylibPathOverridesEnv, _dylibPathOverridesExeLC, ^(const char* libDir, bool &innerStop) {
|
||||
char npath[strlen(libDir)+libraryLeafNameLen+8];
|
||||
strcpy(npath, libDir);
|
||||
strcat(npath, "/");
|
||||
strcat(npath, libraryLeafName);
|
||||
forEachImageSuffix(npath, false, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
|
||||
if ( innerStop )
|
||||
stop = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
if ( stop )
|
||||
return;
|
||||
}
|
||||
|
||||
// try original path
|
||||
forEachImageSuffix(initialPath, false, pathIsInDyldCacheWhichCannotBeOverridden, stop, handler);
|
||||
if ( stop )
|
||||
return;
|
||||
|
||||
// check fallback paths
|
||||
if ( const char* frameworkPartialPath = getFrameworkPartialPath(initialPath) ) {
|
||||
const size_t frameworkPartialPathLen = strlen(frameworkPartialPath);
|
||||
// look at each DYLD_FALLBACK_FRAMEWORK_PATH directory
|
||||
bool usesDefaultFallbackPaths = (_frameworkPathFallbacksEnv == nullptr) && (_frameworkPathFallbacksExeLC == nullptr);
|
||||
forEachFrameworkFallback(platform, ^(const char* dir, bool& innerStop) {
|
||||
char npath[strlen(dir)+frameworkPartialPathLen+8];
|
||||
strcpy(npath, dir);
|
||||
strcat(npath, "/");
|
||||
strcat(npath, frameworkPartialPath);
|
||||
forEachImageSuffix(npath, usesDefaultFallbackPaths, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
|
||||
if ( innerStop )
|
||||
stop = true;
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
const char* libraryLeafName = getLibraryLeafName(initialPath);
|
||||
const size_t libraryLeafNameLen = strlen(libraryLeafName);
|
||||
// look at each DYLD_FALLBACK_LIBRARY_PATH directory
|
||||
bool usesDefaultFallbackPaths = (_dylibPathFallbacksEnv == nullptr) && (_dylibPathFallbacksExeLC == nullptr);
|
||||
forEachDylibFallback(platform, ^(const char* dir, bool& innerStop) {
|
||||
char libpath[strlen(dir)+libraryLeafNameLen+8];
|
||||
strcpy(libpath, dir);
|
||||
strcat(libpath, "/");
|
||||
strcat(libpath, libraryLeafName);
|
||||
forEachImageSuffix(libpath, usesDefaultFallbackPaths, pathIsInDyldCacheWhichCannotBeOverridden, innerStop, handler);
|
||||
if ( innerStop )
|
||||
stop = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////// PathPool ////////////////////////////////////////
|
||||
|
||||
|
||||
PathPool* PathPool::allocate()
|
||||
{
|
||||
vm_address_t addr;
|
||||
::vm_allocate(mach_task_self(), &addr, kAllocationSize, VM_FLAGS_ANYWHERE);
|
||||
PathPool* p = (PathPool*)addr;
|
||||
p->_next = nullptr;
|
||||
p->_current = &(p->_buffer[0]);
|
||||
p->_bytesFree = kAllocationSize - sizeof(PathPool);
|
||||
return p;
|
||||
}
|
||||
|
||||
void PathPool::deallocate(PathPool* pool) {
|
||||
do {
|
||||
PathPool* next = pool->_next;
|
||||
::vm_deallocate(mach_task_self(), (vm_address_t)pool, kAllocationSize);
|
||||
pool = next;
|
||||
} while (pool);
|
||||
}
|
||||
|
||||
const char* PathPool::add(const char* path)
|
||||
{
|
||||
size_t len = strlen(path) + 1;
|
||||
if ( len < _bytesFree ) {
|
||||
char* result = _current;
|
||||
strcpy(_current, path);
|
||||
_current += len;
|
||||
_bytesFree -= len;
|
||||
return result;
|
||||
}
|
||||
if ( _next == nullptr )
|
||||
_next = allocate();
|
||||
return _next->add(path);
|
||||
}
|
||||
|
||||
void PathPool::forEachPath(void (^handler)(const char* path))
|
||||
{
|
||||
for (const char* s = _buffer; s < _current; ++s) {
|
||||
handler(s);
|
||||
s += strlen(s);
|
||||
}
|
||||
|
||||
if ( _next != nullptr )
|
||||
_next->forEachPath(handler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
|
||||
|
||||
|
115
dyld3/PathOverrides.h
Normal file
115
dyld3/PathOverrides.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#include "Logging.h"
|
||||
#include "MachOFile.h"
|
||||
|
||||
|
||||
namespace dyld3 {
|
||||
namespace closure {
|
||||
|
||||
|
||||
class VIS_HIDDEN PathPool
|
||||
{
|
||||
public:
|
||||
static PathPool* allocate();
|
||||
static void deallocate(PathPool* pool);
|
||||
const char* add(const char* path);
|
||||
void forEachPath(void (^handler)(const char* path));
|
||||
|
||||
private:
|
||||
enum { kAllocationSize = 32*1024 };
|
||||
|
||||
PathPool* _next;
|
||||
char* _current;
|
||||
size_t _bytesFree;
|
||||
char _buffer[];
|
||||
};
|
||||
|
||||
|
||||
class VIS_HIDDEN PathOverrides
|
||||
{
|
||||
public:
|
||||
#if !BUILDING_LIBDYLD
|
||||
// libdyld is never unloaded
|
||||
~PathOverrides();
|
||||
#endif
|
||||
enum class FallbackPathMode { classic, restricted, none };
|
||||
|
||||
void setFallbackPathHandling(FallbackPathMode mode);
|
||||
void setEnvVars(const char* envp[], const dyld3::MachOFile* mainExe, const char* mainExePath);
|
||||
void setMainExecutable(const dyld3::MachOFile* mainExe, const char* mainExePath);
|
||||
void forEachPathVariant(const char* requestedPath, bool pathIsInDyldCacheWhichCannotBeOverridden, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop),
|
||||
Platform plat=MachOFile::currentPlatform()) const;
|
||||
|
||||
void forEachEnvVar(void (^handler)(const char* envVar)) const;
|
||||
void forEachExecutableEnvVar(void (^handler)(const char* envVar)) const;
|
||||
void forEachInsertedDylib(void (^handler)(const char* dylibPath, bool &stop)) const;
|
||||
|
||||
private:
|
||||
void setString(const char*& var, const char* value);
|
||||
const char* addString(const char* str);
|
||||
static void forEachInColonList(const char* list1, const char* list2, void (^callback)(const char* path, bool& stop));
|
||||
void addEnvVar(const char* keyEqualsValue, bool forExecutable);
|
||||
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;
|
||||
void forEachImageSuffix(const char* path, bool isFallbackPath, bool pathIsInDyldCacheWhichCannotBeOverridden, bool& stop, void (^handler)(const char* possiblePath, bool isFallbackPath, bool& stop)) const;
|
||||
void addSuffix(const char* path, const char* suffix, char* result) const;
|
||||
|
||||
PathPool* _pathPool = nullptr;
|
||||
const char* _dylibPathOverridesEnv = nullptr;
|
||||
const char* _frameworkPathOverridesEnv = nullptr;
|
||||
const char* _dylibPathFallbacksEnv = nullptr;
|
||||
const char* _frameworkPathFallbacksEnv = nullptr;
|
||||
const char* _dylibPathOverridesExeLC = nullptr;
|
||||
const char* _frameworkPathOverridesExeLC = nullptr;
|
||||
const char* _dylibPathFallbacksExeLC = nullptr;
|
||||
const char* _frameworkPathFallbacksExeLC = nullptr;
|
||||
const char* _insertedDylibs = nullptr;
|
||||
const char* _imageSuffix = nullptr;
|
||||
const char* _rootPath = nullptr; // simulator only
|
||||
FallbackPathMode _fallbackPathMode = FallbackPathMode::classic;
|
||||
};
|
||||
|
||||
#if BUILDING_LIBDYLD
|
||||
extern PathOverrides gPathOverrides;
|
||||
#endif
|
||||
|
||||
|
||||
} // namespace closure
|
||||
} // namespace dyld3
|
||||
|
||||
#endif // __DYLD_PATH_OVERRIDES_H__
|
||||
|
||||
|
806
dyld3/SharedCacheRuntime.cpp
Normal file
806
dyld3/SharedCacheRuntime.cpp
Normal file
@ -0,0 +1,806 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/ldsyms.h>
|
||||
#include <mach/shared_region.h>
|
||||
#include <mach/mach.h>
|
||||
#include <Availability.h>
|
||||
#include <TargetConditionals.h>
|
||||
|
||||
#include "dyld_cache_format.h"
|
||||
#include "SharedCacheRuntime.h"
|
||||
#include "Loading.h"
|
||||
#include "BootArgs.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 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__
|
||||
#if __LP64__
|
||||
#define ARCH_NAME "arm64"
|
||||
#define ARCH_CACHE_MAGIC "dyld_v1 arm64"
|
||||
#else
|
||||
#define ARCH_NAME "arm64_32"
|
||||
#define ARCH_CACHE_MAGIC "dyld_v1arm64_32"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#if !TARGET_OS_SIMULATOR
|
||||
static void rebaseChainV2(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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !__LP64__ && !TARGET_OS_SIMULATOR
|
||||
static void rebaseChainV4(uint8_t* pageContent, uint16_t startOffset, uintptr_t slideAmount, const dyld_cache_slide_info4* 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 & 0xFFFF8000) == 0 ) {
|
||||
// small positive non-pointer, use as-is
|
||||
}
|
||||
else if ( (value & 0x3FFF8000) == 0x3FFF8000 ) {
|
||||
// small negative non-pointer
|
||||
value |= 0xC0000000;
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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_OS_SIMULATOR
|
||||
// use .development cache if it exists
|
||||
struct stat enableStatBuf;
|
||||
struct stat devCacheStatBuf;
|
||||
struct stat optCacheStatBuf;
|
||||
bool developmentDevice = dyld3::internalInstall();
|
||||
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 ( !BootArgs::forceCustomerCache() && developmentDevice && ((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)MachOFile::currentPlatform() )
|
||||
return false;
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
if ( cache->header.simulator == 0 )
|
||||
return false;
|
||||
#else
|
||||
if ( cache->header.simulator != 0 )
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !TARGET_OS_SIMULATOR
|
||||
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 " : ""));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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 open() failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat cacheStatBuf;
|
||||
if ( dyld::my_stat(results->path, &cacheStatBuf) != 0 ) {
|
||||
results->errorMessage = "shared cache file stat() failed";
|
||||
::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 file pread() failed";
|
||||
::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;
|
||||
}
|
||||
if ( (cache->header.mappingCount != 3) || (cache->header.mappingOffset > 0x138) ) {
|
||||
results->errorMessage = "shared cache file mappings are invalid";
|
||||
::close(fd);
|
||||
return false;
|
||||
}
|
||||
const dyld_cache_mapping_info* const fileMappings = (dyld_cache_mapping_info*)&firstPage[cache->header.mappingOffset];
|
||||
if ( (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;
|
||||
}
|
||||
|
||||
// <rdar://problem/23188073> 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 mmap()ed shared cache not valid";
|
||||
::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 >= 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_OS_SIMULATOR
|
||||
|
||||
// update all __DATA pages with slide info
|
||||
static bool rebaseDataPages(bool isVerbose, CacheInfo& info, SharedCacheLoadInfo* results)
|
||||
{
|
||||
uint64_t dataPagesStart = info.mappings[1].sfm_address;
|
||||
const dyld_cache_slide_info* slideInfo = nullptr;
|
||||
if ( info.slideInfoSize != 0 ) {
|
||||
slideInfo = (dyld_cache_slide_info*)(info.slideInfoAddressUnslid + results->slide);
|
||||
}
|
||||
const dyld_cache_slide_info* slideInfoHeader = (dyld_cache_slide_info*)slideInfo;
|
||||
if ( slideInfoHeader != nullptr ) {
|
||||
if ( slideInfoHeader->version == 2 ) {
|
||||
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);
|
||||
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);
|
||||
rebaseChainV2(page, pageStartOffset, results->slide, slideHeader);
|
||||
done = (pInfo & DYLD_CACHE_SLIDE_PAGE_ATTR_END);
|
||||
++chainIndex;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32_t pageOffset = pageEntry * 4;
|
||||
//dyld::log(" start pageOffset=0x%03X\n", pageOffset);
|
||||
rebaseChainV2(page, pageOffset, results->slide, slideHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if __LP64__
|
||||
else if ( slideInfoHeader->version == 3 ) {
|
||||
const dyld_cache_slide_info3* slideHeader = (dyld_cache_slide_info3*)slideInfo;
|
||||
const uint32_t pageSize = slideHeader->page_size;
|
||||
for (int i=0; i < slideHeader->page_starts_count; ++i) {
|
||||
uint8_t* page = (uint8_t*)(dataPagesStart + (pageSize*i));
|
||||
uint64_t delta = slideHeader->page_starts[i];
|
||||
if ( delta == DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE )
|
||||
continue;
|
||||
delta = delta/sizeof(uint64_t); // initial offset is byte based
|
||||
dyld_cache_slide_pointer3* loc = (dyld_cache_slide_pointer3*)page;
|
||||
do {
|
||||
loc += delta;
|
||||
delta = loc->plain.offsetToNextPointer;
|
||||
if ( loc->auth.authenticated ) {
|
||||
#if __has_feature(ptrauth_calls)
|
||||
uint64_t target = info.sharedRegionStart + loc->auth.offsetFromSharedCacheBase + results->slide;
|
||||
MachOLoaded::ChainedFixupPointerOnDisk ptr;
|
||||
ptr.raw64 = *((uint64_t*)loc);
|
||||
loc->raw = ptr.arm64e.signPointer(loc, target);
|
||||
#else
|
||||
results->errorMessage = "invalid pointer kind in cache file";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
MachOLoaded::ChainedFixupPointerOnDisk ptr;
|
||||
ptr.raw64 = *((uint64_t*)loc);
|
||||
loc->raw = ptr.arm64e.unpackTarget() + results->slide;
|
||||
}
|
||||
} while (delta != 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
else if ( slideInfoHeader->version == 4 ) {
|
||||
const dyld_cache_slide_info4* slideHeader = (dyld_cache_slide_info4*)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);
|
||||
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_SLIDE4_PAGE_NO_REBASE )
|
||||
continue;
|
||||
if ( pageEntry & DYLD_CACHE_SLIDE4_PAGE_USE_EXTRA ) {
|
||||
uint16_t chainIndex = (pageEntry & DYLD_CACHE_SLIDE4_PAGE_INDEX);
|
||||
bool done = false;
|
||||
while ( !done ) {
|
||||
uint16_t pInfo = page_extras[chainIndex];
|
||||
uint16_t pageStartOffset = (pInfo & DYLD_CACHE_SLIDE4_PAGE_INDEX)*4;
|
||||
//dyld::log(" chain[%d] pageOffset=0x%03X\n", chainIndex, pageStartOffset);
|
||||
rebaseChainV4(page, pageStartOffset, results->slide, slideHeader);
|
||||
done = (pInfo & DYLD_CACHE_SLIDE4_PAGE_EXTRA_END);
|
||||
++chainIndex;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32_t pageOffset = pageEntry * 4;
|
||||
//dyld::log(" start pageOffset=0x%03X\n", pageOffset);
|
||||
rebaseChainV4(page, pageOffset, results->slide, slideHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // LP64
|
||||
else {
|
||||
results->errorMessage = "invalid slide info in cache file";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
// 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
|
||||
// <rdar://problem/20848977> 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
|
||||
|
||||
// <rdar://problem/32031197> respect -disable_aslr boot-arg
|
||||
if ( BootArgs::contains("-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);
|
||||
}
|
||||
|
||||
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
|
||||
if ( results->errorMessage == nullptr )
|
||||
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 // TARGET_OS_SIMULATOR
|
||||
|
||||
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;
|
||||
#if !TARGET_OS_SIMULATOR // simulator caches do not support sliding
|
||||
if ( info.slideInfoSize != 0 ) {
|
||||
results->slide = pickCacheASLR(info);
|
||||
}
|
||||
#endif
|
||||
results->loadAddress = (const DyldSharedCache*)(info.mappings[0].sfm_address);
|
||||
|
||||
// deallocate any existing system wide shared cache
|
||||
deallocateExistingSharedCache();
|
||||
|
||||
#if TARGET_OS_SIMULATOR && TARGET_OS_WATCH
|
||||
// <rdar://problem/50887685> watchOS 32-bit cache does not overlap macOS dyld cache address range
|
||||
// mmap() of a file needs a vm_allocation behind it, so make one
|
||||
vm_address_t loadAddress = 0x40000000;
|
||||
::vm_allocate(mach_task_self(), &loadAddress, 0x40000000, VM_FLAGS_FIXED);
|
||||
#endif
|
||||
|
||||
// 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
|
||||
::mmap((void*)((long)SHARED_REGION_BASE), SHARED_REGION_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE| MAP_ANON, 0, 0);
|
||||
// return failure
|
||||
results->loadAddress = nullptr;
|
||||
results->errorMessage = "could not mmap() part of dyld cache";
|
||||
::close(info.fd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
::close(info.fd);
|
||||
|
||||
#if TARGET_OS_SIMULATOR // simulator caches do not support sliding
|
||||
return true;
|
||||
#else
|
||||
bool success = rebaseDataPages(options.verbose, info, results);
|
||||
|
||||
if ( options.verbose ) {
|
||||
dyld::log("mapped dyld cache file private to process (%s):\n", results->path);
|
||||
verboseSharedCacheMappings(info.mappings);
|
||||
}
|
||||
return success;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool loadDyldCache(const SharedCacheOptions& options, SharedCacheLoadInfo* results)
|
||||
{
|
||||
results->loadAddress = 0;
|
||||
results->slide = 0;
|
||||
results->errorMessage = nullptr;
|
||||
|
||||
#if TARGET_OS_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
|
||||
bool hasError = false;
|
||||
if ( reuseExistingCache(options, results) ) {
|
||||
hasError = (results->errorMessage != nullptr);
|
||||
} else {
|
||||
// slow path: this is first process to load cache
|
||||
hasError = mapCacheSystemWide(options, results);
|
||||
}
|
||||
return hasError;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results)
|
||||
{
|
||||
if ( loadInfo.loadAddress == nullptr )
|
||||
return false;
|
||||
|
||||
if ( loadInfo.loadAddress->header.formatVersion != dyld3::closure::kFormatVersion ) {
|
||||
// support for older cache with a different Image* format
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||
uint64_t hash = 0;
|
||||
for (const char* s=dylibPathToFind; *s != '\0'; ++s)
|
||||
hash += hash*4 + *s;
|
||||
#endif
|
||||
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) {
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED
|
||||
// on iOS, inode is used to hold hash of path
|
||||
if ( (p->modTime == 0) && (p->inode != hash) )
|
||||
continue;
|
||||
#endif
|
||||
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->image = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const dyld3::closure::ImageArray* images = loadInfo.loadAddress->cachedDylibsImageArray();
|
||||
results->image = nullptr;
|
||||
uint32_t imageIndex;
|
||||
if ( loadInfo.loadAddress->hasImagePath(dylibPathToFind, imageIndex) ) {
|
||||
results->image = images->imageForNum(imageIndex+1);
|
||||
}
|
||||
#if __MAC_OS_X_VERSION_MIN_REQUIRED
|
||||
else {
|
||||
// <rdar://problem/32740215> handle symlink to cached dylib
|
||||
if ( loadInfo.loadAddress->header.dylibsExpectedOnDisk ) {
|
||||
struct stat statBuf;
|
||||
if ( dyld::my_stat(dylibPathToFind, &statBuf) == 0 ) {
|
||||
// on macOS we store the inode and mtime of each dylib in the cache in the dyld_cache_image_info array
|
||||
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) {
|
||||
if ( (p->inode == statBuf.st_ino) && (p->modTime == statBuf.st_mtime) ) {
|
||||
imageIndex = (uint32_t)(p - start);
|
||||
results->image = images->imageForNum(imageIndex+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
char resolvedPath[PATH_MAX];
|
||||
if ( realpath(dylibPathToFind, resolvedPath) != nullptr ) {
|
||||
if ( loadInfo.loadAddress->hasImagePath(resolvedPath, imageIndex) ) {
|
||||
results->image = images->imageForNum(imageIndex+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ( results->image == nullptr )
|
||||
return false;
|
||||
|
||||
results->mhInCache = (const mach_header*)((uintptr_t)loadInfo.loadAddress + results->image->cacheOffset());
|
||||
results->pathInCache = results->image->path();
|
||||
results->slideInCache = loadInfo.slide;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind)
|
||||
{
|
||||
if ( (loadInfo.loadAddress == nullptr) )
|
||||
return false;
|
||||
|
||||
uint32_t imageIndex;
|
||||
return loadInfo.loadAddress->hasImagePath(dylibPathToFind, imageIndex);
|
||||
}
|
||||
|
||||
void deallocateExistingSharedCache()
|
||||
{
|
||||
#if TARGET_OS_SIMULATOR
|
||||
// dyld deallocated macOS shared cache before jumping into dyld_sim
|
||||
#else
|
||||
// <rdar://problem/5077374> remove the shared region sub-map
|
||||
uint64_t existingCacheAddress = 0;
|
||||
if ( __shared_region_check_np(&existingCacheAddress) == 0 ) {
|
||||
::mmap((void*)((long)SHARED_REGION_BASE), SHARED_REGION_SIZE, PROT_NONE, MAP_FIXED | MAP_PRIVATE| MAP_ANON, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
} // namespace dyld3
|
||||
|
70
dyld3/SharedCacheRuntime.h
Normal file
70
dyld3/SharedCacheRuntime.h
Normal file
@ -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 <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "DyldSharedCache.h"
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
struct SharedCacheOptions {
|
||||
const char* cacheDirOverride;
|
||||
bool forcePrivate;
|
||||
bool useHaswell;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
struct SharedCacheLoadInfo {
|
||||
const DyldSharedCache* loadAddress;
|
||||
long slide;
|
||||
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 closure::Image* image;
|
||||
};
|
||||
|
||||
bool findInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind, SharedCacheFindDylibResults* results);
|
||||
|
||||
bool pathIsInSharedCacheImage(const SharedCacheLoadInfo& loadInfo, const char* dylibPathToFind);
|
||||
|
||||
void deallocateExistingSharedCache();
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
#endif // __DYLD_SHARED_CACHE_RUNTIME_H__
|
||||
|
||||
|
45
dyld3/StartGlue.h
Normal file
45
dyld3/StartGlue.h
Normal file
@ -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@
|
||||
*/
|
||||
|
||||
#ifndef __START_GLUE_H__
|
||||
#define __START_GLUE_H__
|
||||
|
||||
// Implemented in start_glue.s
|
||||
// Declare 'start' as a character, so that we can index into it.
|
||||
// Avoid arithmetic on function pointers.
|
||||
extern "C" char start;
|
||||
|
||||
|
||||
// <rdar://problem/12792039> 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__
|
30
dyld3/SupportedArchs.h
Normal file
30
dyld3/SupportedArchs.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- 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 _DYLD_SUPPORTED_ARCHS_H_
|
||||
#define _DYLD_SUPPORTED_ARCHS_H_
|
||||
|
||||
|
||||
|
||||
#endif // _DYLD_SUPPORTED_ARCHS_H_
|
161
dyld3/Tracing.cpp
Normal file
161
dyld3/Tracing.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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 <atomic>
|
||||
|
||||
#include <assert.h>
|
||||
#include <mach/mach.h>
|
||||
#include <kern/kcdata.h>
|
||||
#include <mach-o/dyld_priv.h>
|
||||
#include "Loading.h"
|
||||
#include "Tracing.h"
|
||||
#ifdef DARLING
|
||||
#define kdebug_trace(...)
|
||||
#define kdebug_is_enabled(...) false
|
||||
#endif
|
||||
|
||||
// Workaround for header issues in rdar://49073930
|
||||
// #include <System/os/reason_private.h>
|
||||
extern "C" int
|
||||
os_fault_with_payload(uint32_t reason_namespace, uint64_t reason_code,
|
||||
void *payload, uint32_t payload_size, const char *reason_string,
|
||||
uint64_t reason_flags) __attribute__((cold));
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
VIS_HIDDEN
|
||||
void kdebug_trace_dyld_image(const uint32_t code,
|
||||
const char* imagePath,
|
||||
const uuid_t* uuid_bytes,
|
||||
const fsobj_id_t fsobjid,
|
||||
const fsid_t fsid,
|
||||
const mach_header* load_addr)
|
||||
{
|
||||
uint64_t id = kdebug_trace_string(code, 0, imagePath);
|
||||
#if __ARM_ARCH_7K__
|
||||
uint32_t *uuid = (uint32_t *)uuid_bytes;
|
||||
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, id, 0, 0);
|
||||
#else /* __ARM_ARCH_7K__ */
|
||||
uint64_t *uuid = (uint64_t *)uuid_bytes;
|
||||
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),
|
||||
id, 0, 0);
|
||||
#endif /* !__ARM_ARCH_7K__ */
|
||||
kdebug_trace_string(code, id, nullptr);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// We get distinct copies of this in libdyld and dyld. Eventually we can fix it,
|
||||
// for now we will just offset the values.
|
||||
|
||||
#if BUILDING_DYLD
|
||||
static std::atomic<uint64_t> trace_pair_id(0);
|
||||
#else
|
||||
static std::atomic<uint64_t> trace_pair_id(1LL<<63);
|
||||
#endif
|
||||
|
||||
VIS_HIDDEN
|
||||
bool kdebug_trace_dyld_enabled(uint32_t code) {
|
||||
return kdebug_is_enabled(code);
|
||||
}
|
||||
|
||||
VIS_HIDDEN
|
||||
void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4) {
|
||||
if (kdebug_is_enabled(code)) {
|
||||
data1.prepare(code);
|
||||
data2.prepare(code);
|
||||
data3.prepare(code);
|
||||
data4.prepare(code);
|
||||
kdebug_trace(code, data1.value(), data2.value(), data3.value(), data4.value());
|
||||
data4.destroy(code);
|
||||
data3.destroy(code);
|
||||
data2.destroy(code);
|
||||
data1.destroy(code);
|
||||
}
|
||||
}
|
||||
|
||||
VIS_HIDDEN
|
||||
uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3) {
|
||||
uint64_t result = 0;
|
||||
if (kdebug_is_enabled(code)) {
|
||||
result = ++trace_pair_id;
|
||||
data1.prepare(code);
|
||||
data2.prepare(code);
|
||||
data3.prepare(code);
|
||||
kdebug_trace(code | DBG_FUNC_START, result, data1.value(), data2.value(), data3.value());
|
||||
data3.destroy(code);
|
||||
data2.destroy(code);
|
||||
data1.destroy(code);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
VIS_HIDDEN
|
||||
void kdebug_trace_dyld_duration_end(uint64_t trace_id, uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3) {
|
||||
if (trace_id != 0 && kdebug_is_enabled(code)) {
|
||||
data1.prepare(code);
|
||||
data2.prepare(code);
|
||||
data3.prepare(code);
|
||||
kdebug_trace(code | DBG_FUNC_END, trace_id, data1.value(), data2.value(), data3.value());
|
||||
data3.destroy(code);
|
||||
data2.destroy(code);
|
||||
data1.destroy(code);
|
||||
}
|
||||
}
|
||||
|
||||
void ScopedTimer::startTimer() {
|
||||
current_trace_id = kdebug_trace_dyld_duration_start(code, data1, data2, data3);
|
||||
}
|
||||
|
||||
void ScopedTimer::endTimer() {
|
||||
kdebug_trace_dyld_duration_end(current_trace_id, code, data4, data5, data6);
|
||||
}
|
||||
|
||||
void syntheticBacktrace(const char *reason, bool enableExternally) {
|
||||
if (!enableExternally && !internalInstall()) { return; }
|
||||
|
||||
char payloadBuffer[EXIT_REASON_PAYLOAD_MAX_LEN];
|
||||
dyld_abort_payload* payload = (dyld_abort_payload*)payloadBuffer;
|
||||
payload->version = 1;
|
||||
payload->flags = 0;
|
||||
payload->targetDylibPathOffset = 0;
|
||||
payload->clientPathOffset = 0;
|
||||
payload->symbolOffset = 0;
|
||||
int payloadSize = sizeof(dyld_abort_payload);
|
||||
char truncMessage[EXIT_REASON_USER_DESC_MAX_LEN];
|
||||
strlcpy(truncMessage, reason, EXIT_REASON_USER_DESC_MAX_LEN);
|
||||
os_fault_with_payload(OS_REASON_DYLD, DYLD_EXIT_REASON_OTHER, payloadBuffer, payloadSize, truncMessage, 0);
|
||||
}
|
||||
|
||||
};
|
172
dyld3/Tracing.h
Normal file
172
dyld3/Tracing.h
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <System/sys/kdebug.h>
|
||||
#include <System/sys/reason.h>
|
||||
|
||||
#ifdef DARLING
|
||||
#define kdebug_trace_string(...) ((uint64_t)-1)
|
||||
#endif
|
||||
|
||||
|
||||
#define DBG_DYLD_INTERNAL_SUBCLASS (7)
|
||||
#define DBG_DYLD_API_SUBCLASS (8)
|
||||
#define DBG_DYLD_DEBUGGING_SUBCLASS (9)
|
||||
|
||||
#define DBG_DYLD_TIMING_STATIC_INITIALIZER (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 0))
|
||||
#define DBG_DYLD_TIMING_LAUNCH_EXECUTABLE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 1))
|
||||
#define DBG_DYLD_TIMING_MAP_IMAGE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 2))
|
||||
#define DBG_DYLD_TIMING_APPLY_FIXUPS (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 3))
|
||||
#define DBG_DYLD_TIMING_ATTACH_CODESIGNATURE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 4))
|
||||
#define DBG_DYLD_TIMING_BUILD_CLOSURE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 5))
|
||||
#define DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 6))
|
||||
#define DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 7))
|
||||
#define DBG_DYLD_TIMING_OBJC_INIT (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 8))
|
||||
#define DBG_DYLD_TIMING_OBJC_MAP (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 9))
|
||||
#define DBG_DYLD_TIMING_APPLY_INTERPOSING (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 10))
|
||||
#define DBG_DYLD_GDB_IMAGE_NOTIFIER (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 11))
|
||||
#define DBG_DYLD_REMOTE_IMAGE_NOTIFIER (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 12))
|
||||
#define DBG_DYLD_TIMING_BOOTSTRAP_START (KDBG_CODE(DBG_DYLD, DBG_DYLD_INTERNAL_SUBCLASS, 13))
|
||||
|
||||
#define DBG_DYLD_TIMING_DLOPEN (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 0))
|
||||
#define DBG_DYLD_TIMING_DLOPEN_PREFLIGHT (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 1))
|
||||
#define DBG_DYLD_TIMING_DLCLOSE (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 2))
|
||||
#define DBG_DYLD_TIMING_DLSYM (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 3))
|
||||
#define DBG_DYLD_TIMING_DLADDR (KDBG_CODE(DBG_DYLD, DBG_DYLD_API_SUBCLASS, 4))
|
||||
|
||||
#define DBG_DYLD_DEBUGGING_VM_REMAP (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 0))
|
||||
#define DBG_DYLD_DEBUGGING_VM_UNMAP (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 1))
|
||||
#define DBG_DYLD_DEBUGGING_MAP_LOOP (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 2))
|
||||
#define DBG_DYLD_DEBUGGING_MARK (KDBG_CODE(DBG_DYLD, DBG_DYLD_DEBUGGING_SUBCLASS, 3))
|
||||
|
||||
|
||||
#define VIS_HIDDEN __attribute__((visibility("hidden")))
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
enum class DyldTimingBuildClosure : uint64_t {
|
||||
ClosureBuildFailure = 0,
|
||||
LaunchClosure_Built = 1,
|
||||
DlopenClosure_UsedSharedCacheDylib = 2,
|
||||
DlopenClosure_UsedSharedCacheOther = 3,
|
||||
DlopenClosure_NoLoad = 4,
|
||||
DlopenClosure_Built = 5
|
||||
};
|
||||
|
||||
struct VIS_HIDDEN kt_arg {
|
||||
kt_arg(int value) : _value(value), _str(nullptr) {}
|
||||
kt_arg(uint64_t value) : _value(value), _str(nullptr) {}
|
||||
kt_arg(DyldTimingBuildClosure value) : _value((uint64_t)value), _str(nullptr) {}
|
||||
kt_arg(const char *value) : _value(0), _str(value) {}
|
||||
kt_arg(void *value) : _value((uint64_t)value), _str(nullptr) {}
|
||||
uint64_t value() const { return _value; }
|
||||
private:
|
||||
void prepare(uint32_t code) {
|
||||
if (_str) {
|
||||
_value = kdebug_trace_string(code, 0, _str);
|
||||
if (_value == (uint64_t)-1) _value = 0;
|
||||
}
|
||||
}
|
||||
void destroy(uint32_t code) {
|
||||
if (_str && _value) {
|
||||
kdebug_trace_string(code, _value, nullptr);
|
||||
}
|
||||
}
|
||||
friend class ScopedTimer;
|
||||
friend uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3);
|
||||
friend void kdebug_trace_dyld_duration_end(uint64_t pair_id, uint32_t code, kt_arg data4, kt_arg data5, kt_arg data6);
|
||||
friend void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4);
|
||||
uint64_t _value;
|
||||
const char* _str;
|
||||
};
|
||||
|
||||
class VIS_HIDDEN ScopedTimer {
|
||||
public:
|
||||
ScopedTimer(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3)
|
||||
: code(code), data1(data1), data2(data2), data3(data3), data4(0), data5(0), data6(0) {
|
||||
#if BUILDING_LIBDYLD || BUILDING_DYLD
|
||||
startTimer();
|
||||
#endif
|
||||
}
|
||||
|
||||
~ScopedTimer() {
|
||||
#if BUILDING_LIBDYLD || BUILDING_DYLD
|
||||
endTimer();
|
||||
#endif
|
||||
}
|
||||
|
||||
void setData4(kt_arg data) { data4 = data; }
|
||||
void setData5(kt_arg data) { data5 = data; }
|
||||
void setData6(kt_arg data) { data6 = data; }
|
||||
private:
|
||||
#if BUILDING_LIBDYLD || BUILDING_DYLD
|
||||
void startTimer();
|
||||
void endTimer();
|
||||
#endif
|
||||
|
||||
uint32_t code;
|
||||
kt_arg data1;
|
||||
kt_arg data2;
|
||||
kt_arg data3;
|
||||
kt_arg data4;
|
||||
kt_arg data5;
|
||||
kt_arg data6;
|
||||
uint64_t current_trace_id = 0;
|
||||
};
|
||||
|
||||
VIS_HIDDEN
|
||||
void kdebug_trace_dyld_image(const uint32_t code,
|
||||
const char* path,
|
||||
const uuid_t* uuid_bytes,
|
||||
const fsobj_id_t fsobjid,
|
||||
const fsid_t fsid,
|
||||
const mach_header* load_addr);
|
||||
|
||||
VIS_HIDDEN
|
||||
bool kdebug_trace_dyld_enabled(uint32_t code);
|
||||
|
||||
VIS_HIDDEN
|
||||
void kdebug_trace_dyld_marker(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3, kt_arg data4);
|
||||
|
||||
VIS_HIDDEN
|
||||
uint64_t kdebug_trace_dyld_duration_start(uint32_t code, kt_arg data1, kt_arg data2, kt_arg data3);
|
||||
|
||||
VIS_HIDDEN
|
||||
void kdebug_trace_dyld_duration_end(uint64_t trace_id, uint32_t code, kt_arg data4, kt_arg data5, kt_arg data6);
|
||||
|
||||
VIS_HIDDEN
|
||||
void syntheticBacktrace(const char *reason, bool enableExternally=false);
|
||||
|
||||
};
|
||||
#endif /* Tracing_h */
|
190
dyld3/libdyldEntryVector.cpp
Normal file
190
dyld3/libdyldEntryVector.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 <stdarg.h>
|
||||
#include <mach-o/dyld_priv.h>
|
||||
#include <mach-o/dyld_images.h>
|
||||
|
||||
#include "libdyldEntryVector.h"
|
||||
#include "AllImages.h"
|
||||
#include "Array.h"
|
||||
#include "Loading.h"
|
||||
#include "Logging.h"
|
||||
#include "PathOverrides.h"
|
||||
#include "StartGlue.h"
|
||||
#include "dyld_process_info_internal.h"
|
||||
|
||||
extern "C" char 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_setNotifyMonitoringDyldMain(void (*notifyMonitoringDyldMain)()) {
|
||||
#if !TARGET_OS_DRIVERKIT
|
||||
setNotifyMonitoringDyldMain(notifyMonitoringDyldMain);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void entry_setNotifyMonitoringDyld(void (*notifyMonitoringDyld)(bool unloading,unsigned imageCount,
|
||||
const struct mach_header* loadAddresses[],
|
||||
const char* imagePaths[])) {
|
||||
#if !TARGET_OS_DRIVERKIT
|
||||
setNotifyMonitoringDyld(notifyMonitoringDyld);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void entry_setInitialImageList(const closure::LaunchClosure* closure,
|
||||
const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
|
||||
const Array<LoadedImage>& initialImages, LoadedImage& libSystem)
|
||||
{
|
||||
gAllImages.init(closure, dyldCacheLoadAddress, dyldCachePath, initialImages);
|
||||
gAllImages.applyInterposingToDyldCache(closure);
|
||||
|
||||
// run initializer for libSytem.B.dylib
|
||||
// this calls back into _dyld_initializer which calls gAllIimages.addImages()
|
||||
gAllImages.runLibSystemInitializer(libSystem);
|
||||
|
||||
// now that malloc is available, parse DYLD_ env vars
|
||||
closure::gPathOverrides.setEnvVars((const char**)environ, gAllImages.mainExecutable(), gAllImages.mainExecutableImage()->path());
|
||||
}
|
||||
|
||||
static void entry_runInitialzersBottomUp(const mach_header* mainExecutableImageLoadAddress)
|
||||
{
|
||||
gAllImages.runStartupInitialzers();
|
||||
#if !TARGET_OS_DRIVERKIT
|
||||
gAllImages.notifyMonitorMain();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void entry_setChildForkFunction(void (*func)() )
|
||||
{
|
||||
sChildForkFunction = func;
|
||||
}
|
||||
|
||||
static void entry_setRestrictions(bool allowAtPaths, bool allowEnvPaths, bool allowFallbackPaths)
|
||||
{
|
||||
gAllImages.setRestrictions(allowAtPaths, allowEnvPaths);
|
||||
closure::gPathOverrides.setFallbackPathHandling(allowFallbackPaths ?
|
||||
dyld3::closure::PathOverrides::FallbackPathMode::classic :
|
||||
dyld3::closure::PathOverrides::FallbackPathMode::restricted);
|
||||
}
|
||||
|
||||
static void entry_setHasCacheOverrides(bool someCacheImageOverriden)
|
||||
{
|
||||
gAllImages.setHasCacheOverrides(someCacheImageOverriden);
|
||||
}
|
||||
|
||||
static_assert((closure::kFormatVersion & LibDyldEntryVector::kBinaryFormatVersionMask) == closure::kFormatVersion, "binary format version overflow");
|
||||
|
||||
const LibDyldEntryVector entryVectorForDyld = {
|
||||
LibDyldEntryVector::kCurrentVectorVersion,
|
||||
closure::kFormatVersion,
|
||||
&entry_setVars,
|
||||
&entry_setHaltFunction,
|
||||
&entry_setOldAllImageInfo,
|
||||
&entry_setInitialImageList,
|
||||
&entry_runInitialzersBottomUp,
|
||||
(__typeof(LibDyldEntryVector::startFunc))address_of_start,
|
||||
&entry_setChildForkFunction,
|
||||
&entry_setLogFunction,
|
||||
&entry_setRestrictions,
|
||||
&entry_setNotifyMonitoringDyldMain,
|
||||
&entry_setNotifyMonitoringDyld,
|
||||
&entry_setHasCacheOverrides
|
||||
};
|
||||
|
||||
VIS_HIDDEN void _dyld_atfork_prepare()
|
||||
{
|
||||
gAllImages.takeLockBeforeFork();
|
||||
}
|
||||
|
||||
VIS_HIDDEN void _dyld_atfork_parent()
|
||||
{
|
||||
gAllImages.releaseLockInForkParent();
|
||||
}
|
||||
|
||||
VIS_HIDDEN void _dyld_fork_child()
|
||||
{
|
||||
// Note the child fork function updates the data structures inside dyld
|
||||
(*sChildForkFunction)();
|
||||
|
||||
// And we then need to update the structures for dyld3 in libdyld
|
||||
gAllImages.resetLockInForkChild();
|
||||
}
|
||||
|
||||
|
||||
} // namespace dyld3
|
||||
|
82
dyld3/libdyldEntryVector.h
Normal file
82
dyld3/libdyldEntryVector.h
Normal file
@ -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_ENTRY_VECTOR_H__
|
||||
#define __DYLD_ENTRY_VECTOR_H__
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
#include <Availability.h>
|
||||
|
||||
#include "Loading.h"
|
||||
|
||||
struct dyld_all_image_infos;
|
||||
class DyldSharedCache;
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
|
||||
struct LibDyldEntryVector
|
||||
{
|
||||
enum { kCurrentVectorVersion = 7 };
|
||||
// The 32-bit caches steal bits to make rebase chains, so use 32-bits for the binary format version storage, but mask only some to actually use
|
||||
enum { kBinaryFormatVersionMask = 0x00FFFFFF };
|
||||
|
||||
uint32_t vectorVersion; // should be kCurrentVectorVersion
|
||||
uint32_t binaryFormatVersion; // should be dyld3::closure::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 closure::LaunchClosure* closure,
|
||||
const DyldSharedCache* dyldCacheLoadAddress, const char* dyldCachePath,
|
||||
const Array<LoadedImage>& initialImages, LoadedImage& libSystem);
|
||||
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));
|
||||
// added in version 5
|
||||
void (*setRestrictions)(bool allowAtPaths, bool allowEnvVars, bool allowFallbackPaths);
|
||||
// added in version 6
|
||||
void (*setNotifyMonitoringDyldMain)(void (*notifyMonitoringDyldMain)());
|
||||
void (*setNotifyMonitoringDyld)(void (*notifyMonitoringDyldMain)(bool unloading, unsigned imageCount,
|
||||
const struct mach_header* loadAddresses[],
|
||||
const char* imagePaths[]));
|
||||
// added in version 7
|
||||
void (*setHasCacheOverrides)(bool someCacheImageOverriden);
|
||||
};
|
||||
|
||||
extern const LibDyldEntryVector entryVectorForDyld;
|
||||
|
||||
extern int compatFuncLookup(const char* name, void** address) __API_AVAILABLE(ios(13.0));
|
||||
|
||||
} // namespace dyld3
|
||||
|
||||
|
||||
#endif // __DYLD_ENTRY_VECTOR_H__
|
||||
|
||||
|
||||
|
||||
|
1248
dyld3/shared-cache/AdjustDylibSegments.cpp
Normal file
1248
dyld3/shared-cache/AdjustDylibSegments.cpp
Normal file
File diff suppressed because it is too large
Load Diff
33
dyld3/shared-cache/BuilderUtils.h
Normal file
33
dyld3/shared-cache/BuilderUtils.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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,
|
||||
bool emitDevCaches, bool isLocallyBuiltCache);
|
||||
|
||||
#endif /* BuilderUtils_h */
|
317
dyld3/shared-cache/BuilderUtils.mm
Normal file
317
dyld3/shared-cache/BuilderUtils.mm
Normal file
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* 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 <set>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iomanip> // std::setfill, std::setw
|
||||
#include <pthread.h>
|
||||
#include <mach/mach.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
#include <Bom/Bom.h>
|
||||
#include <Security/Security.h>
|
||||
#include <Security/SecCodeSigner.h>
|
||||
#include <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
#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<std::string> components;
|
||||
std::vector<std::string> 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<std::string> prodBomPaths;
|
||||
std::vector<std::string> 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, bool emitDevCaches, bool isLocallyBuiltCache)
|
||||
{
|
||||
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<std::set<std::string>> 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<std::string> temp;
|
||||
temp.insert(configName);
|
||||
dedupedCacheSets.push_back(temp);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
manifest.forEachConfiguration([&manifest, &dedupedCacheSets](const std::string& configName) {
|
||||
std::set<std::string> temp;
|
||||
temp.insert(configName);
|
||||
dedupedCacheSets.push_back(temp);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<dyld3::BuildQueueEntry> 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;
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
std::array<uint8_t, CC_SHA1_DIGEST_LENGTH> digest = { 0 };
|
||||
CC_SHA1(setName.c_str(), (unsigned int)setName.length(), &digest[0]);
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
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, skipWrites, verbose, emitDevCaches, isLocallyBuiltCache](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;
|
||||
}
|
||||
|
||||
mkpath_np(configPath.c_str(), 0755);
|
||||
if (manifest.platform() == dyld3::Platform::macOS) {
|
||||
buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, false, setName + "/" + arch,
|
||||
isLocallyBuiltCache, skipWrites, verbose));
|
||||
} else {
|
||||
if (emitDevCaches)
|
||||
buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch + ".development", cacheSet, arch, false, setName + "/" + arch,
|
||||
isLocallyBuiltCache, skipWrites, verbose));
|
||||
buildQueue.push_back(manifest.makeQueueEntry(configPath + "dyld_shared_cache_" + arch, cacheSet, arch, true, setName + "/" + arch,
|
||||
isLocallyBuiltCache, skipWrites, verbose));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
__block bool cacheBuildFailure = false;
|
||||
__block std::set<std::string> warnings;
|
||||
__block std::set<std::string> errors;
|
||||
|
||||
dispatch_sync(warningQueue, ^{
|
||||
auto manifestWarnings = diags.warnings();
|
||||
//warnings.insert(manifestWarnings.begin(), manifestWarnings.end());
|
||||
});
|
||||
|
||||
bool requuiresConcurrencyLimit = false;
|
||||
dispatch_semaphore_t concurrencyLimit = NULL;
|
||||
// Limit cuncurrency to 8 threads for machines with 32GB of RAM and to 1 thread if we have 4GB or less of memory
|
||||
uint64_t memSize = 0;
|
||||
size_t sz = sizeof(memSize);;
|
||||
if ( sysctlbyname("hw.memsize", &memSize, &sz, NULL, 0) == 0 ) {
|
||||
if ( memSize <= 0x100000000ULL ) {
|
||||
fprintf(stderr, "Detected 4Gb or less of memory, limiting concurrency to 1 thread\n");
|
||||
requuiresConcurrencyLimit = true;
|
||||
concurrencyLimit = dispatch_semaphore_create(1);
|
||||
} else if ( memSize <= 0x800000000ULL ) {
|
||||
fprintf(stderr, "Detected 32Gb or less of memory, limiting concurrency to 8 threads\n");
|
||||
requuiresConcurrencyLimit = true;
|
||||
concurrencyLimit = dispatch_semaphore_create(8);
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_apply(buildQueue.size(), queue, ^(size_t index) {
|
||||
auto queueEntry = buildQueue[index];
|
||||
pthread_setname_np(queueEntry.options.loggingPrefix.substr(0, MAXTHREADNAMESIZE - 1).c_str());
|
||||
|
||||
// Horrible hack to limit concurrency in low spec build machines.
|
||||
if (requuiresConcurrencyLimit) { dispatch_semaphore_wait(concurrencyLimit, DISPATCH_TIME_FOREVER); }
|
||||
DyldSharedCache::CreateResults results = DyldSharedCache::create(queueEntry.options, queueEntry.fileSystem, queueEntry.dylibsForCache, queueEntry.otherDylibsAndBundles, queueEntry.mainExecutables);
|
||||
if (requuiresConcurrencyLimit) { dispatch_semaphore_signal(concurrencyLimit); }
|
||||
|
||||
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) {
|
||||
auto& configResults = manifest.configuration(configName).architecture(queueEntry.options.archs->name()).results;
|
||||
for (const auto& mh : results.evictions) {
|
||||
configResults.exclude(mh, "VM overflow, evicting");
|
||||
}
|
||||
configResults.warnings = results.warnings;
|
||||
if (queueEntry.options.optimizeStubs) {
|
||||
configResults.productionCache.cdHash = chooseSecondCdHash ? results.cdHashSecond : results.cdHashFirst;
|
||||
} else {
|
||||
configResults.developmentCache.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());
|
||||
cacheBuildFailure = true;
|
||||
} else if (skipWrites) {
|
||||
fprintf(stderr, "[%s] Skipped writing cache to: %s\n", queueEntry.options.loggingPrefix.c_str(), queueEntry.outputPath.c_str());
|
||||
}
|
||||
});
|
||||
|
||||
// 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;
|
||||
}
|
3657
dyld3/shared-cache/CacheBuilder.cpp
Normal file
3657
dyld3/shared-cache/CacheBuilder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
325
dyld3/shared-cache/CacheBuilder.h
Normal file
325
dyld3/shared-cache/CacheBuilder.h
Normal file
@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "ClosureFileSystem.h"
|
||||
#include "DyldSharedCache.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "MachOAnalyzer.h"
|
||||
|
||||
|
||||
|
||||
template <typename P> class LinkeditOptimizer;
|
||||
|
||||
|
||||
class CacheBuilder {
|
||||
public:
|
||||
CacheBuilder(const DyldSharedCache::CreateOptions& options, const dyld3::closure::FileSystem& fileSystem);
|
||||
|
||||
struct InputFile {
|
||||
enum State {
|
||||
Unset,
|
||||
MustBeIncluded,
|
||||
MustBeIncludedForDependent,
|
||||
MustBeExcludedIfUnused
|
||||
};
|
||||
InputFile(const char* path, State state) : path(path), state(state) { }
|
||||
const char* path;
|
||||
State state = Unset;
|
||||
Diagnostics diag;
|
||||
|
||||
bool mustBeIncluded() const {
|
||||
return (state == MustBeIncluded) || (state == MustBeIncludedForDependent);
|
||||
}
|
||||
};
|
||||
|
||||
// Contains a MachO which has been loaded from the file system and may potentially need to be unloaded later.
|
||||
struct LoadedMachO {
|
||||
DyldSharedCache::MappedMachO mappedFile;
|
||||
dyld3::closure::LoadedFileInfo loadedFileInfo;
|
||||
InputFile* inputFile;
|
||||
};
|
||||
|
||||
void build(std::vector<InputFile>& inputFiles,
|
||||
std::vector<DyldSharedCache::FileAlias>& aliases);
|
||||
void build(const std::vector<LoadedMachO>& dylibs,
|
||||
const std::vector<LoadedMachO>& otherOsDylibsInput,
|
||||
const std::vector<LoadedMachO>& osExecutables,
|
||||
std::vector<DyldSharedCache::FileAlias>& aliases);
|
||||
void build(const std::vector<DyldSharedCache::MappedMachO>& dylibsToCache,
|
||||
const std::vector<DyldSharedCache::MappedMachO>& otherOsDylibs,
|
||||
const std::vector<DyldSharedCache::MappedMachO>& osExecutables,
|
||||
std::vector<DyldSharedCache::FileAlias>& aliases);
|
||||
void writeFile(const std::string& path);
|
||||
void writeBuffer(uint8_t*& buffer, uint64_t& size);
|
||||
void writeMapFile(const std::string& path);
|
||||
std::string getMapFileBuffer(const std::string& cacheDisposition) const;
|
||||
void deleteBuffer();
|
||||
std::string errorMessage();
|
||||
const std::set<std::string> warnings();
|
||||
const std::set<const dyld3::MachOAnalyzer*> evictions();
|
||||
const bool agileSignature();
|
||||
const std::string cdHashFirst();
|
||||
const std::string cdHashSecond();
|
||||
const std::string uuid() const;
|
||||
|
||||
void forEachCacheDylib(void (^callback)(const std::string& path));
|
||||
|
||||
struct SegmentMappingInfo {
|
||||
const void* srcSegment;
|
||||
const char* segName;
|
||||
void* dstSegment;
|
||||
uint64_t dstCacheUnslidAddress;
|
||||
uint32_t dstCacheFileOffset;
|
||||
uint32_t dstCacheSegmentSize;
|
||||
uint32_t dstCacheFileSize;
|
||||
uint32_t copySegmentSize;
|
||||
uint32_t srcSegmentIndex;
|
||||
};
|
||||
|
||||
struct DylibTextCoalescer {
|
||||
|
||||
typedef std::map<uint32_t, uint32_t> DylibSectionOffsetToCacheSectionOffset;
|
||||
|
||||
DylibSectionOffsetToCacheSectionOffset objcClassNames;
|
||||
DylibSectionOffsetToCacheSectionOffset objcMethNames;
|
||||
DylibSectionOffsetToCacheSectionOffset objcMethTypes;
|
||||
|
||||
bool sectionWasCoalesced(std::string_view sectionName) const;
|
||||
DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName);
|
||||
const DylibSectionOffsetToCacheSectionOffset& getSectionCoalescer(std::string_view sectionName) const;
|
||||
};
|
||||
|
||||
struct CacheCoalescedText {
|
||||
static const char* SupportedSections[3];
|
||||
struct StringSection {
|
||||
// Map from class name strings to offsets in to the class names buffer
|
||||
std::map<std::string_view, uint32_t> stringsToOffsets;
|
||||
uint8_t* bufferAddr = nullptr;
|
||||
uint32_t bufferSize = 0;
|
||||
uint64_t bufferVMAddr = 0;
|
||||
|
||||
// Note this is for debugging only
|
||||
uint64_t savedSpace = 0;
|
||||
};
|
||||
|
||||
StringSection objcClassNames;
|
||||
StringSection objcMethNames;
|
||||
StringSection objcMethTypes;
|
||||
|
||||
void parseCoalescableText(const dyld3::MachOAnalyzer* ma,
|
||||
DylibTextCoalescer& textCoalescer);
|
||||
void clear();
|
||||
|
||||
StringSection& getSectionData(std::string_view sectionName);
|
||||
const StringSection& getSectionData(std::string_view sectionName) const;
|
||||
};
|
||||
|
||||
class ASLR_Tracker
|
||||
{
|
||||
public:
|
||||
~ASLR_Tracker();
|
||||
|
||||
void setDataRegion(const void* rwRegionStart, size_t rwRegionSize);
|
||||
void add(void* p);
|
||||
void remove(void* p);
|
||||
bool has(void* p);
|
||||
const bool* bitmap() { return _bitmap; }
|
||||
unsigned dataPageCount() { return _pageCount; }
|
||||
void disable() { _enabled = false; };
|
||||
|
||||
private:
|
||||
|
||||
uint8_t* _regionStart = nullptr;
|
||||
uint8_t* _endStart = nullptr;
|
||||
bool* _bitmap = nullptr;
|
||||
unsigned _pageCount = 0;
|
||||
unsigned _pageSize = 4096;
|
||||
bool _enabled = true;
|
||||
};
|
||||
|
||||
typedef std::map<uint64_t, std::set<void*>> LOH_Tracker;
|
||||
|
||||
struct Region
|
||||
{
|
||||
uint8_t* buffer = nullptr;
|
||||
uint64_t bufferSize = 0;
|
||||
uint64_t sizeInUse = 0;
|
||||
uint64_t unslidLoadAddress = 0;
|
||||
uint64_t cacheFileOffset = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
template <typename P>
|
||||
friend class LinkeditOptimizer;
|
||||
|
||||
struct ArchLayout
|
||||
{
|
||||
uint64_t sharedMemoryStart;
|
||||
uint64_t sharedMemorySize;
|
||||
uint64_t textAndDataMaxSize;
|
||||
uint64_t sharedRegionPadding;
|
||||
uint64_t pointerDeltaMask;
|
||||
const char* archName;
|
||||
uint16_t csPageSize;
|
||||
uint8_t sharedRegionAlignP2;
|
||||
uint8_t slideInfoBytesPerPage;
|
||||
bool sharedRegionsAreDiscontiguous;
|
||||
bool is64;
|
||||
bool useValueAdd;
|
||||
};
|
||||
|
||||
static const ArchLayout _s_archLayout[];
|
||||
static const char* const _s_neverStubEliminateDylibs[];
|
||||
static const char* const _s_neverStubEliminateSymbols[];
|
||||
|
||||
struct UnmappedRegion
|
||||
{
|
||||
uint8_t* buffer = nullptr;
|
||||
uint64_t bufferSize = 0;
|
||||
uint64_t sizeInUse = 0;
|
||||
};
|
||||
|
||||
struct DylibInfo
|
||||
{
|
||||
const LoadedMachO* input;
|
||||
std::string runtimePath;
|
||||
std::vector<SegmentMappingInfo> cacheLocation;
|
||||
DylibTextCoalescer textCoalescer;
|
||||
};
|
||||
|
||||
void makeSortedDylibs(const std::vector<LoadedMachO>& dylibs, const std::unordered_map<std::string, unsigned> sortOrder);
|
||||
void processSelectorStrings(const std::vector<LoadedMachO>& executables);
|
||||
void parseCoalescableSegments();
|
||||
void assignSegmentAddresses();
|
||||
|
||||
uint64_t cacheOverflowAmount();
|
||||
size_t evictLeafDylibs(uint64_t reductionTarget, std::vector<const LoadedMachO*>& overflowDylibs);
|
||||
|
||||
void fipsSign();
|
||||
void codeSign();
|
||||
uint64_t pathHash(const char* path);
|
||||
void writeCacheHeader();
|
||||
void copyRawSegments();
|
||||
void adjustAllImagesForNewSegmentLocations();
|
||||
void writeSlideInfoV1();
|
||||
void writeSlideInfoV3(const bool bitmap[], unsigned dataPageCoun);
|
||||
uint16_t pageStartV3(uint8_t* pageContent, uint32_t pageSize, const bool bitmap[]);
|
||||
void findDylibAndSegment(const void* contentPtr, std::string& dylibName, std::string& segName);
|
||||
void addImageArray();
|
||||
void buildImageArray(std::vector<DyldSharedCache::FileAlias>& aliases);
|
||||
void addOtherImageArray(const std::vector<LoadedMachO>&, std::vector<const LoadedMachO*>& overflowDylibs);
|
||||
void addClosures(const std::vector<LoadedMachO>&);
|
||||
void markPaddingInaccessible();
|
||||
|
||||
bool writeCache(void (^cacheSizeCallback)(uint64_t size), bool (^copyCallback)(const uint8_t* src, uint64_t size, uint64_t dstOffset));
|
||||
|
||||
template <typename P> void writeSlideInfoV2(const bool bitmap[], unsigned dataPageCount);
|
||||
template <typename P> bool makeRebaseChainV2(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info2* info);
|
||||
template <typename P> void addPageStartsV2(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info2* info,
|
||||
std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
|
||||
|
||||
template <typename P> void writeSlideInfoV4(const bool bitmap[], unsigned dataPageCount);
|
||||
template <typename P> bool makeRebaseChainV4(uint8_t* pageContent, uint16_t lastLocationOffset, uint16_t newOffset, const struct dyld_cache_slide_info4* info);
|
||||
template <typename P> void addPageStartsV4(uint8_t* pageContent, const bool bitmap[], const struct dyld_cache_slide_info4* info,
|
||||
std::vector<uint16_t>& pageStarts, std::vector<uint16_t>& pageExtras);
|
||||
|
||||
// implemented in AdjustDylibSegemnts.cpp
|
||||
void adjustDylibSegments(const DylibInfo& dylib, Diagnostics& diag) const;
|
||||
|
||||
// implemented in OptimizerLinkedit.cpp
|
||||
void optimizeLinkedit();
|
||||
|
||||
// implemented in OptimizerObjC.cpp
|
||||
void optimizeObjC();
|
||||
uint32_t computeReadOnlyObjC(uint32_t selRefCount, uint32_t classDefCount, uint32_t protocolDefCount);
|
||||
uint32_t computeReadWriteObjC(uint32_t imageCount, uint32_t protocolDefCount);
|
||||
|
||||
// implemented in OptimizerBranches.cpp
|
||||
void optimizeAwayStubs();
|
||||
|
||||
typedef std::unordered_map<std::string, const dyld3::MachOAnalyzer*> InstallNameToMA;
|
||||
|
||||
typedef uint64_t CacheOffset;
|
||||
|
||||
const DyldSharedCache::CreateOptions& _options;
|
||||
const dyld3::closure::FileSystem& _fileSystem;
|
||||
Region _readExecuteRegion;
|
||||
Region _readWriteRegion;
|
||||
Region _readOnlyRegion;
|
||||
UnmappedRegion _localSymbolsRegion;
|
||||
UnmappedRegion _codeSignatureRegion;
|
||||
vm_address_t _fullAllocatedBuffer;
|
||||
uint64_t _nonLinkEditReadOnlySize;
|
||||
Diagnostics _diagnostics;
|
||||
std::set<const dyld3::MachOAnalyzer*> _evictions;
|
||||
const ArchLayout* _archLayout;
|
||||
uint32_t _aliasCount;
|
||||
uint64_t _slideInfoFileOffset;
|
||||
uint64_t _slideInfoBufferSizeAllocated;
|
||||
uint8_t* _objcReadOnlyBuffer;
|
||||
uint64_t _objcReadOnlyBufferSizeUsed;
|
||||
uint64_t _objcReadOnlyBufferSizeAllocated;
|
||||
uint8_t* _objcReadWriteBuffer;
|
||||
uint64_t _objcReadWriteBufferSizeAllocated;
|
||||
uint64_t _allocatedBufferSize;
|
||||
CacheCoalescedText _coalescedText;
|
||||
uint64_t _selectorStringsFromExecutables;
|
||||
std::vector<DylibInfo> _sortedDylibs;
|
||||
InstallNameToMA _installNameToCacheDylib;
|
||||
std::unordered_map<std::string, uint32_t> _dataDirtySegsOrder;
|
||||
// Note this is mutable as the only parallel writes to it are done atomically to the bitmap
|
||||
mutable ASLR_Tracker _aslrTracker;
|
||||
std::map<void*, std::string> _missingWeakImports;
|
||||
mutable LOH_Tracker _lohTracker;
|
||||
const dyld3::closure::ImageArray* _imageArray;
|
||||
uint32_t _sharedStringsPoolVmOffset;
|
||||
uint8_t _cdHashFirst[20];
|
||||
uint8_t _cdHashSecond[20];
|
||||
std::unordered_map<const dyld3::MachOLoaded*, std::set<CacheOffset>> _dylibToItsExports;
|
||||
std::set<std::pair<const dyld3::MachOLoaded*, CacheOffset>> _dylibWeakExports;
|
||||
std::unordered_map<CacheOffset, std::vector<dyld_cache_patchable_location>> _exportsToUses;
|
||||
std::unordered_map<CacheOffset, std::string> _exportsToName;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
inline uint64_t align(uint64_t addr, uint8_t p2)
|
||||
{
|
||||
uint64_t mask = (1 << p2);
|
||||
return (addr + mask - 1) & (-mask);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif /* CacheBuilder_h */
|
898
dyld3/shared-cache/DyldSharedCache.cpp
Normal file
898
dyld3/shared-cache/DyldSharedCache.cpp
Normal file
@ -0,0 +1,898 @@
|
||||
/* -*- 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 <dirent.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/param.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <mach-o/dyld_priv.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include "CacheBuilder.h"
|
||||
#include "FileUtils.h"
|
||||
#endif
|
||||
|
||||
#define NO_ULEB
|
||||
#include "MachOLoaded.h"
|
||||
#include "ClosureFileSystemPhysical.h"
|
||||
#include "DyldSharedCache.h"
|
||||
#include "Trie.hpp"
|
||||
#include "StringUtils.h"
|
||||
|
||||
#include "objc-shared-cache.h"
|
||||
|
||||
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
#include "JSONWriter.h"
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
DyldSharedCache::CreateResults DyldSharedCache::create(const CreateOptions& options,
|
||||
const dyld3::closure::FileSystem& fileSystem,
|
||||
const std::vector<MappedMachO>& dylibsToCache,
|
||||
const std::vector<MappedMachO>& otherOsDylibs,
|
||||
const std::vector<MappedMachO>& osExecutables)
|
||||
{
|
||||
CreateResults results;
|
||||
CacheBuilder cache(options, fileSystem);
|
||||
if (!cache.errorMessage().empty()) {
|
||||
results.errorMessage = cache.errorMessage();
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<FileAlias> aliases;
|
||||
switch ( options.platform ) {
|
||||
case dyld3::Platform::iOS:
|
||||
case dyld3::Platform::watchOS:
|
||||
case dyld3::Platform::tvOS:
|
||||
// FIXME: embedded cache builds should be getting aliases from manifest
|
||||
aliases.push_back({"/System/Library/Frameworks/IOKit.framework/Versions/A/IOKit", "/System/Library/Frameworks/IOKit.framework/IOKit"});
|
||||
aliases.push_back({"/usr/lib/libstdc++.6.dylib", "/usr/lib/libstdc++.dylib"});
|
||||
aliases.push_back({"/usr/lib/libstdc++.6.dylib", "/usr/lib/libstdc++.6.0.9.dylib"});
|
||||
aliases.push_back({"/usr/lib/libz.1.dylib", "/usr/lib/libz.dylib"});
|
||||
aliases.push_back({"/usr/lib/libSystem.B.dylib", "/usr/lib/libSystem.dylib"});
|
||||
aliases.push_back({"/System/Library/Frameworks/Foundation.framework/Foundation", "/usr/lib/libextension.dylib"}); // <rdar://44315703>
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
cache.build(dylibsToCache, otherOsDylibs, osExecutables, aliases);
|
||||
|
||||
results.agileSignature = cache.agileSignature();
|
||||
results.cdHashFirst = cache.cdHashFirst();
|
||||
results.cdHashSecond = cache.cdHashSecond();
|
||||
results.warnings = cache.warnings();
|
||||
results.evictions = cache.evictions();
|
||||
if ( cache.errorMessage().empty() ) {
|
||||
if ( !options.outputFilePath.empty() ) {
|
||||
// write cache file, if path non-empty
|
||||
cache.writeFile(options.outputFilePath);
|
||||
}
|
||||
if ( !options.outputMapFilePath.empty() ) {
|
||||
// write map file, if path non-empty
|
||||
cache.writeMapFile(options.outputMapFilePath);
|
||||
}
|
||||
}
|
||||
results.errorMessage = cache.errorMessage();
|
||||
cache.deleteBuffer();
|
||||
return results;
|
||||
}
|
||||
|
||||
bool DyldSharedCache::verifySelfContained(std::vector<MappedMachO>& dylibsToCache,
|
||||
std::unordered_set<std::string>& badZippered,
|
||||
MappedMachO (^loader)(const std::string& runtimePath),
|
||||
std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& rejected)
|
||||
{
|
||||
// build map of dylibs
|
||||
__block std::map<std::string, std::set<std::string>> badDylibs;
|
||||
__block std::set<std::string> knownDylibs;
|
||||
for (const DyldSharedCache::MappedMachO& dylib : dylibsToCache) {
|
||||
std::set<std::string> reasons;
|
||||
if ( dylib.mh->canBePlacedInDyldCache(dylib.runtimePath.c_str(), ^(const char* msg) { badDylibs[dylib.runtimePath].insert(msg);}) ) {
|
||||
knownDylibs.insert(dylib.runtimePath);
|
||||
knownDylibs.insert(dylib.mh->installName());
|
||||
} else {
|
||||
badDylibs[dylib.runtimePath].insert("");
|
||||
}
|
||||
}
|
||||
|
||||
// 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<DyldSharedCache::MappedMachO> 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;
|
||||
dylib.mh->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;
|
||||
if ( badZippered.count(loadPath) != 0 ) {
|
||||
badDylibs[dylib.runtimePath].insert("");
|
||||
knownDylibs.erase(dylib.runtimePath);
|
||||
knownDylibs.erase(dylib.mh->installName());
|
||||
badZippered.insert(dylib.runtimePath);
|
||||
badZippered.insert(dylib.mh->installName());
|
||||
return;
|
||||
}
|
||||
MappedMachO foundMapping;
|
||||
if ( badDylibs.count(loadPath) == 0 )
|
||||
foundMapping = loader(loadPath);
|
||||
if ( foundMapping.length == 0 ) {
|
||||
badDylibs[dylib.runtimePath].insert(std::string("Could not find dependency '") + loadPath +"'");
|
||||
knownDylibs.erase(dylib.runtimePath);
|
||||
knownDylibs.erase(dylib.mh->installName());
|
||||
}
|
||||
else {
|
||||
std::set<std::string> reasons;
|
||||
if ( foundMapping.mh->canBePlacedInDyldCache(foundMapping.runtimePath.c_str(), ^(const char* msg) { badDylibs[foundMapping.runtimePath].insert(msg);})) {
|
||||
// see if existing mapping was returned
|
||||
bool alreadyInVector = false;
|
||||
for (const MappedMachO& existing : dylibsToCache) {
|
||||
if ( existing.mh == foundMapping.mh ) {
|
||||
alreadyInVector = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !alreadyInVector )
|
||||
foundMappings.push_back(foundMapping);
|
||||
knownDylibs.insert(loadPath);
|
||||
knownDylibs.insert(foundMapping.runtimePath);
|
||||
knownDylibs.insert(foundMapping.mh->installName());
|
||||
} else {
|
||||
badDylibs[dylib.runtimePath].insert("");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
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()) {
|
||||
// Only add the warning if we are not a bad zippered dylib
|
||||
if ( badZippered.count(dylib.runtimePath) == 0 )
|
||||
rejected.push_back(std::make_pair(dylib, i->second));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}), dylibsToCache.end());
|
||||
}
|
||||
|
||||
return badDylibs.empty();
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
const T DyldSharedCache::getAddrField(uint64_t addr) const {
|
||||
uint64_t slide = (uint64_t)this - unslidLoadAddress();
|
||||
return (const T)(addr + slide);
|
||||
}
|
||||
|
||||
void DyldSharedCache::forEachRegion(void (^handler)(const void* content, uint64_t vmAddr, uint64_t size, uint32_t permissions)) const
|
||||
{
|
||||
// <rdar://problem/49875993> sanity check cache header
|
||||
if ( strncmp(header.magic, "dyld_v1", 7) != 0 )
|
||||
return;
|
||||
if ( header.mappingOffset > 1024 )
|
||||
return;
|
||||
if ( header.mappingCount > 20 )
|
||||
return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
bool DyldSharedCache::inCache(const void* addr, size_t length, bool& readOnly) const
|
||||
{
|
||||
// quick out if before start of cache
|
||||
if ( addr < this )
|
||||
return false;
|
||||
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
uintptr_t unslidStart = (uintptr_t)addr - slide;
|
||||
|
||||
// quick out if after end of cache
|
||||
if ( unslidStart > (mappings[2].address + mappings[2].size) )
|
||||
return false;
|
||||
|
||||
// walk cache regions
|
||||
const dyld_cache_mapping_info* mappingsEnd = &mappings[header.mappingCount];
|
||||
uintptr_t unslidEnd = unslidStart + length;
|
||||
for (const dyld_cache_mapping_info* m=mappings; m < mappingsEnd; ++m) {
|
||||
if ( (unslidStart >= m->address) && (unslidEnd < (m->address+m->size)) ) {
|
||||
readOnly = ((m->initProt & VM_PROT_WRITE) == 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
const mach_header* DyldSharedCache::getIndexedImageEntry(uint32_t index, 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);
|
||||
mTime = dylibs[index].modTime;
|
||||
inode = dylibs[index].inode;
|
||||
return (mach_header*)((uint8_t*)this + dylibs[index].address - mappings[0].address);
|
||||
}
|
||||
|
||||
void DyldSharedCache::forEachImageTextSegment(void (^handler)(uint64_t loadAddressUnslid, uint64_t textSegmentSize, const uuid_t dylibUUID, const char* installName, bool& stop)) 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];
|
||||
bool stop = false;
|
||||
for (const dyld_cache_image_text_info* p=imagesText; p < imagesTextEnd && !stop; ++p) {
|
||||
handler(p->loadAddress, p->textSegmentSize, p->uuid, (char*)this + p->pathOffset, stop);
|
||||
}
|
||||
}
|
||||
|
||||
bool DyldSharedCache::addressInText(uint32_t cacheOffset, uint32_t* imageIndex) const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
if ( cacheOffset > mappings[0].size )
|
||||
return false;
|
||||
uint64_t targetAddr = mappings[0].address + cacheOffset;
|
||||
// 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) {
|
||||
if ( (p->loadAddress <= targetAddr) && (targetAddr < p->loadAddress+p->textSegmentSize) ) {
|
||||
*imageIndex = (uint32_t)(p-imagesText);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* DyldSharedCache::archName() const
|
||||
{
|
||||
const char* archSubString = ((char*)this) + 7;
|
||||
while (*archSubString == ' ')
|
||||
++archSubString;
|
||||
return archSubString;
|
||||
}
|
||||
|
||||
|
||||
dyld3::Platform DyldSharedCache::platform() const
|
||||
{
|
||||
return (dyld3::Platform)header.platform;
|
||||
}
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
std::string DyldSharedCache::mapFile() const
|
||||
{
|
||||
__block std::string result;
|
||||
__block std::vector<uint64_t> regionStartAddresses;
|
||||
__block std::vector<uint64_t> regionSizes;
|
||||
__block std::vector<uint64_t> 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";
|
||||
const dyld3::MachOFile* mf = (dyld3::MachOFile*)mh;
|
||||
mf->forEachSegment(^(const dyld3::MachOFile::SegmentInfo& info, bool& stop) {
|
||||
char lineBuffer[256];
|
||||
sprintf(lineBuffer, "\t%16s 0x%08llX -> 0x%08llX\n", info.segName, info.vmAddr, info.vmAddr+info.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);
|
||||
}
|
||||
|
||||
bool DyldSharedCache::findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
uint64_t unslidMh = (uintptr_t)mh - slide;
|
||||
const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
|
||||
for (uint32_t i=0; i < header.imagesCount; ++i) {
|
||||
if ( dylibs[i].address == unslidMh ) {
|
||||
imageIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DyldSharedCache::hasImagePath(const char* dylibPath, uint32_t& imageIndex) const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
if ( mappings[0].fileOffset != 0 )
|
||||
return false;
|
||||
if ( header.mappingOffset >= 0x118 ) {
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
const uint8_t* dylibTrieStart = (uint8_t*)(this->header.dylibsTrieAddr + slide);
|
||||
const uint8_t* dylibTrieEnd = dylibTrieStart + this->header.dylibsTrieSize;
|
||||
|
||||
Diagnostics diag;
|
||||
const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, dylibTrieStart, dylibTrieEnd, dylibPath);
|
||||
if ( imageNode != NULL ) {
|
||||
imageIndex = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, dylibTrieEnd);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const dyld_cache_image_info* dylibs = (dyld_cache_image_info*)((char*)this + header.imagesOffset);
|
||||
uint64_t firstImageOffset = 0;
|
||||
uint64_t firstRegionAddress = mappings[0].address;
|
||||
for (uint32_t i=0; i < header.imagesCount; ++i) {
|
||||
const char* aPath = (char*)this + dylibs[i].pathFileOffset;
|
||||
if ( strcmp(aPath, dylibPath) == 0 ) {
|
||||
imageIndex = i;
|
||||
return true;
|
||||
}
|
||||
uint64_t offset = dylibs[i].address - firstRegionAddress;
|
||||
if ( firstImageOffset == 0 )
|
||||
firstImageOffset = offset;
|
||||
// skip over aliases
|
||||
if ( dylibs[i].pathFileOffset < firstImageOffset)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DyldSharedCache::hasNonOverridablePath(const char* dylibPath) const
|
||||
{
|
||||
// all dylibs in customer dyld cache cannot be overridden except libdispatch.dylib
|
||||
bool pathIsInDyldCacheWhichCannotBeOverridden = false;
|
||||
if ( header.cacheType == kDyldSharedCacheTypeProduction ) {
|
||||
uint32_t imageIndex;
|
||||
pathIsInDyldCacheWhichCannotBeOverridden = this->hasImagePath(dylibPath, imageIndex);
|
||||
if ( pathIsInDyldCacheWhichCannotBeOverridden && (strcmp(dylibPath, "/usr/lib/system/libdispatch.dylib") == 0) )
|
||||
pathIsInDyldCacheWhichCannotBeOverridden = false;
|
||||
}
|
||||
return pathIsInDyldCacheWhichCannotBeOverridden;
|
||||
}
|
||||
|
||||
const dyld3::closure::Image* DyldSharedCache::findDlopenOtherImage(const char* path) const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
if ( mappings[0].fileOffset != 0 )
|
||||
return nullptr;
|
||||
if ( header.mappingOffset < sizeof(dyld_cache_header) )
|
||||
return nullptr;
|
||||
if ( header.otherImageArrayAddr == 0 )
|
||||
return nullptr;
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
const uint8_t* dylibTrieStart = (uint8_t*)(this->header.otherTrieAddr + slide);
|
||||
const uint8_t* dylibTrieEnd = dylibTrieStart + this->header.otherTrieSize;
|
||||
|
||||
Diagnostics diag;
|
||||
const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, dylibTrieStart, dylibTrieEnd, path);
|
||||
if ( imageNode != NULL ) {
|
||||
dyld3::closure::ImageNum imageNum = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, dylibTrieEnd);
|
||||
uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
|
||||
const dyld3::closure::ImageArray* otherImageArray = (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
|
||||
return otherImageArray->imageForNum(imageNum);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
const dyld3::closure::LaunchClosure* DyldSharedCache::findClosure(const char* executablePath) const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
const uint8_t* executableTrieStart = (uint8_t*)(this->header.progClosuresTrieAddr + slide);
|
||||
const uint8_t* executableTrieEnd = executableTrieStart + this->header.progClosuresTrieSize;
|
||||
const uint8_t* closuresStart = (uint8_t*)(this->header.progClosuresAddr + slide);
|
||||
|
||||
Diagnostics diag;
|
||||
const uint8_t* imageNode = dyld3::MachOLoaded::trieWalk(diag, executableTrieStart, executableTrieEnd, executablePath);
|
||||
if ( (imageNode == NULL) && (strncmp(executablePath, "/System/", 8) == 0) ) {
|
||||
// anything in /System/ should have a closure. Perhaps it was launched via symlink path
|
||||
char realPath[PATH_MAX];
|
||||
if ( realpath(executablePath, realPath) != NULL )
|
||||
imageNode = dyld3::MachOLoaded::trieWalk(diag, executableTrieStart, executableTrieEnd, realPath);
|
||||
}
|
||||
if ( imageNode != NULL ) {
|
||||
uint32_t closureOffset = (uint32_t)dyld3::MachOFile::read_uleb128(diag, imageNode, executableTrieEnd);
|
||||
if ( closureOffset < this->header.progClosuresSize )
|
||||
return (dyld3::closure::LaunchClosure*)((uint8_t*)closuresStart + closureOffset);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if !BUILDING_LIBDYLD && !BUILDING_DYLD
|
||||
void DyldSharedCache::forEachLaunchClosure(void (^handler)(const char* executableRuntimePath, const dyld3::closure::LaunchClosure* closure)) const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
const uint8_t* executableTrieStart = (uint8_t*)(this->header.progClosuresTrieAddr + slide);
|
||||
const uint8_t* executableTrieEnd = executableTrieStart + this->header.progClosuresTrieSize;
|
||||
const uint8_t* closuresStart = (uint8_t*)(this->header.progClosuresAddr + slide);
|
||||
|
||||
std::vector<DylibIndexTrie::Entry> closureEntries;
|
||||
if ( Trie<DylibIndex>::parseTrie(executableTrieStart, executableTrieEnd, closureEntries) ) {
|
||||
for (DylibIndexTrie::Entry& entry : closureEntries ) {
|
||||
uint32_t offset = entry.info.index;
|
||||
if ( offset < this->header.progClosuresSize )
|
||||
handler(entry.name.c_str(), (const dyld3::closure::LaunchClosure*)(closuresStart+offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DyldSharedCache::forEachDlopenImage(void (^handler)(const char* runtimePath, const dyld3::closure::Image* image)) const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
const uint8_t* otherTrieStart = (uint8_t*)(this->header.otherTrieAddr + slide);
|
||||
const uint8_t* otherTrieEnd = otherTrieStart + this->header.otherTrieSize;
|
||||
|
||||
std::vector<DylibIndexTrie::Entry> otherEntries;
|
||||
if ( Trie<DylibIndex>::parseTrie(otherTrieStart, otherTrieEnd, otherEntries) ) {
|
||||
for (const DylibIndexTrie::Entry& entry : otherEntries ) {
|
||||
dyld3::closure::ImageNum imageNum = entry.info.index;
|
||||
uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
|
||||
const dyld3::closure::ImageArray* otherImageArray = (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
|
||||
handler(entry.name.c_str(), otherImageArray->imageForNum(imageNum));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const dyld3::closure::ImageArray* DyldSharedCache::cachedDylibsImageArray() const
|
||||
{
|
||||
// check for old cache without imagesArray
|
||||
if ( header.mappingOffset < 0x100 )
|
||||
return nullptr;
|
||||
|
||||
if ( header.dylibsImageArrayAddr == 0 )
|
||||
return nullptr;
|
||||
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uint64_t arrayAddrOffset = header.dylibsImageArrayAddr - mappings[0].address;
|
||||
return (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
|
||||
}
|
||||
|
||||
const dyld3::closure::ImageArray* DyldSharedCache::otherOSImageArray() const
|
||||
{
|
||||
// check for old cache without imagesArray
|
||||
if ( header.mappingOffset < sizeof(dyld_cache_header) )
|
||||
return nullptr;
|
||||
|
||||
if ( header.otherImageArrayAddr == 0 )
|
||||
return nullptr;
|
||||
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uint64_t arrayAddrOffset = header.otherImageArrayAddr - mappings[0].address;
|
||||
return (dyld3::closure::ImageArray*)((char*)this + arrayAddrOffset);
|
||||
}
|
||||
|
||||
|
||||
uint32_t DyldSharedCache::patchableExportCount(uint32_t imageIndex) const {
|
||||
if ( header.patchInfoAddr == 0 )
|
||||
return 0;
|
||||
|
||||
const dyld_cache_patch_info* patchInfo = getAddrField<dyld_cache_patch_info*>(header.patchInfoAddr);
|
||||
const dyld_cache_image_patches* patchArray = getAddrField<dyld_cache_image_patches*>(patchInfo->patchTableArrayAddr);
|
||||
if (imageIndex > patchInfo->patchTableArrayCount)
|
||||
return 0;
|
||||
return patchArray[imageIndex].patchExportsCount;
|
||||
}
|
||||
|
||||
void DyldSharedCache::forEachPatchableExport(uint32_t imageIndex, void (^handler)(uint32_t cacheOffsetOfImpl, const char* exportName)) const {
|
||||
if ( header.patchInfoAddr == 0 )
|
||||
return;
|
||||
|
||||
const dyld_cache_patch_info* patchInfo = getAddrField<dyld_cache_patch_info*>(header.patchInfoAddr);
|
||||
const dyld_cache_image_patches* patchArray = getAddrField<dyld_cache_image_patches*>(patchInfo->patchTableArrayAddr);
|
||||
if (imageIndex > patchInfo->patchTableArrayCount)
|
||||
return;
|
||||
const dyld_cache_image_patches& patch = patchArray[imageIndex];
|
||||
if ( (patch.patchExportsStartIndex + patch.patchExportsCount) > patchInfo->patchExportArrayCount )
|
||||
return;
|
||||
const dyld_cache_patchable_export* patchExports = getAddrField<dyld_cache_patchable_export*>(patchInfo->patchExportArrayAddr);
|
||||
const char* exportNames = getAddrField<char*>(patchInfo->patchExportNamesAddr);
|
||||
for (uint64_t exportIndex = 0; exportIndex != patch.patchExportsCount; ++exportIndex) {
|
||||
const dyld_cache_patchable_export& patchExport = patchExports[patch.patchExportsStartIndex + exportIndex];
|
||||
const char* exportName = ( patchExport.exportNameOffset < patchInfo->patchExportNamesSize ) ? &exportNames[patchExport.exportNameOffset] : "";
|
||||
handler(patchExport.cacheOffsetOfImpl, exportName);
|
||||
}
|
||||
}
|
||||
|
||||
void DyldSharedCache::forEachPatchableUseOfExport(uint32_t imageIndex, uint32_t cacheOffsetOfImpl,
|
||||
void (^handler)(dyld_cache_patchable_location patchLocation)) const {
|
||||
if ( header.patchInfoAddr == 0 )
|
||||
return;
|
||||
|
||||
// Loading a new cache so get the data from the cache header
|
||||
const dyld_cache_patch_info* patchInfo = getAddrField<dyld_cache_patch_info*>(header.patchInfoAddr);
|
||||
const dyld_cache_image_patches* patchArray = getAddrField<dyld_cache_image_patches*>(patchInfo->patchTableArrayAddr);
|
||||
if (imageIndex > patchInfo->patchTableArrayCount)
|
||||
return;
|
||||
const dyld_cache_image_patches& patch = patchArray[imageIndex];
|
||||
if ( (patch.patchExportsStartIndex + patch.patchExportsCount) > patchInfo->patchExportArrayCount )
|
||||
return;
|
||||
const dyld_cache_patchable_export* patchExports = getAddrField<dyld_cache_patchable_export*>(patchInfo->patchExportArrayAddr);
|
||||
const dyld_cache_patchable_location* patchLocations = getAddrField<dyld_cache_patchable_location*>(patchInfo->patchLocationArrayAddr);
|
||||
for (uint64_t exportIndex = 0; exportIndex != patch.patchExportsCount; ++exportIndex) {
|
||||
const dyld_cache_patchable_export& patchExport = patchExports[patch.patchExportsStartIndex + exportIndex];
|
||||
if ( patchExport.cacheOffsetOfImpl != cacheOffsetOfImpl )
|
||||
continue;
|
||||
if ( (patchExport.patchLocationsStartIndex + patchExport.patchLocationsCount) > patchInfo->patchLocationArrayCount )
|
||||
return;
|
||||
for (uint64_t locationIndex = 0; locationIndex != patchExport.patchLocationsCount; ++locationIndex) {
|
||||
const dyld_cache_patchable_location& patchLocation = patchLocations[patchExport.patchLocationsStartIndex + locationIndex];
|
||||
handler(patchLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
// MRM map file generator
|
||||
std::string DyldSharedCache::generateJSONMap(const char* disposition) const {
|
||||
dyld3::json::Node cacheNode;
|
||||
|
||||
cacheNode.map["version"].value = "1";
|
||||
cacheNode.map["disposition"].value = disposition;
|
||||
cacheNode.map["base-address"].value = dyld3::json::hex(unslidLoadAddress());
|
||||
uuid_t cache_uuid;
|
||||
getUUID(cache_uuid);
|
||||
uuid_string_t cache_uuidStr;
|
||||
uuid_unparse(cache_uuid, cache_uuidStr);
|
||||
cacheNode.map["uuid"].value = cache_uuidStr;
|
||||
|
||||
__block dyld3::json::Node imagesNode;
|
||||
forEachImage(^(const mach_header *mh, const char *installName) {
|
||||
dyld3::json::Node imageNode;
|
||||
imageNode.map["path"].value = installName;
|
||||
dyld3::MachOAnalyzer* ma = (dyld3::MachOAnalyzer*)mh;
|
||||
uuid_t uuid;
|
||||
if (ma->getUuid(uuid)) {
|
||||
uuid_string_t uuidStr;
|
||||
uuid_unparse(uuid, uuidStr);
|
||||
imageNode.map["uuid"].value = uuidStr;
|
||||
}
|
||||
|
||||
__block dyld3::json::Node segmentsNode;
|
||||
ma->forEachSegment(^(const dyld3::MachOAnalyzer::SegmentInfo &info, bool &stop) {
|
||||
dyld3::json::Node segmentNode;
|
||||
segmentNode.map["name"].value = info.segName;
|
||||
segmentNode.map["start-vmaddr"].value = dyld3::json::hex(info.vmAddr);
|
||||
segmentNode.map["end-vmaddr"].value = dyld3::json::hex(info.vmAddr + info.vmSize);
|
||||
segmentsNode.array.push_back(segmentNode);
|
||||
});
|
||||
imageNode.map["segments"] = segmentsNode;
|
||||
imagesNode.array.push_back(imageNode);
|
||||
});
|
||||
|
||||
cacheNode.map["images"] = imagesNode;
|
||||
|
||||
std::stringstream stream;
|
||||
printJSON(cacheNode, 0, stream);
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string DyldSharedCache::generateJSONDependents() const {
|
||||
std::unordered_map<std::string, std::set<std::string>> dependents;
|
||||
computeTransitiveDependents(dependents);
|
||||
|
||||
std::stringstream stream;
|
||||
|
||||
stream << "{";
|
||||
bool first = true;
|
||||
for (auto p : dependents) {
|
||||
if (!first) stream << "," << std::endl;
|
||||
first = false;
|
||||
|
||||
stream << "\"" << p.first << "\" : [" << std::endl;
|
||||
bool firstDependent = true;
|
||||
for (const std::string & dependent : p.second) {
|
||||
if (!firstDependent) stream << "," << std::endl;
|
||||
firstDependent = false;
|
||||
stream << " \"" << dependent << "\"";
|
||||
}
|
||||
stream << "]" << std::endl;
|
||||
}
|
||||
stream << "}" << std::endl;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
const dyld_cache_slide_info* DyldSharedCache::slideInfo() const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
|
||||
uint64_t offsetInLinkEditRegion = (header.slideInfoOffset - mappings[2].fileOffset);
|
||||
return (dyld_cache_slide_info*)((uint8_t*)(mappings[2].address) + slide + offsetInLinkEditRegion);
|
||||
}
|
||||
|
||||
const uint8_t* DyldSharedCache::dataRegionStart() const
|
||||
{
|
||||
const dyld_cache_mapping_info* mappings = (dyld_cache_mapping_info*)((char*)this + header.mappingOffset);
|
||||
uintptr_t slide = (uintptr_t)this - (uintptr_t)(mappings[0].address);
|
||||
|
||||
return (uint8_t*)(mappings[1].address) + slide;
|
||||
}
|
||||
|
||||
const objc_opt::objc_opt_t* DyldSharedCache::objcOpt() const {
|
||||
// Find the objc image
|
||||
const dyld3::MachOAnalyzer* objcMA = nullptr;
|
||||
|
||||
uint32_t imageIndex;
|
||||
if ( hasImagePath("/usr/lib/libobjc.A.dylib", imageIndex) ) {
|
||||
const dyld3::closure::ImageArray* images = cachedDylibsImageArray();
|
||||
const dyld3::closure::Image* image = images->imageForNum(imageIndex+1);
|
||||
objcMA = (const dyld3::MachOAnalyzer*)((uintptr_t)this + image->cacheOffset());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If we found the objc image, then try to find the read-only data inside.
|
||||
__block const uint8_t* objcROContent = nullptr;
|
||||
int64_t slide = objcMA->getSlide();
|
||||
objcMA->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
|
||||
if (strcmp(info.segInfo.segName, "__TEXT") != 0)
|
||||
return;
|
||||
if (strcmp(info.sectName, "__objc_opt_ro") != 0)
|
||||
return;
|
||||
if ( malformedSectionRange ) {
|
||||
stop = true;
|
||||
return;
|
||||
}
|
||||
objcROContent = (uint8_t*)(info.sectAddr + slide);
|
||||
});
|
||||
|
||||
if (objcROContent == nullptr)
|
||||
return nullptr;
|
||||
|
||||
const objc_opt::objc_opt_t* optObjCHeader = (const objc_opt::objc_opt_t*)objcROContent;
|
||||
return optObjCHeader->version == objc_opt::VERSION ? optObjCHeader : nullptr;
|
||||
}
|
||||
|
||||
const void* DyldSharedCache::objcOptPtrs() const {
|
||||
// Find the objc image
|
||||
const dyld3::MachOAnalyzer* objcMA = nullptr;
|
||||
|
||||
uint32_t imageIndex;
|
||||
if ( hasImagePath("/usr/lib/libobjc.A.dylib", imageIndex) ) {
|
||||
const dyld3::closure::ImageArray* images = cachedDylibsImageArray();
|
||||
const dyld3::closure::Image* image = images->imageForNum(imageIndex+1);
|
||||
objcMA = (const dyld3::MachOAnalyzer*)((uintptr_t)this + image->cacheOffset());
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If we found the objc image, then try to find the read-only data inside.
|
||||
__block const void* objcPointersContent = nullptr;
|
||||
int64_t slide = objcMA->getSlide();
|
||||
uint32_t pointerSize = objcMA->pointerSize();
|
||||
objcMA->forEachSection(^(const dyld3::MachOAnalyzer::SectionInfo& info, bool malformedSectionRange, bool& stop) {
|
||||
if ( strncmp(info.segInfo.segName, "__DATA", 6) != 0 )
|
||||
return;
|
||||
if (strcmp(info.sectName, "__objc_opt_ptrs") != 0)
|
||||
return;
|
||||
if ( info.sectSize != pointerSize ) {
|
||||
stop = true;
|
||||
return;
|
||||
}
|
||||
if ( malformedSectionRange ) {
|
||||
stop = true;
|
||||
return;
|
||||
}
|
||||
objcPointersContent = (uint8_t*)(info.sectAddr + slide);
|
||||
});
|
||||
|
||||
return objcPointersContent;
|
||||
}
|
||||
|
||||
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
void DyldSharedCache::fillMachOAnalyzersMap(std::unordered_map<std::string,dyld3::MachOAnalyzer*> & dylibAnalyzers) const {
|
||||
forEachImage(^(const mach_header *mh, const char *iteratedInstallName) {
|
||||
dylibAnalyzers[std::string(iteratedInstallName)] = (dyld3::MachOAnalyzer*)mh;
|
||||
});
|
||||
}
|
||||
|
||||
void DyldSharedCache::computeReverseDependencyMapForDylib(std::unordered_map<std::string, std::set<std::string>> &reverseDependencyMap, const std::unordered_map<std::string,dyld3::MachOAnalyzer*> & dylibAnalyzers, const std::string &loadPath) const {
|
||||
dyld3::MachOAnalyzer *ma = dylibAnalyzers.at(loadPath);
|
||||
if (reverseDependencyMap.find(loadPath) != reverseDependencyMap.end()) return;
|
||||
reverseDependencyMap[loadPath] = std::set<std::string>();
|
||||
|
||||
ma->forEachDependentDylib(^(const char *dependencyLoadPath, bool isWeak, bool isReExport, bool isUpward, uint32_t compatVersion, uint32_t curVersion, bool &stop) {
|
||||
if (isUpward) return;
|
||||
std::string dependencyLoadPathString = std::string(dependencyLoadPath);
|
||||
computeReverseDependencyMapForDylib(reverseDependencyMap, dylibAnalyzers, dependencyLoadPathString);
|
||||
reverseDependencyMap[dependencyLoadPathString].insert(loadPath);
|
||||
});
|
||||
}
|
||||
|
||||
// Walks the shared cache and construct the reverse dependency graph (if dylib A depends on B,
|
||||
// constructs the graph with B -> A edges)
|
||||
void DyldSharedCache::computeReverseDependencyMap(std::unordered_map<std::string, std::set<std::string>> &reverseDependencyMap) const {
|
||||
std::unordered_map<std::string,dyld3::MachOAnalyzer*> dylibAnalyzers;
|
||||
|
||||
fillMachOAnalyzersMap(dylibAnalyzers);
|
||||
forEachImage(^(const mach_header *mh, const char *installName) {
|
||||
computeReverseDependencyMapForDylib(reverseDependencyMap, dylibAnalyzers, std::string(installName));
|
||||
});
|
||||
}
|
||||
|
||||
// uses the reverse dependency graph constructed above to find the recursive set of dependents for each dylib
|
||||
void DyldSharedCache::findDependentsRecursively(std::unordered_map<std::string, std::set<std::string>> &transitiveDependents, const std::unordered_map<std::string, std::set<std::string>> &reverseDependencyMap, std::set<std::string> & visited, const std::string &loadPath) const {
|
||||
|
||||
if (transitiveDependents.find(loadPath) != transitiveDependents.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (visited.find(loadPath) != visited.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
visited.insert(loadPath);
|
||||
|
||||
std::set<std::string> dependents;
|
||||
|
||||
for (const std::string & dependent : reverseDependencyMap.at(loadPath)) {
|
||||
findDependentsRecursively(transitiveDependents, reverseDependencyMap, visited, dependent);
|
||||
if (transitiveDependents.find(dependent) != transitiveDependents.end()) {
|
||||
std::set<std::string> & theseTransitiveDependents = transitiveDependents.at(dependent);
|
||||
dependents.insert(theseTransitiveDependents.begin(), theseTransitiveDependents.end());
|
||||
}
|
||||
dependents.insert(dependent);
|
||||
}
|
||||
|
||||
transitiveDependents[loadPath] = dependents;
|
||||
}
|
||||
|
||||
// Fills a map from each install name N to the set of install names depending on N
|
||||
void DyldSharedCache::computeTransitiveDependents(std::unordered_map<std::string, std::set<std::string>> & transitiveDependents) const {
|
||||
std::unordered_map<std::string, std::set<std::string>> reverseDependencyMap;
|
||||
computeReverseDependencyMap(reverseDependencyMap);
|
||||
forEachImage(^(const mach_header *mh, const char *installName) {
|
||||
std::set<std::string> visited;
|
||||
findDependentsRecursively(transitiveDependents, reverseDependencyMap, visited, std::string(installName));
|
||||
});
|
||||
}
|
||||
#endif
|
362
dyld3/shared-cache/DyldSharedCache.h
Normal file
362
dyld3/shared-cache/DyldSharedCache.h
Normal file
@ -0,0 +1,362 @@
|
||||
/*
|
||||
* 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 <TargetConditionals.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#endif
|
||||
|
||||
#include "dyld_cache_format.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "MachOAnalyzer.h"
|
||||
#include "Closure.h"
|
||||
|
||||
namespace objc_opt {
|
||||
struct objc_opt_t;
|
||||
}
|
||||
|
||||
class VIS_HIDDEN DyldSharedCache
|
||||
{
|
||||
public:
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
enum CodeSigningDigestMode
|
||||
{
|
||||
SHA256only = 0,
|
||||
SHA1only = 1,
|
||||
Agile = 2
|
||||
};
|
||||
|
||||
struct CreateOptions
|
||||
{
|
||||
std::string outputFilePath;
|
||||
std::string outputMapFilePath;
|
||||
const dyld3::GradedArchs* archs;
|
||||
dyld3::Platform platform;
|
||||
bool excludeLocalSymbols;
|
||||
bool optimizeStubs;
|
||||
bool optimizeObjC;
|
||||
CodeSigningDigestMode codeSigningDigestMode;
|
||||
bool dylibsRemovedDuringMastering;
|
||||
bool inodesAreSameAsRuntime;
|
||||
bool cacheSupportsASLR;
|
||||
bool forSimulator;
|
||||
bool isLocallyBuiltCache;
|
||||
bool verbose;
|
||||
bool evictLeafDylibsOnOverflow;
|
||||
std::unordered_map<std::string, unsigned> dylibOrdering;
|
||||
std::unordered_map<std::string, unsigned> dirtyDataSegmentOrdering;
|
||||
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 dyld3::MachOAnalyzer* 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 dyld3::MachOAnalyzer* 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
|
||||
{
|
||||
std::string errorMessage;
|
||||
std::set<std::string> warnings;
|
||||
std::set<const dyld3::MachOAnalyzer*> evictions;
|
||||
bool agileSignature = false;
|
||||
std::string cdHashFirst;
|
||||
std::string cdHashSecond;
|
||||
};
|
||||
|
||||
|
||||
struct FileAlias
|
||||
{
|
||||
std::string realPath;
|
||||
std::string aliasPath;
|
||||
};
|
||||
|
||||
|
||||
// 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<MappedMachO>& dylibsToCache,
|
||||
std::unordered_set<std::string>& badZippered,
|
||||
MappedMachO (^loader)(const std::string& runtimePath), std::vector<std::pair<DyldSharedCache::MappedMachO, std::set<std::string>>>& 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 dyld3::closure::FileSystem& fileSystem,
|
||||
const std::vector<MappedMachO>& dylibsToCache,
|
||||
const std::vector<MappedMachO>& otherOsDylibs,
|
||||
const std::vector<MappedMachO>& osExecutables);
|
||||
|
||||
|
||||
//
|
||||
// Returns a text "map" file as a big string
|
||||
//
|
||||
std::string mapFile() const;
|
||||
|
||||
#endif // TARGET_OS_OSX
|
||||
|
||||
|
||||
//
|
||||
// Returns the architecture name of the shared cache, e.g. "arm64"
|
||||
//
|
||||
const char* archName() const;
|
||||
|
||||
|
||||
//
|
||||
// Returns the platform the cache is for
|
||||
//
|
||||
dyld3::Platform platform() const;
|
||||
|
||||
|
||||
//
|
||||
// Iterates over each dylib in the cache
|
||||
//
|
||||
void forEachImage(void (^handler)(const mach_header* mh, const char* installName)) const;
|
||||
|
||||
|
||||
//
|
||||
// Searches cache for dylib with specified path
|
||||
//
|
||||
bool hasImagePath(const char* dylibPath, uint32_t& imageIndex) const;
|
||||
|
||||
|
||||
//
|
||||
// Path is to a dylib in the cache and this is an optimized cache so that path cannot be overridden
|
||||
//
|
||||
bool hasNonOverridablePath(const char* dylibPath) const;
|
||||
|
||||
|
||||
//
|
||||
// Searches cache for dylib with specified mach_header
|
||||
//
|
||||
bool findMachHeaderImageIndex(const mach_header* mh, uint32_t& imageIndex) 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
|
||||
//
|
||||
const mach_header* getIndexedImageEntry(uint32_t index, uint64_t& mTime, uint64_t& node) 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, bool& stop)) 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 if an address range is in this cache, and if so if in a read-only area
|
||||
//
|
||||
bool inCache(const void* addr, size_t length, bool& readOnly) 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;
|
||||
|
||||
|
||||
//
|
||||
// searches cache for pre-built closure for program
|
||||
//
|
||||
const dyld3::closure::LaunchClosure* findClosure(const char* executablePath) const;
|
||||
|
||||
|
||||
//
|
||||
// iterates all pre-built closures for program
|
||||
//
|
||||
void forEachLaunchClosure(void (^handler)(const char* executableRuntimePath, const dyld3::closure::LaunchClosure* closure)) const;
|
||||
|
||||
|
||||
//
|
||||
// iterates all pre-built Image* for OS dylibs/bundles not in dyld cache
|
||||
//
|
||||
void forEachDlopenImage(void (^handler)(const char* runtimePath, const dyld3::closure::Image* image)) const;
|
||||
|
||||
|
||||
//
|
||||
// returns the ImageArray pointer to Images in dyld shared cache
|
||||
//
|
||||
const dyld3::closure::ImageArray* cachedDylibsImageArray() const;
|
||||
|
||||
|
||||
//
|
||||
// returns the ImageArray pointer to Images in OS with pre-build dlopen closure
|
||||
//
|
||||
const dyld3::closure::ImageArray* otherOSImageArray() const;
|
||||
|
||||
|
||||
//
|
||||
// searches cache for pre-built dlopen closure for OS dylib/bundle
|
||||
//
|
||||
const dyld3::closure::Image* findDlopenOtherImage(const char* path) const;
|
||||
|
||||
//
|
||||
// Returns the pointer to the slide info for this cache
|
||||
//
|
||||
const dyld_cache_slide_info* slideInfo() const;
|
||||
|
||||
//
|
||||
// Returns a pointer to the start of the __DATA region in the cache
|
||||
//
|
||||
const uint8_t* dataRegionStart() const;
|
||||
|
||||
//
|
||||
// Returns a pointer to the shared cache optimized Objective-C data structures
|
||||
//
|
||||
const objc_opt::objc_opt_t* objcOpt() const;
|
||||
|
||||
//
|
||||
// Returns a pointer to the shared cache optimized Objective-C pointer structures
|
||||
//
|
||||
const void* objcOptPtrs() const;
|
||||
|
||||
|
||||
//
|
||||
// returns true if the offset is in the TEXT of some cached dylib and sets *index to the dylib index
|
||||
//
|
||||
bool addressInText(uint32_t cacheOffset, uint32_t* index) const;
|
||||
|
||||
uint32_t patchableExportCount(uint32_t imageIndex) const;
|
||||
void forEachPatchableExport(uint32_t imageIndex, void (^handler)(uint32_t cacheOffsetOfImpl, const char* exportName)) const;
|
||||
void forEachPatchableUseOfExport(uint32_t imageIndex, uint32_t cacheOffsetOfImpl,
|
||||
void (^handler)(dyld_cache_patchable_location patchLocation)) const;
|
||||
|
||||
// Helper to get the addend for a patch location since we don't want to put C++ in the shared cache format header
|
||||
static uint64_t getAddend(const dyld_cache_patchable_location& loc) {
|
||||
uint64_t unsingedAddend = loc.addend;
|
||||
int64_t signedAddend = (int64_t)unsingedAddend;
|
||||
signedAddend = (signedAddend << 52) >> 52;
|
||||
return (uint64_t)signedAddend;
|
||||
}
|
||||
// Helper to get the key nam for a patch location since we don't want to put C++ in the shared cache format header
|
||||
static const char* keyName(const dyld_cache_patchable_location& patchLocation) {
|
||||
dyld3::MachOLoaded::ChainedFixupPointerOnDisk dummy;
|
||||
dummy.arm64e.authRebase.auth = 1;
|
||||
dummy.arm64e.authRebase.bind = 0;
|
||||
dummy.arm64e.authRebase.key = patchLocation.key;
|
||||
return dummy.arm64e.keyName();
|
||||
}
|
||||
|
||||
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
// MRM map file generator
|
||||
std::string generateJSONMap(const char* disposition) const;
|
||||
|
||||
// This generates a JSON representation of deep reverse dependency information in the cache.
|
||||
// For each dylib, the output will contain the list of all the other dylibs transitively
|
||||
// dependening on that library. (For example, the entry for libsystem will contain almost
|
||||
// all of the dylibs in the cache ; a very high-level framework such as ARKit will have way
|
||||
// fewer dependents).
|
||||
// This is used by the shared cache ordering script to put "deep" dylibs used by everybody
|
||||
// closer to the center of the cache.
|
||||
std::string generateJSONDependents() const;
|
||||
#endif
|
||||
|
||||
dyld_cache_header header;
|
||||
|
||||
private:
|
||||
// Returns a variable of type "const T" which corresponds to the header field with the given unslid address
|
||||
template<typename T>
|
||||
const T getAddrField(uint64_t addr) const;
|
||||
|
||||
#if !(BUILDING_LIBDYLD || BUILDING_DYLD)
|
||||
void fillMachOAnalyzersMap(std::unordered_map<std::string,dyld3::MachOAnalyzer*> & dylibAnalyzers) const;
|
||||
void computeReverseDependencyMapForDylib(std::unordered_map<std::string, std::set<std::string>> &reverseDependencyMap, const std::unordered_map<std::string,dyld3::MachOAnalyzer*> & dylibAnalyzers, const std::string &loadPath) const;
|
||||
void computeReverseDependencyMap(std::unordered_map<std::string, std::set<std::string>> &reverseDependencyMap) const;
|
||||
void findDependentsRecursively(std::unordered_map<std::string, std::set<std::string>> &transitiveDependents, const std::unordered_map<std::string, std::set<std::string>> &reverseDependencyMap, std::set<std::string> & visited, const std::string &loadPath) const;
|
||||
void computeTransitiveDependents(std::unordered_map<std::string, std::set<std::string>> & transitiveDependents) const;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* DyldSharedCache_h */
|
163
dyld3/shared-cache/FileAbstraction.hpp
Normal file
163
dyld3/shared-cache/FileAbstraction.hpp
Normal file
@ -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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
#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<LittleEndian>
|
||||
//
|
||||
//
|
||||
// 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<<bitCount)-1)); }
|
||||
static void setBitsRaw(uint32_t& into, uint32_t value,
|
||||
uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
|
||||
const uint32_t mask = ((1<<bitCount)-1);
|
||||
temp &= ~(mask << (32-firstBit-bitCount));
|
||||
temp |= ((value & mask) << (32-firstBit-bitCount));
|
||||
into = temp; }
|
||||
enum { little_endian = 0 };
|
||||
};
|
||||
|
||||
|
||||
class LittleEndian
|
||||
{
|
||||
public:
|
||||
static uint16_t get16(const uint16_t& from) INLINE { return OSReadLittleInt16(&from, 0); }
|
||||
static void set16(uint16_t& into, uint16_t value) INLINE { OSWriteLittleInt16(&into, 0, value); }
|
||||
|
||||
static uint32_t get32(const uint32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
|
||||
static void set32(uint32_t& into, uint32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
|
||||
|
||||
static int32_t get32(const int32_t& from) INLINE { return OSReadLittleInt32(&from, 0); }
|
||||
static void set32(int32_t& into, int32_t value) INLINE { OSWriteLittleInt32(&into, 0, value); }
|
||||
|
||||
static uint64_t get64(const uint64_t& from) INLINE { return OSReadLittleInt64(&from, 0); }
|
||||
static void set64(uint64_t& into, uint64_t value) INLINE { OSWriteLittleInt64(&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 >> firstBit) & ((1<<bitCount)-1)); }
|
||||
static void setBitsRaw(uint32_t& into, uint32_t value,
|
||||
uint8_t firstBit, uint8_t bitCount) INLINE { uint32_t temp = into;
|
||||
const uint32_t mask = ((1<<bitCount)-1);
|
||||
temp &= ~(mask << firstBit);
|
||||
temp |= ((value & mask) << firstBit);
|
||||
into = temp; }
|
||||
enum { little_endian = 1 };
|
||||
};
|
||||
|
||||
|
||||
template <typename _E>
|
||||
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 <typename T>
|
||||
static T round_up(T value) { return (value+3) & ~(T)3; }
|
||||
template <typename T>
|
||||
static T round_down(T value) { return value & ~(T)3; }
|
||||
};
|
||||
|
||||
|
||||
template <typename _E>
|
||||
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 <typename T>
|
||||
static T round_up(T value) { return (value+7) & ~(T)7; }
|
||||
template <typename T>
|
||||
static T round_down(T value) { return value & ~(T)7; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // __FILE_ABSTRACTION__
|
||||
|
||||
|
594
dyld3/shared-cache/FileUtils.cpp
Normal file
594
dyld3/shared-cache/FileUtils.cpp
Normal file
@ -0,0 +1,594 @@
|
||||
/*
|
||||
* 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 <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mman.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <System/sys/csr.h>
|
||||
#ifndef DARLING
|
||||
#include <rootless.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include "FileUtils.h"
|
||||
#include "StringUtils.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
|
||||
|
||||
#ifdef DARLING
|
||||
static int rootless_check_trusted(const char* path) { return -1; }
|
||||
static int rootless_check_trusted_class(const char* path, const char* cls) { return -1; }
|
||||
#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, bool recurse)
|
||||
{
|
||||
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;
|
||||
if (recurse)
|
||||
iterateDirectoryTree(pathPrefix, dirAndFile, dirFilter, fileCallback, processFiles, true);
|
||||
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 ( (size_t)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 char* path, size_t& mappedSize)
|
||||
{
|
||||
struct stat statBuf;
|
||||
if ( ::stat(path, &statBuf) != 0 )
|
||||
return nullptr;
|
||||
|
||||
int fd = ::open(path, 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 isProtectedBySIPExceptDyld(const std::string& path)
|
||||
{
|
||||
if ( !sipIsEnabled() )
|
||||
return false;
|
||||
|
||||
return (rootless_check_trusted_class(path.c_str(), "dyld") == 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<std::string, uint32_t> parseOrderFile(const std::string& orderFileData) {
|
||||
std::unordered_map<std::string, uint32_t> order;
|
||||
|
||||
if (orderFileData.empty())
|
||||
return order;
|
||||
|
||||
std::stringstream myData(orderFileData);
|
||||
|
||||
uint32_t count = 0;
|
||||
std::string line;
|
||||
while ( std::getline(myData, 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++;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
std::string loadOrderFile(const std::string& orderFilePath) {
|
||||
std::string order;
|
||||
|
||||
size_t size = 0;
|
||||
char* data = (char*)mapFileReadOnly(orderFilePath.c_str(), size);
|
||||
if (data) {
|
||||
order = std::string(data, size);
|
||||
::munmap((void*)data, size);
|
||||
}
|
||||
|
||||
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<std::string> components;
|
||||
std::vector<std::string> 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));
|
||||
}
|
||||
|
||||
std::pair<uint8_t*, struct stat> FileCache::cacheLoad(Diagnostics& diags, const std::string path)
|
||||
{
|
||||
__block bool found = false;
|
||||
__block std::pair<uint8_t*, struct stat> 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<uint8_t*, struct stat> 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);
|
||||
}
|
||||
|
||||
static void normalizePath(std::string& path) {
|
||||
// Remove a bunch of stuff we don't need, like trailing slashes.
|
||||
while ( !path.empty() && (path.back() == '/'))
|
||||
path.pop_back();
|
||||
}
|
||||
|
||||
void SymlinkResolver::addFile(Diagnostics& diags, std::string path) {
|
||||
if (path.front() != '/') {
|
||||
diags.error("Path must start with '/'");
|
||||
return;
|
||||
}
|
||||
if (symlinks.find(path) != symlinks.end()) {
|
||||
diags.error("Cannot add regular file as it is already a symlink");
|
||||
return;
|
||||
}
|
||||
filePaths.insert(path);
|
||||
}
|
||||
|
||||
void SymlinkResolver::addSymlink(Diagnostics& diags, std::string fromPath, std::string toPath) {
|
||||
normalizePath(fromPath);
|
||||
normalizePath(toPath);
|
||||
if (fromPath.front() != '/') {
|
||||
diags.error("Path must start with '/'");
|
||||
return;
|
||||
}
|
||||
if (filePaths.find(fromPath) != filePaths.end()) {
|
||||
diags.error("Cannot add symlink from '%s' as it is already a regular path", fromPath.c_str());
|
||||
return;
|
||||
}
|
||||
auto itAndInserted = symlinks.insert({ fromPath, toPath });
|
||||
if (!itAndInserted.second) {
|
||||
// The path is already a symlink. Make sure its a dupe.
|
||||
if (toPath != itAndInserted.first->second) {
|
||||
diags.error("Duplicate symlink for path '%s'", fromPath.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string SymlinkResolver::realPath(Diagnostics& diags, const std::string& originalPath) const {
|
||||
// First make sure the path doesn't have any magic in it.
|
||||
std::string path = originalPath;
|
||||
normalizePath(path);
|
||||
|
||||
std::set<std::string> seenSymlinks;
|
||||
|
||||
// Now see if any prefix is a symlink
|
||||
if (path.front() != '/')
|
||||
return path;
|
||||
|
||||
std::string::size_type prev_pos = 0;
|
||||
while (prev_pos != std::string::npos) {
|
||||
std::string::size_type pos = path.find("/", prev_pos + 1);
|
||||
|
||||
// First look to see if this path component is special, eg, ., .., etc.
|
||||
std::string component = path.substr(prev_pos, pos - prev_pos);
|
||||
if (component == "/..") {
|
||||
// Fold with the previous path component.
|
||||
if (prev_pos == 0) {
|
||||
// This is the root path, and .. applied to / is just /
|
||||
path = path.substr(3);
|
||||
prev_pos = 0;
|
||||
} else {
|
||||
std::string::size_type lastSlashPos = path.rfind("/", prev_pos - 1);
|
||||
path = path.substr(0, lastSlashPos) + path.substr(pos);
|
||||
prev_pos = lastSlashPos;
|
||||
}
|
||||
continue;
|
||||
} else if (component == "/.") {
|
||||
if (prev_pos == 0) {
|
||||
// Path starts with /./ so just remove the first one.
|
||||
path = path.substr(2);
|
||||
} else {
|
||||
if (pos == std::string::npos) {
|
||||
// Trailing . on the path
|
||||
path = path.substr(0, prev_pos );
|
||||
} else {
|
||||
path = path.substr(0, prev_pos) + path.substr(pos);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else if (component == "/") {
|
||||
// Path must contain // somewhere so strip out the duplicates.
|
||||
if (prev_pos == 0) {
|
||||
// Path starts with // so just remove the first one.
|
||||
path = path.substr(1);
|
||||
} else {
|
||||
if (pos == std::string::npos) {
|
||||
// Trailing / on the path
|
||||
path = path.substr(0, prev_pos);
|
||||
prev_pos = pos;
|
||||
} else {
|
||||
path = path.substr(0, pos) + path.substr(pos + 1);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Path is not special, so see if it is a symlink to something.
|
||||
std::string prefix = path.substr(0, pos);
|
||||
//printf("%s\n", prefix.c_str());
|
||||
auto it = symlinks.find(prefix);
|
||||
if (it == symlinks.end()) {
|
||||
// This is not a symlink so move to the next prefix.
|
||||
prev_pos = pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we've already done this prefix then error out.
|
||||
if (seenSymlinks.count(prefix)) {
|
||||
diags.error("Loop in symlink processing for '%s'", originalPath.c_str());
|
||||
return std::string();
|
||||
}
|
||||
|
||||
seenSymlinks.insert(prefix);
|
||||
|
||||
// This is a symlink, so resolve the new path.
|
||||
std::string toPath = it->second;
|
||||
if (toPath.front() == '/') {
|
||||
// Symlink points to an absolute address so substitute the whole prefix for the new path
|
||||
// If we didn't substitute the last component of the path then there is also a path suffix.
|
||||
std::string pathSuffix = "";
|
||||
if (pos != std::string::npos) {
|
||||
std::string::size_type nextSlashPos = path.find("/", pos + 1);
|
||||
if (nextSlashPos != std::string::npos)
|
||||
pathSuffix = path.substr(nextSlashPos);
|
||||
}
|
||||
path = toPath + pathSuffix;
|
||||
prev_pos = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Symlink points to a relative path so we need to do more processing to get the real path.
|
||||
|
||||
// First calculate which part of the previous prefix we'll keep. Eg, in /a/b/c where "b -> blah", we want to keep /a here.
|
||||
std::string prevPrefix = path.substr(0, prev_pos);
|
||||
//printf("prevPrefix %s\n", prevPrefix.c_str());
|
||||
|
||||
// If we didn't substitute the last component of the path then there is also a path suffix.
|
||||
std::string pathSuffix = "";
|
||||
if (prefix.size() != path.size())
|
||||
pathSuffix = path.substr(pos);
|
||||
|
||||
// The new path is the remaining prefix, plus the symlink target, plus any remaining suffix from the original path.
|
||||
path = prevPrefix + "/" + toPath + pathSuffix;
|
||||
prev_pos = 0;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<DyldSharedCache::FileAlias> SymlinkResolver::getResolvedSymlinks(Diagnostics& diags) {
|
||||
diags.assertNoError();
|
||||
std::vector<DyldSharedCache::FileAlias> aliases;
|
||||
for (auto& fromPathAndToPath : symlinks) {
|
||||
std::string newPath = realPath(diags, fromPathAndToPath.first);
|
||||
if (diags.hasError()) {
|
||||
aliases.clear();
|
||||
return aliases;
|
||||
}
|
||||
|
||||
if (filePaths.count(newPath)) {
|
||||
aliases.push_back({ newPath, fromPathAndToPath.first });
|
||||
// printf("symlink ('%s' -> '%s') resolved to '%s'\n", fromPathAndToPath.first.c_str(), fromPathAndToPath.second.c_str(), newPath.c_str());
|
||||
}
|
||||
}
|
||||
return aliases;
|
||||
}
|
||||
|
||||
#endif // BUILDING_CACHE_BUILDER
|
||||
|
113
dyld3/shared-cache/FileUtils.h
Normal file
113
dyld3/shared-cache/FileUtils.h
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
#include "DyldSharedCache.h"
|
||||
|
||||
class Diagnostics;
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
struct FileCache {
|
||||
FileCache(void);
|
||||
std::pair<uint8_t*, struct stat> cacheLoad(Diagnostics& diags, const std::string path);
|
||||
|
||||
private:
|
||||
std::pair<uint8_t*, struct stat> fill(Diagnostics& diags, const std::string& path);
|
||||
|
||||
std::unordered_map<std::string, std::pair<uint8_t*, struct stat>> 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, bool recurse=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 char* path, size_t& mappedSize);
|
||||
|
||||
bool isProtectedBySIP(const std::string& path);
|
||||
bool isProtectedBySIPExceptDyld(const std::string& path);
|
||||
bool isProtectedBySIP(int fd);
|
||||
|
||||
bool fileExists(const std::string& path);
|
||||
|
||||
std::unordered_map<std::string, uint32_t> parseOrderFile(const std::string& orderFileData);
|
||||
std::string loadOrderFile(const std::string& orderFilePath);
|
||||
|
||||
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();
|
||||
|
||||
#if BUILDING_CACHE_BUILDER
|
||||
class SymlinkResolver {
|
||||
public:
|
||||
SymlinkResolver() { }
|
||||
|
||||
void addFile(Diagnostics& diags, std::string path);
|
||||
|
||||
void addSymlink(Diagnostics& diags, std::string fromPath, std::string toPath);
|
||||
|
||||
std::string realPath(Diagnostics& diags, const std::string& path) const;
|
||||
|
||||
std::vector<DyldSharedCache::FileAlias> getResolvedSymlinks(Diagnostics& diags);
|
||||
|
||||
private:
|
||||
std::set<std::string> filePaths;
|
||||
std::map<std::string, std::string> symlinks;
|
||||
};
|
||||
#endif // BUILDING_CACHE_BUILDER
|
||||
|
||||
|
||||
#endif // FileUtils_h
|
897
dyld3/shared-cache/MachOFileAbstraction.hpp
Normal file
897
dyld3/shared-cache/MachOFileAbstraction.hpp
Normal file
@ -0,0 +1,897 @@
|
||||
/* -*- 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 <mach-o/loader.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <mach-o/reloc.h>
|
||||
#include <mach/machine.h>
|
||||
|
||||
// suport older versions of mach-o/loader.h
|
||||
|
||||
|
||||
|
||||
#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 DYLD_CACHE_ADJ_V2_THREADED_POINTER_64 0x0D
|
||||
|
||||
#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 <typename P>
|
||||
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 <typename P> struct macho_segment_content {};
|
||||
template <> struct macho_segment_content<Pointer32<BigEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
|
||||
template <> struct macho_segment_content<Pointer64<BigEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
|
||||
template <> struct macho_segment_content<Pointer32<LittleEndian> > { segment_command fields; enum { CMD = LC_SEGMENT }; };
|
||||
template <> struct macho_segment_content<Pointer64<LittleEndian> > { segment_command_64 fields; enum { CMD = LC_SEGMENT_64 }; };
|
||||
|
||||
template <typename P>
|
||||
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<P>::CMD
|
||||
};
|
||||
|
||||
typedef typename P::E E;
|
||||
private:
|
||||
macho_segment_content<P> segment;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// mach-o section
|
||||
//
|
||||
template <typename P> struct macho_section_content {};
|
||||
template <> struct macho_section_content<Pointer32<BigEndian> > { section fields; };
|
||||
template <> struct macho_section_content<Pointer64<BigEndian> > { section_64 fields; };
|
||||
template <> struct macho_section_content<Pointer32<LittleEndian> > { section fields; };
|
||||
template <> struct macho_section_content<Pointer64<LittleEndian> > { section_64 fields; };
|
||||
|
||||
template <typename P>
|
||||
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<P> section;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// mach-o dylib load command
|
||||
//
|
||||
template <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P> struct macho_routines_content {};
|
||||
template <> struct macho_routines_content<Pointer32<BigEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
|
||||
template <> struct macho_routines_content<Pointer64<BigEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
|
||||
template <> struct macho_routines_content<Pointer32<LittleEndian> > { routines_command fields; enum { CMD = LC_ROUTINES }; };
|
||||
template <> struct macho_routines_content<Pointer64<LittleEndian> > { routines_command_64 fields; enum { CMD = LC_ROUTINES_64 }; };
|
||||
|
||||
template <typename P>
|
||||
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<P>::CMD
|
||||
};
|
||||
private:
|
||||
macho_routines_content<P> routines;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// mach-o symbol table load command
|
||||
//
|
||||
template <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P> struct macho_nlist_content {};
|
||||
template <> struct macho_nlist_content<Pointer32<BigEndian> > { struct nlist fields; };
|
||||
template <> struct macho_nlist_content<Pointer64<BigEndian> > { struct nlist_64 fields; };
|
||||
template <> struct macho_nlist_content<Pointer32<LittleEndian> > { struct nlist fields; };
|
||||
template <> struct macho_nlist_content<Pointer64<LittleEndian> > { struct nlist_64 fields; };
|
||||
|
||||
template <typename P>
|
||||
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<P> entry;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// mach-o relocation info
|
||||
//
|
||||
template <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P> struct macho_header_content {};
|
||||
template <> struct macho_header_content<Pointer32<BigEndian> > { mach_header fields; };
|
||||
template <> struct macho_header_content<Pointer64<BigEndian> > { mach_header_64 fields; };
|
||||
template <> struct macho_header_content<Pointer32<LittleEndian> > { mach_header fields; };
|
||||
template <> struct macho_header_content<Pointer64<LittleEndian> > { mach_header_64 fields; };
|
||||
|
||||
template <typename P>
|
||||
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<P>* getSegment(const char *segname) const
|
||||
{
|
||||
const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
|
||||
uint32_t cmd_count = this->ncmds();
|
||||
const macho_load_command<P>* cmd = cmds;
|
||||
for (uint32_t i = 0; i < cmd_count; ++i) {
|
||||
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
|
||||
const macho_segment_command<P>* segcmd = (macho_segment_command<P>*)cmd;
|
||||
if (0 == strncmp(segname, segcmd->segname(), 16)) {
|
||||
return segcmd;
|
||||
}
|
||||
}
|
||||
cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const macho_section<P>* getSection(const char *segname, const char *sectname) const
|
||||
{
|
||||
const macho_segment_command<P>* segcmd = getSegment(segname);
|
||||
if (!segcmd) return NULL;
|
||||
|
||||
const macho_section<P>* sectcmd = (macho_section<P>*)(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<P>* getLoadCommand(int query) const
|
||||
{
|
||||
const macho_load_command<P>* cmds = (macho_load_command<P>*)((uint8_t*)this + sizeof(macho_header<P>));
|
||||
uint32_t cmd_count = this->ncmds();
|
||||
const macho_load_command<P>* cmd = cmds;
|
||||
for (uint32_t i = 0; i < cmd_count; ++i) {
|
||||
if ( cmd->cmd() == query ) {
|
||||
return cmd;
|
||||
}
|
||||
cmd = (macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef typename P::E E;
|
||||
private:
|
||||
macho_header_content<P> header;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// compressed dyld info load command
|
||||
//
|
||||
template <typename P>
|
||||
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 |= (~0ULL) << bit;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __MACH_O_FILE_ABSTRACTION__
|
||||
|
||||
|
264
dyld3/shared-cache/Manifest.h
Normal file
264
dyld3/shared-cache/Manifest.h
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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 <map>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <assert.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "DyldSharedCache.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "MachOAnalyzer.h"
|
||||
#include "ClosureFileSystemPhysical.h"
|
||||
|
||||
extern std::string toolDir();
|
||||
|
||||
namespace dyld3 {
|
||||
|
||||
struct VIS_HIDDEN UUID {
|
||||
UUID() {}
|
||||
UUID(const UUID& other) { uuid_copy(_bytes, other._bytes); }
|
||||
UUID(const uuid_t other) { uuid_copy(&_bytes[0], other); }
|
||||
UUID(const dyld3::MachOAnalyzer* ml) { ml->getUuid(_bytes); }
|
||||
bool operator<(const UUID& other) const { return uuid_compare(_bytes, other._bytes) < 0; }
|
||||
bool operator==(const UUID& other) const { return uuid_compare(_bytes, other._bytes) == 0; }
|
||||
bool operator!=(const UUID& other) const { return !(*this == other); }
|
||||
|
||||
size_t hash() const
|
||||
{
|
||||
size_t retval = 0;
|
||||
for (size_t i = 0; i < (16 / sizeof(size_t)); ++i) {
|
||||
retval ^= ((size_t*)(&_bytes[0]))[i];
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
const unsigned char* get() const { return _bytes; };
|
||||
|
||||
private:
|
||||
uuid_t _bytes;
|
||||
};
|
||||
|
||||
struct BuildQueueEntry {
|
||||
DyldSharedCache::CreateOptions options;
|
||||
dyld3::closure::FileSystemPhysical fileSystem;
|
||||
std::vector<DyldSharedCache::MappedMachO> dylibsForCache;
|
||||
std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles;
|
||||
std::vector<DyldSharedCache::MappedMachO> mainExecutables;
|
||||
std::string outputPath;
|
||||
std::set<std::string> configNames;
|
||||
};
|
||||
|
||||
struct Manifest {
|
||||
struct UUIDInfo {
|
||||
const MachOAnalyzer* mh;
|
||||
uint64_t sliceFileOffset;
|
||||
std::size_t size;
|
||||
std::string runtimePath;
|
||||
std::string buildPath;
|
||||
std::string installName;
|
||||
std::string arch;
|
||||
UUID uuid;
|
||||
UUIDInfo(const MachOAnalyzer* 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<std::string> sources;
|
||||
};
|
||||
|
||||
|
||||
struct SegmentInfo {
|
||||
std::string name;
|
||||
uint64_t startAddr;
|
||||
uint64_t endAddr;
|
||||
};
|
||||
|
||||
struct CacheInfo {
|
||||
std::vector<SegmentInfo> regions;
|
||||
std::string cdHash;
|
||||
};
|
||||
|
||||
struct CacheImageInfo {
|
||||
bool included;
|
||||
std::string exclusionInfo;
|
||||
UUID uuid;
|
||||
std::string installname;
|
||||
std::vector<SegmentInfo> segments;
|
||||
CacheImageInfo(void)
|
||||
: included(true)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct Results {
|
||||
std::string failure;
|
||||
std::map<UUID, CacheImageInfo> dylibs;
|
||||
std::map<UUID, CacheImageInfo> bundles;
|
||||
std::map<UUID, CacheImageInfo> executables;
|
||||
|
||||
std::set<std::string> warnings;
|
||||
CacheInfo developmentCache;
|
||||
CacheInfo productionCache;
|
||||
CacheImageInfo& dylibForInstallname(const std::string& installname);
|
||||
void exclude(const dyld3::MachOAnalyzer* ml, 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<std::string> metabomTags;
|
||||
std::set<std::string> metabomExcludeTags;
|
||||
std::set<std::string> metabomRestrictTags;
|
||||
std::set<std::string> restrictedInstallnames;
|
||||
std::map<std::string, Architecture> 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<void(const std::string& archName)> lambda) const;
|
||||
};
|
||||
|
||||
const std::map<std::string, Project>& projects();
|
||||
const Configuration& configuration(const std::string& configuration) const;
|
||||
void forEachConfiguration(std::function<void(const std::string& configName)> 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, bool populateIt = true);
|
||||
void populate(const std::set<std::string>& overlays);
|
||||
|
||||
BuildQueueEntry makeQueueEntry(const std::string& outputPath, const std::set<std::string>& configs, const std::string& arch, bool optimizeStubs, const std::string& prefix,
|
||||
bool isLocallyBuiltCache, bool skipWrites, bool verbose);
|
||||
|
||||
void write(const std::string& path);
|
||||
void writeJSON(const std::string& path);
|
||||
void canonicalize(void);
|
||||
void calculateClosure();
|
||||
const MachOAnalyzer* machOForUUID(const UUID& uuid) const;
|
||||
const std::string buildPathForUUID(const UUID& uuid);
|
||||
const std::string runtimePathForUUID(const UUID& uuid);
|
||||
const std::string& installNameForUUID(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);
|
||||
void runConcurrently(dispatch_queue_t queue, dispatch_semaphore_t concurrencyLimitingSemaphore, std::function<void(const std::string configuration, const std::string architecture)> lambda);
|
||||
bool filterForConfig(const std::string& configName);
|
||||
std::set<std::string> resultsForConfiguration(const std::string& configName);
|
||||
|
||||
// These are used by MRM to support having the Manifest give us a list of files/symlinks from the BOM but we use MRM for the actual cache generation
|
||||
void forEachMachO(std::string configuration, std::function<void(const std::string &buildPath, const std::string &runtimePath, const std::string &arch, bool shouldBeExcludedIfLeaf)> lambda);
|
||||
|
||||
void forEachSymlink(std::string configuration, std::function<void(const std::string &fromPath, const std::string &toPath)> lambda);
|
||||
|
||||
private:
|
||||
NSDictionary* _manifestDict;
|
||||
Diagnostics& _diags;
|
||||
std::map<UUID, UUIDInfo> _uuidMap;
|
||||
std::map<std::pair<std::string, std::string>, UUID> _installNameMap;
|
||||
std::vector<std::pair<std::string, std::string>> _symlinks;
|
||||
static dispatch_queue_t _identifierQueue;
|
||||
uint32_t _manifestVersion;
|
||||
std::string _build;
|
||||
std::string _dylibOrderFile;
|
||||
std::string _dirtyDataOrderFile;
|
||||
std::string _metabomFile;
|
||||
Platform _platform;
|
||||
std::map<std::string, Project> _projects;
|
||||
std::map<std::string, Configuration> _configurations;
|
||||
std::map<std::string, std::set<std::string>> _metabomTagMap;
|
||||
std::map<std::string, std::set<std::string>> _metabomSymlinkTagMap;
|
||||
std::map<std::string, std::set<std::string>> _metabomExcludeTagMap;
|
||||
std::map<std::string, std::set<std::string>> _metabomRestrictedTagMap;
|
||||
|
||||
std::vector<DyldSharedCache::MappedMachO> dylibsForCache(const std::string& configuration, const std::string& architecture);
|
||||
std::vector<DyldSharedCache::MappedMachO> otherDylibsAndBundles(const std::string& configuration, const std::string& architecture);
|
||||
std::vector<DyldSharedCache::MappedMachO> 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<DyldSharedCache::MappedMachO>& mappedMachOs, const CacheImageInfo& imageInfo);
|
||||
bool loadParser(const void* p, size_t sliceLength, uint64_t sliceOffset, const std::string& runtimePath, const std::string& buildPath, const std::set<std::string>& architectures);
|
||||
bool loadParsers(const std::string& pathToMachO, const std::string& runtimePath, const std::set<std::string>& architectures);
|
||||
void dedupeDispositions();
|
||||
void calculateClosure(const std::string& configuration, const std::string& architecture);
|
||||
void canonicalizeDylib(const std::string& installname);
|
||||
template <typename P>
|
||||
void canonicalizeDylib(const std::string& installname, const uint8_t* p);
|
||||
void addImplicitAliases(void);
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<dyld3::UUID> {
|
||||
size_t operator()(const dyld3::UUID& x) const
|
||||
{
|
||||
return x.hash();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* Manifest_h */
|
1227
dyld3/shared-cache/Manifest.mm
Normal file
1227
dyld3/shared-cache/Manifest.mm
Normal file
File diff suppressed because it is too large
Load Diff
231
dyld3/shared-cache/ObjC1Abstraction.hpp
Normal file
231
dyld3/shared-cache/ObjC1Abstraction.hpp
Normal file
@ -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 <typename P>
|
||||
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 <typename P>
|
||||
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 <typename P>
|
||||
struct objc_method_list {
|
||||
enum { OBJC_FIXED_UP = 1771 };
|
||||
uint32_t obsolete; // struct objc_method_list *
|
||||
uint32_t method_count; // int
|
||||
struct objc_method<P> 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 <typename P>
|
||||
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<P> *getIsa(ContentAccessor* cache) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(isa)); }
|
||||
struct objc_method_list<P> *getMethodList(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(methodList)); }
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
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<P> *getInstanceMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
|
||||
struct objc_method_list<P> *getClassMethods(ContentAccessor* cache) const INLINE { return (struct objc_method_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
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<P> *getClass(ContentAccessor* cache, int index) const INLINE { return (struct objc_class<P> *)cache->contentForVMAddr(P::E::get32(defs[index])); }
|
||||
struct objc_category<P> *getCategory(ContentAccessor* cache, int index) const INLINE { return (struct objc_category<P> *)cache->contentForVMAddr(P::E::get32(defs[getClassCount() + index])); }
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
struct objc_module {
|
||||
uint32_t version; // unsigned long
|
||||
uint32_t size; // unsigned long
|
||||
uint32_t name; // char*
|
||||
uint32_t symtab; // Symtab
|
||||
|
||||
struct objc_symtab<P> *getSymtab(ContentAccessor* cache) const INLINE { return (struct objc_symtab<P> *)cache->contentForVMAddr(P::E::get32(symtab)); }
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
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 <typename P>
|
||||
struct objc_method_description_list {
|
||||
uint32_t count; // int
|
||||
struct objc_method_description<P> list[0];
|
||||
|
||||
uint32_t getCount() const INLINE { return P::E::get32(count); }
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
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<P> *getInstanceMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(instance_methods)); }
|
||||
struct objc_method_description_list<P> *getClassMethodDescriptions(ContentAccessor* cache) const INLINE { return (struct objc_method_description_list<P> *)cache->contentForVMAddr(P::E::get32(class_methods)); }
|
||||
};
|
||||
|
||||
|
||||
template <typename P, typename V>
|
||||
class LegacySelectorUpdater {
|
||||
typedef typename P::uint_t pint_t;
|
||||
|
||||
static void visitMethodList(objc_method_list<P> *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<P> *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<P>* header, V& visitor)
|
||||
{
|
||||
ArraySection<P, objc_module<P> >
|
||||
modules(cache, header, "__OBJC", "__module_info");
|
||||
for (uint64_t m = 0; m < modules.count(); m++) {
|
||||
objc_symtab<P> *symtab = modules.get(m).getSymtab(cache);
|
||||
if (!symtab) continue;
|
||||
|
||||
// Method lists in classes
|
||||
for (int c = 0; c < symtab->getClassCount(); c++) {
|
||||
objc_class<P> *cls = symtab->getClass(cache, c);
|
||||
objc_class<P> *isa = cls->getIsa(cache);
|
||||
objc_method_list<P> *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<P> *cat = symtab->getCategory(cache, c);
|
||||
objc_method_list<P> *mlist;
|
||||
if ((mlist = cat->getInstanceMethods(cache))) {
|
||||
visitMethodList(mlist, visitor);
|
||||
}
|
||||
if ((mlist = cat->getClassMethods(cache))) {
|
||||
visitMethodList(mlist, visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Method description lists from protocols
|
||||
ArraySection<P, objc_protocol<P>>
|
||||
protocols(cache, header, "__OBJC", "__protocol");
|
||||
for (uint64_t p = 0; p < protocols.count(); p++) {
|
||||
objc_protocol<P>& protocol = protocols.get(p);
|
||||
objc_method_description_list<P> *mlist;
|
||||
if ((mlist = protocol.getInstanceMethodDescriptions(cache))) {
|
||||
visitMethodDescriptionList(mlist, visitor);
|
||||
}
|
||||
if ((mlist = protocol.getClassMethodDescriptions(cache))) {
|
||||
visitMethodDescriptionList(mlist, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
// Message refs
|
||||
PointerSection<P, const char *> 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);
|
||||
}
|
||||
}
|
||||
};
|
1251
dyld3/shared-cache/ObjC2Abstraction.hpp
Normal file
1251
dyld3/shared-cache/ObjC2Abstraction.hpp
Normal file
File diff suppressed because it is too large
Load Diff
973
dyld3/shared-cache/OptimizerBranches.cpp
Normal file
973
dyld3/shared-cache/OptimizerBranches.cpp
Normal file
@ -0,0 +1,973 @@
|
||||
/* -*- 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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include "Trie.hpp"
|
||||
#include "MachOFileAbstraction.hpp"
|
||||
#include "MachOAnalyzer.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "DyldSharedCache.h"
|
||||
#include "CacheBuilder.h"
|
||||
|
||||
static const bool verbose = false;
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename P>
|
||||
class StubOptimizer {
|
||||
public:
|
||||
StubOptimizer(const DyldSharedCache* cache, macho_header<P>* mh, Diagnostics& diags);
|
||||
void buildStubMap(const std::unordered_set<std::string>& neverStubEliminate);
|
||||
void optimizeStubs();
|
||||
void optimizeCallSites(std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr);
|
||||
const char* installName() { return _installName; }
|
||||
const uint8_t* exportsTrie() {
|
||||
if ( _dyldInfo != nullptr )
|
||||
return &_linkeditBias[_dyldInfo->export_off()];
|
||||
else
|
||||
return &_linkeditBias[_exportTrie->dataoff()];
|
||||
}
|
||||
uint32_t exportsTrieSize() {
|
||||
if ( _dyldInfo != nullptr )
|
||||
return _dyldInfo->export_size();
|
||||
else
|
||||
return _exportTrie->datasize();
|
||||
}
|
||||
|
||||
uint32_t _stubCount = 0;
|
||||
uint32_t _stubOptimizedCount = 0;
|
||||
uint32_t _stubsLeftInterposable = 0;
|
||||
uint32_t _branchToStubCount = 0;
|
||||
uint32_t _branchOptimizedToDirectCount = 0;
|
||||
uint32_t _branchToOptimizedStubCount = 0;
|
||||
uint32_t _branchToReUsedOptimizedStubCount = 0;
|
||||
|
||||
private:
|
||||
Diagnostics _diagnostics;
|
||||
|
||||
typedef std::function<bool(uint8_t callSiteKind, uint64_t callSiteAddr, uint64_t stubAddr, uint32_t& instruction)> CallSiteHandler;
|
||||
typedef typename P::uint_t pint_t;
|
||||
typedef typename P::E E;
|
||||
|
||||
void forEachCallSiteToAStub(CallSiteHandler);
|
||||
void optimizeArm64CallSites(std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr);
|
||||
void optimizeArm64Stubs();
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
void optimizeArm64eStubs();
|
||||
#endif
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
void optimizeArm64_32Stubs();
|
||||
#endif
|
||||
void optimizeArmCallSites(std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr);
|
||||
void optimizeArmStubs();
|
||||
uint64_t lazyPointerAddrFromArm64Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
uint64_t lazyPointerAddrFromArm64eStub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
|
||||
#endif
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
uint64_t lazyPointerAddrFromArm64_32Stub(const uint8_t* stubInstructions, uint64_t stubVMAddr);
|
||||
#endif
|
||||
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<pint_t, AddressAndName> StubVMAddrToTarget;
|
||||
|
||||
static const int64_t b128MegLimit = 0x07FFFFFF;
|
||||
static const int64_t b16MegLimit = 0x00FFFFFF;
|
||||
|
||||
|
||||
|
||||
macho_header<P>* _mh;
|
||||
int64_t _cacheSlide = 0;
|
||||
uint64_t _cacheUnslideAddr = 0;
|
||||
bool _chainedFixups = false;
|
||||
uint32_t _linkeditSize = 0;
|
||||
uint64_t _linkeditAddr = 0;
|
||||
const uint8_t* _linkeditBias = nullptr;
|
||||
const char* _installName = nullptr;
|
||||
const macho_symtab_command<P>* _symTabCmd = nullptr;
|
||||
const macho_dysymtab_command<P>* _dynSymTabCmd = nullptr;
|
||||
const macho_dyld_info_command<P>* _dyldInfo = nullptr;
|
||||
const macho_linkedit_data_command<P>* _exportTrie = nullptr;
|
||||
macho_linkedit_data_command<P>* _splitSegInfoCmd = nullptr;
|
||||
const macho_section<P>* _textSection = nullptr;
|
||||
const macho_section<P>* _stubSection = nullptr;
|
||||
uint32_t _textSectionIndex = 0;
|
||||
uint32_t _stubSectionIndex = 0;
|
||||
pint_t _textSegStartAddr = 0;
|
||||
std::vector<macho_segment_command<P>*> _segCmds;
|
||||
std::unordered_map<pint_t, pint_t> _stubAddrToLPAddr;
|
||||
std::unordered_map<pint_t, pint_t> _lpAddrToTargetAddr;
|
||||
std::unordered_map<pint_t, const char*> _targetAddrToName;
|
||||
std::unordered_set<uint64_t> _stubsToOptimize;
|
||||
};
|
||||
|
||||
|
||||
template <typename P>
|
||||
StubOptimizer<P>::StubOptimizer(const DyldSharedCache* cache, macho_header<P>* mh, Diagnostics& diags)
|
||||
: _mh(mh), _diagnostics(diags)
|
||||
{
|
||||
_cacheSlide = (long)cache - cache->unslidLoadAddress();
|
||||
_cacheUnslideAddr = cache->unslidLoadAddress();
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
_chainedFixups = (strcmp(cache->archName(), "arm64e") == 0);
|
||||
#else
|
||||
_chainedFixups = false;
|
||||
#endif
|
||||
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
|
||||
const uint32_t cmd_count = mh->ncmds();
|
||||
macho_segment_command<P>* segCmd;
|
||||
uint32_t sectionIndex = 0;
|
||||
const macho_load_command<P>* cmd = cmds;
|
||||
for (uint32_t i = 0; i < cmd_count; ++i) {
|
||||
switch (cmd->cmd()) {
|
||||
case LC_ID_DYLIB:
|
||||
_installName = ((macho_dylib_command<P>*)cmd)->name();
|
||||
break;
|
||||
case LC_SYMTAB:
|
||||
_symTabCmd = (macho_symtab_command<P>*)cmd;
|
||||
break;
|
||||
case LC_DYSYMTAB:
|
||||
_dynSymTabCmd = (macho_dysymtab_command<P>*)cmd;
|
||||
break;
|
||||
case LC_SEGMENT_SPLIT_INFO:
|
||||
_splitSegInfoCmd = (macho_linkedit_data_command<P>*)cmd;
|
||||
break;
|
||||
case LC_DYLD_INFO:
|
||||
case LC_DYLD_INFO_ONLY:
|
||||
_dyldInfo = (macho_dyld_info_command<P>*)cmd;
|
||||
break;
|
||||
case LC_DYLD_EXPORTS_TRIE:
|
||||
_exportTrie = (macho_linkedit_data_command<P>*)cmd;
|
||||
break;
|
||||
case macho_segment_command<P>::CMD:
|
||||
segCmd =( macho_segment_command<P>*)cmd;
|
||||
_segCmds.push_back(segCmd);
|
||||
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
|
||||
_linkeditBias = (uint8_t*)(segCmd->vmaddr() + _cacheSlide - segCmd->fileoff());
|
||||
_linkeditSize = (uint32_t)segCmd->vmsize();
|
||||
_linkeditAddr = segCmd->vmaddr();
|
||||
}
|
||||
else if ( strcmp(segCmd->segname(), "__TEXT") == 0 ) {
|
||||
_textSegStartAddr = (pint_t)segCmd->vmaddr();
|
||||
const macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)segCmd + sizeof(macho_segment_command<P>));
|
||||
const macho_section<P>* const sectionsEnd = §ionsStart[segCmd->nsects()];
|
||||
for (const macho_section<P>* 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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename P>
|
||||
uint32_t StubOptimizer<P>::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 <typename P>
|
||||
uint64_t StubOptimizer<P>::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;
|
||||
}
|
||||
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
template <typename P>
|
||||
uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64_32Stub(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) != 0xB9400210 ) {
|
||||
_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*4; // LDR Wn has a scale factor of 4
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
template <typename P>
|
||||
uint64_t StubOptimizer<P>::lazyPointerAddrFromArm64eStub(const uint8_t* stubInstructions, uint64_t stubVMAddr)
|
||||
{
|
||||
uint32_t stubInstr1 = E::get32(*(uint32_t*)stubInstructions);
|
||||
// ADRP X17, dyld_mageLoaderCache@page
|
||||
if ( (stubInstr1 & 0x9F00001F) != 0x90000011 ) {
|
||||
_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;
|
||||
|
||||
// ADD X17, X17, dyld_mageLoaderCache@pageoff
|
||||
uint32_t stubInstr2 = E::get32(*(uint32_t*)(stubInstructions + 4));
|
||||
if ( (stubInstr2 & 0xFFC003FF) != 0x91000231 ) {
|
||||
_diagnostics.warning("second instruction of stub (0x%08X) is not ADD for stub at addr 0x%0llX in %s",
|
||||
stubInstr2, (uint64_t)stubVMAddr, _installName);
|
||||
return 0;
|
||||
}
|
||||
uint32_t addValue = ((stubInstr2 & 0x003FFC00) >> 10);
|
||||
|
||||
// LDR X16, [X17]
|
||||
uint32_t stubInstr3 = E::get32(*(uint32_t*)(stubInstructions + 8));
|
||||
if ( stubInstr3 != 0xF9400230 ) {
|
||||
_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;
|
||||
}
|
||||
return (stubVMAddr & (-4096)) + adrpValue*4096 + addValue;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template <typename P>
|
||||
void StubOptimizer<P>::buildStubMap(const std::unordered_set<std::string>& neverStubEliminate)
|
||||
{
|
||||
// find all stubs and lazy pointers
|
||||
const macho_nlist<P>* symbolTable = (const macho_nlist<P>*)(&_linkeditBias[_symTabCmd->symoff()]);
|
||||
const char* symbolStrings = (char*)(&_linkeditBias[_symTabCmd->stroff()]);
|
||||
const uint32_t* const indirectTable = (uint32_t*)(&_linkeditBias[_dynSymTabCmd->indirectsymoff()]);
|
||||
const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)_mh + sizeof(macho_header<P>));
|
||||
const uint32_t cmd_count = _mh->ncmds();
|
||||
const macho_load_command<P>* cmd = cmds;
|
||||
for (uint32_t i = 0; i < cmd_count; ++i) {
|
||||
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
|
||||
macho_segment_command<P>* seg = (macho_segment_command<P>*)cmd;
|
||||
macho_section<P>* const sectionsStart = (macho_section<P>*)((char*)seg + sizeof(macho_segment_command<P>));
|
||||
macho_section<P>* const sectionsEnd = §ionsStart[seg->nsects()];
|
||||
for(macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
|
||||
if ( sect->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:
|
||||
case INDIRECT_SYMBOL_ABS | 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<P>* 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) ) {
|
||||
//fprintf(stderr, "stubVMAddr=0x%llX, not bypassing stub to %s in %s because target is interposable\n", (uint64_t)stubVMAddr, symName, _installName);
|
||||
_stubsLeftInterposable++;
|
||||
continue;
|
||||
}
|
||||
const uint8_t* stubInstrs = (uint8_t*)(long)stubVMAddr + _cacheSlide;
|
||||
pint_t targetLPAddr = 0;
|
||||
switch ( _mh->cputype() ) {
|
||||
case CPU_TYPE_ARM64:
|
||||
case CPU_TYPE_ARM64_32:
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64E)
|
||||
targetLPAddr = (pint_t)lazyPointerAddrFromArm64eStub(stubInstrs, stubVMAddr);
|
||||
else
|
||||
#endif
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
if (_mh->cputype() == CPU_TYPE_ARM64_32)
|
||||
targetLPAddr = (pint_t)lazyPointerAddrFromArm64_32Stub(stubInstrs, stubVMAddr);
|
||||
else
|
||||
#endif
|
||||
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) || (sectionType == S_NON_LAZY_SYMBOL_POINTERS) ) {
|
||||
pint_t lpVMAddr;
|
||||
pint_t* lpContent = (pint_t*)(sect->addr() + _cacheSlide);
|
||||
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:
|
||||
case INDIRECT_SYMBOL_LOCAL|INDIRECT_SYMBOL_ABS:
|
||||
break;
|
||||
default:
|
||||
lpValue = (pint_t)P::getP(lpContent[j]);
|
||||
|
||||
// Fixup threaded rebase/bind
|
||||
if ( _chainedFixups ) {
|
||||
dyld3::MachOLoaded::ChainedFixupPointerOnDisk ptr;
|
||||
ptr.raw64 = lpValue;
|
||||
assert(ptr.arm64e.authRebase.bind == 0);
|
||||
if ( ptr.arm64e.authRebase.auth ) {
|
||||
lpValue = (pint_t)(_cacheUnslideAddr + ptr.arm64e.authRebase.target);
|
||||
}
|
||||
else {
|
||||
lpValue = (pint_t)ptr.arm64e.unpackTarget();
|
||||
}
|
||||
}
|
||||
|
||||
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<P>* 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) ) {
|
||||
//fprintf(stderr, "skipping lazy pointer at 0x%0lX to %s in %s because target is within dylib\n", (long)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<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename P>
|
||||
void StubOptimizer<P>::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*)(_textSection->addr() + _cacheSlide);
|
||||
|
||||
// Whole :== <count> FromToSection+
|
||||
// FromToSection :== <from-sect-index> <to-sect-index> <count> ToOffset+
|
||||
// ToOffset :== <to-sect-offset-delta> <count> FromOffset+
|
||||
// FromOffset :== <kind> <count> <from-sect-offset-delta>
|
||||
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 > 13 ) {
|
||||
_diagnostics.error("bad kind (%llu) value in %s\n", 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);
|
||||
_branchToStubCount++;
|
||||
if ( handler(kind, instrAddr, stubAddr, instruction) ) {
|
||||
E::set32(*instrPtr, instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Extract displacement from a thumb b/bl/blx instruction.
|
||||
template <typename P>
|
||||
int32_t StubOptimizer<P>::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 <typename P>
|
||||
uint32_t StubOptimizer<P>::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 <typename P>
|
||||
void StubOptimizer<P>::optimizeArmCallSites(std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr)
|
||||
{
|
||||
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 (resolver or interposable)
|
||||
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 + 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;
|
||||
_branchOptimizedToDirectCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to re-use an existing optimized stub
|
||||
const auto& pos3 = targetAddrToOptStubAddr.find(finalTargetAddr);
|
||||
if ( pos3 != targetAddrToOptStubAddr.end() ) {
|
||||
uint64_t existingStub = pos3->second;
|
||||
if ( existingStub != stubAddr ) {
|
||||
int64_t deltaToOptStub = existingStub - (callSiteAddr + 4);
|
||||
if ( (deltaToOptStub > -b16MegLimit) && (deltaToOptStub < b16MegLimit) ) {
|
||||
bool targetIsThumb = (existingStub & 1);
|
||||
instruction = setDisplacementInThumbBranch(instruction, (uint32_t)callSiteAddr, (int32_t)deltaToOptStub, targetIsThumb);
|
||||
if (_diagnostics.hasError())
|
||||
return false;
|
||||
_branchToReUsedOptimizedStubCount++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// leave as BL to stub, but optimize the stub
|
||||
_stubsToOptimize.insert(stubAddr);
|
||||
targetAddrToOptStubAddr[finalTargetAddr] = stubAddr;
|
||||
_branchToOptimizedStubCount++;
|
||||
return false;
|
||||
}
|
||||
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 <typename P>
|
||||
void StubOptimizer<P>::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));
|
||||
uint32_t* stubInstructions = (uint32_t*)((uint8_t*)(long)stubVMAddr + _cacheSlide);
|
||||
assert(stubInstructions[0] == 0xe59fc004);
|
||||
stubInstructions[0] = 0xe59fc000; // ldr ip, L0
|
||||
stubInstructions[1] = 0xe08ff00c; // add pc, pc, ip
|
||||
stubInstructions[2] = delta; // L0: .long xxxx
|
||||
stubInstructions[3] = 0xe7ffdefe; // trap
|
||||
_stubOptimizedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename P>
|
||||
void StubOptimizer<P>::optimizeArm64Stubs()
|
||||
{
|
||||
for (const uint64_t stubVMAddr : _stubsToOptimize ) {
|
||||
pint_t lpVMAddr = _stubAddrToLPAddr[(pint_t)stubVMAddr];
|
||||
const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr);
|
||||
if ( pos == _lpAddrToTargetAddr.end() )
|
||||
return;
|
||||
pint_t targetVMAddr = pos->second;
|
||||
|
||||
int64_t adrpDelta = (targetVMAddr & -4096) - (stubVMAddr & -4096);
|
||||
// Note: ADRP/ADD can only span +/-4GB
|
||||
uint32_t* stubInstructions = (uint32_t*)((uint8_t*)(long)stubVMAddr + _cacheSlide);
|
||||
bool rightInstr1 = ((stubInstructions[0] & 0x9F00001F) == 0x90000010); // ADRP X16, lp@page
|
||||
bool rightInstr2 = ((stubInstructions[1] & 0xFFC003FF) == 0xF9400210); // LDR X16, [X16, lp@pageoff]
|
||||
bool rightInstr3 = (stubInstructions[2] == 0xD61F0200); // BR X16
|
||||
|
||||
if ( rightInstr1 && rightInstr2 && rightInstr3 ) {
|
||||
uint32_t immhi = (adrpDelta >> 9) & (0x00FFFFE0);
|
||||
uint32_t immlo = (adrpDelta << 17) & (0x60000000);
|
||||
uint32_t newADRP = (0x90000010) | immlo | immhi;
|
||||
uint32_t off12 = (targetVMAddr & 0xFFF);
|
||||
uint32_t newADD = (0x91000210) | (off12 << 10);
|
||||
|
||||
stubInstructions[0] = newADRP; // ADRP X16, target@page
|
||||
stubInstructions[1] = newADD; // ADD X16, X16, target@pageoff
|
||||
stubInstructions[2] = 0xD61F0200; // BR X16
|
||||
_stubOptimizedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
template <typename P>
|
||||
void StubOptimizer<P>::optimizeArm64eStubs()
|
||||
{
|
||||
for (const uint64_t stubVMAddr : _stubsToOptimize ) {
|
||||
pint_t lpVMAddr = _stubAddrToLPAddr[(pint_t)stubVMAddr];
|
||||
const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr);
|
||||
if ( pos == _lpAddrToTargetAddr.end() )
|
||||
return;
|
||||
pint_t targetVMAddr = pos->second;
|
||||
|
||||
int64_t adrpDelta = (targetVMAddr & -4096) - (stubVMAddr & -4096);
|
||||
// Note: ADRP/ADD can only span +/-4GB
|
||||
uint32_t* stubInstructions = (uint32_t*)((uint8_t*)(long)stubVMAddr + _cacheSlide);
|
||||
bool rightInstr1 = ((stubInstructions[0] & 0x9F00001F) == 0x90000011); // ADRP X17, lp@page
|
||||
bool rightInstr2 = ((stubInstructions[1] & 0xFFC003FF) == 0x91000231); // ADD X17, [X17, lp@pageoff]
|
||||
bool rightInstr3 = (stubInstructions[2] == 0xF9400230); // LDR X16, [X17]
|
||||
bool rightInstr4 = (stubInstructions[3] == 0xD71F0A11); // BRAA X16, X17
|
||||
|
||||
if ( rightInstr1 && rightInstr2 && rightInstr3 && rightInstr4) {
|
||||
uint32_t immhi = (adrpDelta >> 9) & (0x00FFFFE0);
|
||||
uint32_t immlo = (adrpDelta << 17) & (0x60000000);
|
||||
uint32_t newADRP = (0x90000010) | immlo | immhi;
|
||||
uint32_t off12 = (targetVMAddr & 0xFFF);
|
||||
uint32_t newADD = (0x91000210) | (off12 << 10);
|
||||
|
||||
stubInstructions[0] = newADRP; // ADRP X16, target@page
|
||||
stubInstructions[1] = newADD; // ADD X16, X16, target@pageoff
|
||||
stubInstructions[2] = 0xD61F0200; // BR X16
|
||||
stubInstructions[3] = 0xD4200020; // TRAP
|
||||
_stubOptimizedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
template <typename P>
|
||||
void StubOptimizer<P>::optimizeArm64_32Stubs()
|
||||
{
|
||||
for (const uint64_t stubVMAddr : _stubsToOptimize ) {
|
||||
pint_t lpVMAddr = _stubAddrToLPAddr[(pint_t)stubVMAddr];
|
||||
const auto& pos = _lpAddrToTargetAddr.find(lpVMAddr);
|
||||
if ( pos == _lpAddrToTargetAddr.end() )
|
||||
return;
|
||||
pint_t targetVMAddr = pos->second;
|
||||
|
||||
int64_t adrpDelta = (targetVMAddr & -4096) - (stubVMAddr & -4096);
|
||||
uint32_t* stubInstructions = (uint32_t*)((uint8_t*)(long)stubVMAddr + _cacheSlide);
|
||||
bool rightInstr1 = ((stubInstructions[0] & 0x9F00001F) == 0x90000010); // ADRP X16, lp@page
|
||||
bool rightInstr2 = ((stubInstructions[1] & 0xFFC003FF) == 0xB9400210); // LDR W16, [X16, lp@pageoff]
|
||||
bool rightInstr3 = (stubInstructions[2] == 0xD61F0200); // BR X16
|
||||
|
||||
if ( rightInstr1 && rightInstr2 && rightInstr3 ) {
|
||||
uint32_t immhi = (adrpDelta >> 9) & (0x00FFFFE0);
|
||||
uint32_t immlo = (adrpDelta << 17) & (0x60000000);
|
||||
uint32_t newADRP = (0x90000010) | immlo | immhi;
|
||||
uint32_t off12 = (targetVMAddr & 0xFFF);
|
||||
uint32_t newADD = (0x91000210) | (off12 << 10);
|
||||
|
||||
stubInstructions[0] = newADRP; // ADRP X16, target@page
|
||||
stubInstructions[1] = newADD; // ADD X16, X16, target@pageoff
|
||||
stubInstructions[2] = 0xD61F0200; // BR X16
|
||||
_stubOptimizedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template <typename P>
|
||||
void StubOptimizer<P>::optimizeArm64CallSites(std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr)
|
||||
{
|
||||
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 (resolver or interposable)
|
||||
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);
|
||||
_branchOptimizedToDirectCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// try to re-use an existing optimized stub
|
||||
const auto& pos3 = targetAddrToOptStubAddr.find((pint_t)finalTargetAddr);
|
||||
if ( pos3 != targetAddrToOptStubAddr.end() ) {
|
||||
uint64_t existingStub = pos3->second;
|
||||
if ( existingStub != stubAddr ) {
|
||||
int64_t deltaToOptStub = existingStub - callSiteAddr;
|
||||
if ( (deltaToOptStub > -b128MegLimit) && (deltaToOptStub < b128MegLimit) ) {
|
||||
instruction = (instruction & 0xFC000000) | ((deltaToOptStub >> 2) & 0x03FFFFFF);
|
||||
_branchToReUsedOptimizedStubCount++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// leave as BL to stub, but optimize the stub
|
||||
_stubsToOptimize.insert(stubAddr);
|
||||
targetAddrToOptStubAddr[(pint_t)finalTargetAddr] = (pint_t)stubAddr;
|
||||
_branchToOptimizedStubCount++;
|
||||
return false;
|
||||
});
|
||||
if (_diagnostics.hasError())
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
template <typename P>
|
||||
void StubOptimizer<P>::optimizeCallSites(std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr)
|
||||
{
|
||||
if ( _textSection == NULL )
|
||||
return;
|
||||
if ( _stubSection == NULL )
|
||||
return;
|
||||
|
||||
|
||||
switch ( _mh->cputype() ) {
|
||||
case CPU_TYPE_ARM64:
|
||||
optimizeArm64CallSites(targetAddrToOptStubAddr);
|
||||
#if SUPPORT_ARCH_arm64e
|
||||
if (_mh->cpusubtype() == CPU_SUBTYPE_ARM64E)
|
||||
optimizeArm64eStubs();
|
||||
else
|
||||
#endif
|
||||
optimizeArm64Stubs();
|
||||
break;
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
case CPU_TYPE_ARM64_32:
|
||||
optimizeArm64CallSites(targetAddrToOptStubAddr);
|
||||
optimizeArm64_32Stubs();
|
||||
break;
|
||||
#endif
|
||||
case CPU_TYPE_ARM:
|
||||
optimizeArmCallSites(targetAddrToOptStubAddr);
|
||||
optimizeArmStubs();
|
||||
break;
|
||||
}
|
||||
if ( verbose ) {
|
||||
_diagnostics.verbose("dylib has %6u BLs to %4u stubs. Changed %5u, %5u, %5u BLs to use direct branch, optimized stub, neighbor's optimized stub. "
|
||||
"%5u stubs left interposable, %4u stubs optimized. path=%s\n",
|
||||
_branchToStubCount, _stubCount, _branchOptimizedToDirectCount, _branchToOptimizedStubCount, _branchToReUsedOptimizedStubCount,
|
||||
_stubsLeftInterposable, _stubOptimizedCount, _installName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
void bypassStubs(DyldSharedCache* cache, const std::string& archName, std::unordered_map<uint64_t, uint64_t>& targetAddrToOptStubAddr,
|
||||
const char* const neverStubEliminateDylibs[], const char* const neverStubEliminateSymbols[],
|
||||
Diagnostics& diags)
|
||||
{
|
||||
diags.verbose("Stub elimination optimization:\n");
|
||||
|
||||
// construct a StubOptimizer for each image
|
||||
__block std::vector<StubOptimizer<P>*> optimizers;
|
||||
cache->forEachImage(^(const mach_header* mh, const char* installName) {
|
||||
optimizers.push_back(new StubOptimizer<P>(cache, (macho_header<P>*)mh, diags));
|
||||
});
|
||||
|
||||
// build set of functions to never stub-eliminate because tools may need to override them
|
||||
std::unordered_set<std::string> neverStubEliminate;
|
||||
for (const char* const* p=neverStubEliminateSymbols; *p != nullptr; ++p) {
|
||||
neverStubEliminate.insert(*p);
|
||||
}
|
||||
for (const char* const* d=neverStubEliminateDylibs; *d != nullptr; ++d) {
|
||||
for (StubOptimizer<P>* 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<ExportInfoTrie::Entry> 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<P>* op : optimizers)
|
||||
op->buildStubMap(neverStubEliminate);
|
||||
|
||||
// optimize call sites to by-pass stubs or jump through island
|
||||
for (StubOptimizer<P>* op : optimizers)
|
||||
op->optimizeCallSites(targetAddrToOptStubAddr);
|
||||
|
||||
// write total optimization info
|
||||
uint32_t callSiteCount = 0;
|
||||
uint32_t callSiteDirectOptCount = 0;
|
||||
for (StubOptimizer<P>* op : optimizers) {
|
||||
callSiteCount += op->_branchToStubCount;
|
||||
callSiteDirectOptCount += op->_branchOptimizedToDirectCount;
|
||||
}
|
||||
diags.verbose(" cache contains %u call sites of which %u were direct bound\n", callSiteCount, callSiteDirectOptCount);
|
||||
|
||||
// clean up
|
||||
for (StubOptimizer<P>* op : optimizers)
|
||||
delete op;
|
||||
}
|
||||
|
||||
void CacheBuilder::optimizeAwayStubs()
|
||||
{
|
||||
std::unordered_map<uint64_t, uint64_t> targetAddrToOptStubAddr;
|
||||
|
||||
DyldSharedCache* dyldCache = (DyldSharedCache*)_readExecuteRegion.buffer;
|
||||
std::string archName = dyldCache->archName();
|
||||
#if SUPPORT_ARCH_arm64_32
|
||||
if ( startsWith(archName, "arm64_32") )
|
||||
bypassStubs<Pointer32<LittleEndian> >(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics);
|
||||
else
|
||||
#endif
|
||||
if ( startsWith(archName, "arm64") )
|
||||
bypassStubs<Pointer64<LittleEndian> >(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics);
|
||||
else if ( archName == "armv7k" )
|
||||
bypassStubs<Pointer32<LittleEndian>>(dyldCache, archName, targetAddrToOptStubAddr, _s_neverStubEliminateDylibs, _s_neverStubEliminateSymbols, _diagnostics);
|
||||
// no stub optimization done for other arches
|
||||
}
|
1201
dyld3/shared-cache/OptimizerLinkedit.cpp
Normal file
1201
dyld3/shared-cache/OptimizerLinkedit.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1139
dyld3/shared-cache/OptimizerObjC.cpp
Normal file
1139
dyld3/shared-cache/OptimizerObjC.cpp
Normal file
File diff suppressed because it is too large
Load Diff
144
dyld3/shared-cache/StringUtils.h
Normal file
144
dyld3/shared-cache/StringUtils.h
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
|
||||
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 startsWith(const std::string_view& str, const std::string_view& 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 (size_t i=0; i < byteCount; ++i) {
|
||||
*p++ = hexDigit(bytes[i] >> 4);
|
||||
*p++ = hexDigit(bytes[i] & 0x0F);
|
||||
}
|
||||
*p++ = '\0';
|
||||
}
|
||||
|
||||
inline void putHexNibble(uint8_t value, char*& p)
|
||||
{
|
||||
if ( value < 10 )
|
||||
*p++ = '0' + value;
|
||||
else
|
||||
*p++ = 'A' + value - 10;
|
||||
}
|
||||
|
||||
inline void putHexByte(uint8_t value, char*& p)
|
||||
{
|
||||
value &= 0xFF;
|
||||
putHexNibble(value >> 4, p);
|
||||
putHexNibble(value & 0x0F, p);
|
||||
}
|
||||
|
||||
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) {
|
||||
const char* scratch;
|
||||
if (endHexByte == nullptr) {
|
||||
endHexByte = &scratch;
|
||||
}
|
||||
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
|
||||
|
498
dyld3/shared-cache/Trie.hpp
Normal file
498
dyld3/shared-cache/Trie.hpp
Normal file
@ -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 <typename V>
|
||||
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<std::bidirectional_iterator_tag, const Entry>;
|
||||
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
Trie(void);
|
||||
Trie(const uint8_t* start, const uint8_t* end);
|
||||
Trie(const std::vector<Entry>& entries);
|
||||
|
||||
void emit(std::vector<uint8_t>& output);
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __TRIE__
|
||||
#define __TRIE__
|
||||
#define TRIE_DEBUG (0)
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iterator>
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
|
||||
#if __cplusplus <= 201103L
|
||||
namespace std {
|
||||
template<typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace TrieUtils {
|
||||
|
||||
static void append_uleb128(uint64_t value, std::vector<uint8_t>& 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<uint8_t>& 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 <typename V>
|
||||
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<Entry>& entries) : count(0), nodeCount(1) {
|
||||
// make nodes for all exported symbols
|
||||
for (auto& entry : entries) {
|
||||
addEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void emit(std::vector<uint8_t>& output) {
|
||||
// create vector of nodes
|
||||
std::vector<Node*> 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<Entry>& output)
|
||||
{
|
||||
// empty trie has no entries
|
||||
if ( start == end )
|
||||
return false;
|
||||
char cummulativeString[32768];
|
||||
std::vector<EntryWithOffset> 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<std::string,std::unique_ptr<Node> > 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<uint8_t>& 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<Node> nodeC;
|
||||
std::swap(nodeC, entry.second);
|
||||
currentNode->fChildren.erase(entry.first);
|
||||
|
||||
//Build the new node and insert it
|
||||
std::unique_ptr<Node> nodeB = std::make_unique<Node>();
|
||||
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<Node>(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<EntryWithOffset>& 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<Node*>& 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<uint8_t>& 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<ExportInfo> 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<uint8_t>& 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<DylibIndex> DylibIndexTrie;
|
||||
|
||||
|
||||
#endif // __TRIE__
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user