Moved Source From Main Repo

dyld-733.6
This commit is contained in:
Thomas A 2022-04-18 20:33:29 -07:00
commit 01ca8e46cb
1201 changed files with 349259 additions and 0 deletions

13
.clang-format Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

View File

@ -0,0 +1,2 @@
CODE_SIGN_ENTITLEMENTS[sdk=embedded*] = dyld3/closured/closured_entitlements.plist

26
configs/dyld.xcconfig Normal file
View 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
View 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

View File

@ -0,0 +1 @@

View 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

View 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

File diff suppressed because it is too large Load Diff

10
doc/man/man1/closured.1 Normal file
View 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
View 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
View 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.
.

View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>

File diff suppressed because it is too large Load Diff

1447
dyld3/APIs.cpp Normal file

File diff suppressed because it is too large Load Diff

241
dyld3/APIs.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

278
dyld3/AllImages.h Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

957
dyld3/Closure.h Normal file
View 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

File diff suppressed because it is too large Load Diff

365
dyld3/ClosureBuilder.h Normal file
View 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
View 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 */

View 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;
}

View 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 */

View 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;
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

353
dyld3/MachOAnalyzer.h Normal file
View 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

File diff suppressed because it is too large Load Diff

273
dyld3/MachOFile.h Normal file
View 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

File diff suppressed because it is too large Load Diff

201
dyld3/MachOLoaded.h Normal file
View 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
View 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
View 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
View 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__

View 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

View 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
View 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
View 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
View 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
View 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 */

View 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

View 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__

File diff suppressed because it is too large Load Diff

View 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 */

View 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;
}

File diff suppressed because it is too large Load Diff

View 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 */

View 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

View 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 */

View 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__

View 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

View 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

View 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__

View 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 */

File diff suppressed because it is too large Load Diff

View 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);
}
}
};

File diff suppressed because it is too large Load Diff

View 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 = &sectionsStart[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 = &sectionsStart[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
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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
View 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