preface
The previous exploration is basically the underlying process after main function, so today I will explore the underlying operation after main function
The preparatory work
- dyld-852
- libdispatch
- libsystem
- Objc4-818.2 –
Compile and library
IOS programmers tend to write upper-level code, most familiar with the.h and.m files, and how they go through the process of compiling the.h and.m files to produce the executable files
- The source file:
.h
,.m
,.cpp
And other documents - Preprocessing: During preprocessing, comments are removed, conditional compilation is processed, headers are expanded, and macros are replaced
- Compilation: performs lexical analysis and syntax analysis as well as intermediate layers
IR
File, and finally generate assembly file.s
file - Assembly:
.s
Files are converted to machine language generation.o
file - Links: will all
.o
Files as well as links to third-party libraries that generate onemacho
Type of executable file
Compilation flowchart
Dynamic library Static library
- Static libraries: The assembler generated target and reference libraries are linked and packaged into an executable during the linking phase
- Dynamic libraries: program compilations are not linked to object code, but are loaded while the program is running
Dynamic library static repository
Advantages and disadvantages of static libraries
advantages
- Static libraries are packaged into executable files that can run independently of the external environment after successful compilation
disadvantages
- Compiled files become larger and must be recompiled if static libraries are updated
Advantages and disadvantages of dynamic libraries
advantages
- After reducing packaging
App
The size of the - Share content and resources
- The purpose of updating program is achieved by updating dynamic library
disadvantages
- Executable files cannot be run alone and must depend on the external environment
Static and dynamic library diagram
Dyld loading process
Since it is exploring the flow before main, the value can be assembled and flow directly to the breakpoint of main
The stack shows that the libdyld. Dylib library’s start function is the starting position, and then directly to the main function. Nothing in between. The load method is called before main, so continue debugging at the load method breakpoint
The stack information clearly shows how the load method is called
_dyld_start
–>dyldbootstrap::start
–>dyld::_main
–>initializeMainExecutable
–>runInitializers
–>processInitializers
–>runInitializers
–>recursiveInitialization
–>notifySingle
–>load_images
–>+[ViewController load]
Copy the code
_DYLD_START is in the source library of dyLD, _DYLD_START is compiled in detail below
The dyLDBootstrap ::start method is called after _dyLD_START in the assembly. Dyld bootstrap::start global search dyLDBootstrap ::start global search dyLDBootstrap ::start global search dyLDBootstrap ::start global search dyLDBootstrap ::start global search dyLDBootstrap ::start global search dyLDBootstrap
dyldbootstrap
Is a namespace indyldInitialization.cpp
Search in filestart
methods
- relocation
dyld
Because theApp
The system will give it to you automaticallyApp
Randomly assignedASLR
.dyld
Relocation is required because it needs to go to the current process to get its own information - call
dyld::_main
Method to get the return result
dyld::_main
There are more than 800 lines of code in the dyld::_main method. It is enough to sort out the overall process and master the overall loading and starting process. If you are interested in details, you can explore them by yourself
Configuring environment Variables
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char*
apple[], uintptr_t* startGlue)
{
// System kernel check
//Check and see if there are any kernel flags
dyld3::BootArgs::setFlags(hexToUInt64(_simple_getenv(apple, "dyld_flags"), nullptr)); .// Get the hash value of the main program
uint8_t mainExecutableCDHashBuffer[20];
// The main program initializes the hash value to 0
const uint8_t* mainExecutableCDHash = nullptr;
if ( const char* mainExeCdHashStr = _simple_getenv(apple, "executable_cdhash")) {... mainExecutableCDHash = mainExecutableCDHashBuffer;/ / assignment
}
// Configure the CPU architecture according to the macho header, just some file configurations
getHostInfo(mainExecutableMH, mainExecutableSlide); .uintptr_t result = 0; // result is the address of main
sMainExecutableMachHeader = mainExecutableMH;// The header of the executable
sMainExecutableSlide = mainExecutableSlide; // Loading into the process system automatically provides ASLR virtual memory offset
{
__block bool platformFound = false;
Is it arm64 or x86? Is it 64-bit or 32-bit
((dyld3::MachOFile*)mainExecutableMH)->forEachSupportedPlatform
(^(dyld3::Platform platform, uint32_t minOS, uint32_t sdk) {
gProcessInfo->platform = (uint32_t)platform;
platformFound = true; }); }...// Setting the context will save the information to gLinkContext
setContext(mainExecutableMH, argc, argv, envp, apple); .// Whether the file is restricted, AFMI Apple mobile file protection mechanism
configureProcessRestrictions(mainExecutableMH, envp); .// set again because envp and apple may have changed or moved
// Update the context information again
setContext(mainExecutableMH, argc, argv, envp, apple);
// Environment variable configuration, Xcode configuration environment variable console can print information
if ( sEnv.DYLD_PRINT_OPTS )
printOptions(argv);
if ( sEnv.DYLD_PRINT_ENV )
printEnvironmentVariables(envp);
Copy the code
Above is some header information for reading macho, saving Settings, and setting environment variables. It’s just preparation
Loading the Shared cache
Shared cacheIOS
It is necessary to have a shared cache, which stores dynamic libraries at the system level. Such asUIKit
.CoreFoundation
And so on. Self-created dynamic libraries or third-party dynamic libraries are not placed in the shared cache
checkSharedRegionDisable
The method is to detect whether different schemas need to share cachesmapSharedCache
Loading the Shared cache
The checkSharedRegionDisable method clearly indicates that IOS must have a shared cache. So let’s see how we load a shared cache and one of the mapSharedCache methods calls loadDyldCache to load a shared cache, okay
There are three scenarios for loading a shared cache
Forcing private
:forcePrivate
=YES
Is mandatory private. Only the current loadApp
Process, not in the shared cacheThe shared cache has been loaded
If the library you are relying on is already loaded in the shared cache, you can use it without further actionFirst load
; If the library you depend on is not in the shared cache, it will be loaded into the shared cache
I think we now have a clear understanding of the shared cache loading. IOS must have a shared cache and the shared cache only holds system libraries. The purpose of the shared cache is to allow multiple processes to use system libraries together
dyld3
ordyld2
Dyld3 has something called closure mode which loads faster and more efficiently. IOS11 after the main program is loaded with DYLD3, IOS13 after the dynamic library and tripartite library with dyLD3 loading
Start ClosureMode::on use dyLD3; otherwise use dyLD2
if ( sClosureMode == ClosureMode::Off ) {
//dyld2
if ( gLinkContext.verboseWarnings )
dyld::log("dyld: not using closures\n");
} else {
// dyLD3 DYLD_LAUNCH_MODE_USING_CLOSURE uses closure mode
sLaunchModeUsed = DYLD_LAUNCH_MODE_USING_CLOSURE;
const dyld3::closure::LaunchClosure* mainClosure = nullptr; dyld3::closure::LoadedFileInfo mainFileInfo; mainFileInfo.fileContent = mainExecutableMH; mainFileInfo.path = sExecPath; .// Check the shared cache to see if there is a mainClosure for dyLD3
if( sSharedCacheLoadInfo.loadAddress ! =nullptr ) {
mainClosure = sSharedCacheLoadInfo.loadAddress->findClosure(sExecPath); . }...// If there is one in the shared cache, then verify that closure is valid
if( (mainClosure ! =nullptr) &&!closureValid(mainClosure mainFileInfo, and mainExecutableCDHash,true, envp) ) {
mainClosure = nullptr;
sLaunchModeUsed &= ~DYLD_LAUNCH_MODE_CLOSURE_FROM_OS;
}
bool allowClosureRebuilds = false;
if ( sClosureMode == ClosureMode::On ) {
allowClosureRebuilds = true; }...// If no valid closure is found in the shared cache, a closure is automatically created
if ( (mainClosure == nullptr) && allowClosureRebuilds ) {
...
if ( mainClosure == nullptr ) {
// Create a mainClosure
mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo, envp,
bootToken);
if( mainClosure ! =nullptr) sLaunchModeUsed |= DYLD_LAUNCH_MODE_BUILT_CLOSURE_AT_LAUNCH; }}// try using launch closure
// dyLD3 starts
if( mainClosure ! =nullptr ) {
CRSetCrashLogMessage("dyld3: launch started"); ./ / start launchWithClosure
bool launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress,(dyld3::MachOLoaded*)mainExecutableMH,...) ;// Failed to start
if ( !launched && closureOutOfDate && allowClosureRebuilds ) {
// closure is out of date, build new one
// Re-create mainClosure if it fails
mainClosure = buildLaunchClosure(mainExecutableCDHash, mainFileInfo,
envp, bootToken);
if( mainClosure ! =nullptr) {...// dyLD3 starts again
launched = launchWithClosure(mainClosure, sSharedCacheLoadInfo.loadAddress,
(dyld3::MachOLoaded*)mainExecutableMH,...);
}
}
if ( launched ) {
gLinkContext.startedInitializingMainExecutable = true;
if (sSkipMain)
// Return the address of main on success
result = (uintptr_t)&fake_main;
return result;
}
else {
// Failed to start}}}Copy the code
Dyld3 startup process is after a lot of attempts, the system gave a lot of opportunities, generally there will not be a startup failure
If you don’t use dyLD3, you will use dyLD2
// could not use closure info, launch old way
// Use dyLD2 instead of dyLD3
sLaunchModeUsed = 0;
// install gdb notifier
stateToHandlers(dyld_image_state_dependents_mapped, sBatchHandlers)->push_back(notifyGDB);
stateToHandlers(dyld_image_state_mapped, sSingleHandlers)->push_back(updateAllImages);
// make initial allocations large enough that it is unlikely to need to be re-alloced
sImageRoots.reserve(16);
sAddImageCallbacks.reserve(4);
sRemoveImageCallbacks.reserve(4);
sAddLoadImageCallbacks.reserve(4);
sImageFilesNeedingTermination.reserve(16);
sImageFilesNeedingDOFUnregistration.reserve(8);
#if! TARGET_OS_SIMULATOR
#ifdef WAIT_FOR_SYSTEM_ORDER_HANDSHAKE
file generation process
WAIT_FOR_SYSTEM_ORDER_HANDSHAKE(dyld::gProcessInfo->systemOrderFlag);
#endif
#endif
try {
// add dyld itself to UUID list
addDyldImageToUUIDList(a); . }Copy the code
Today, WE mainly look at the mode of DYLD3, because this will gradually replace DYLD2 in the future, and we will explore the dyLD2 process later
Summarize the startup process of DYLD3
- From the shared cache
dyld3
An instance of themainClosure
- validation
mainClosure
The validity of - Then go to the shared cache to find a valid one
mainClosure
If there is a direct boot - If not, create one
mainClosure
- Start the
mainClosure
, startdyld3
- After successful startup, the main program is successfully started,
result
ismain
The address of the function returned todyldbootstrap::start
Method and then entermian
function
Instantiate the main program
Dyld3 goes through the same process as dyLD2 except dyLD3 uses closure mode. Image often appears in the source code, image does not mean the picture, but the image file, the image file is mapped from disk to memory macho file. Any macho file loaded into memory is called a mirror file
Instantiation of the main program is the need of the main program loaded into memory, part of the information through instantiateMainExecutable method returns ImageLoader instance of type object, and then the main program for signature
Add the instantiated image to the image file array. Note here that the image of the main program is added first to the array. Now explore the ImageLoaderMachO: : instantiateMainExecutable method
Load command information in macho file, and verify. The sniffLoadCommands code fetching part of the important exploration
sniffLoadCommands
In the loadsegment
andcommod
Information, and some validation
segment
The maximum number of segments is256
acommand
The maximum number of alpha is alpha4096
a- Make sure you have to rely on it
libSystem
library
If you’re still a little confused about segment and command and macho headers, use the MachOView tool to learn about executables
The picture is very clearmacho
Header files are schema information, file types, etc.macho
The file consists of three main piecesHeader
,Commods
,Data
Insert dynamic library
throughloadInsertedDylib
Insert dynamic library, at this time the number of dynamic library is all the image files minus one
Link main program
ImageLoader is responsible for loading the image file (main program, dynamic library). Each image corresponds to an instance of the ImageLoader class
void ImageLoader::link(const LinkContext& context, bool forceLazysBound, bool
preflightOnly, bool neverUnload, const RPathChain& loaderRPaths, const char* imagePath{
...
// Load all dynamic libraries recursively
this->recursiveLoadLibraries(context, preflightOnly, loaderRPaths, imagePath); context.notifyBatch(dyld_image_state_dependents_mapped, preflightOnly); . __blockuint64_t t2, t3, t4, t5;
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_APPLY_FIXUPS, 0.0.0);
t2 = mach_absolute_time();
// Recursive relocation
this->recursiveRebaseWithAccounting(context);
context.notifyBatch(dyld_image_state_rebased, false);
t3 = mach_absolute_time();
if ( !context.linkingMainExecutable )
// Recursive binding is not lazy loading
this->recursiveBindWithAccounting(context, forceLazysBound, neverUnload);
t4 = mach_absolute_time();
if ( !context.linkingMainExecutable )
/ / weak binding
this->weakBind(context); t5 = mach_absolute_time(); }...// The link process can count the link dynamic library time, in the environment variable setting can print information
}
Copy the code
- Load all dynamic libraries recursively
- recursive
image
relocation - Recursive binding is not lazy loading
- Weak binding
RecursiveLoadLibraries (recursiveLoadLibraries) is a recursively loaded dynamic library
void ImageLoader::recursiveLoadLibraries(const LinkContext& context, bool
preflightOnly, const RPathChain& loaderRPaths, const char* loadPath){...// get list of libraries this image needs
// Get the current image-dependent dynamic library
DependentLibraryInfo libraryInfos[fLibraryCount];
this->doGetDependentLibraries(libraryInfos);
// get list of rpaths that this image adds
// Get the file path of the current image-dependent dynamic library
std::vector<const char*> rpathsFromThisImage;
this->getRPaths(context, rpathsFromThisImage);
const RPathChain thisRPaths(&loaderRPaths, &rpathsFromThisImage);
// load the image-dependent dynamic library
for(unsigned int i=0; i < fLibraryCount; ++i){
...
dependentLib = context.loadLibrary(requiredLibInfo.name, true.this->getPath(),
&thisRPaths, cacheIndex);
// Save the loaded dynamic library
setLibImage(i, dependentLib, depLibReExported, requiredLibInfo.upward); . `} `// Tell the image-dependent dynamic libraries to load the required dynamic libraries
for(unsigned int i=0; i < libraryCount(a); ++i) { ImageLoader* dependentImage =libImage(i);
if( dependentImage ! =NULL ) {
dependentImage->recursiveLoadLibraries(context, preflightOnly, thisRPaths, libraryInfos[i].name); }}}Copy the code
A lot of code just captures the whole process
- Gets the current
image
Dependent dynamic library and the file path of the dynamic library - loading
image
Dependency dynamic library and save it - Tell the image-dependent dynamic libraries to load the required dynamic libraries
Linked dynamic library
The logic of linking the dynamic library is basically the same as that of linking the main program. Note that the next loop to fetch the image file starts at 1, because the 0th position is the main program. Let’s verify this
The first data in the image list is the main program
Weakly bound main program
You might wonder if there’s a weak symbol binding when you link the main program. Because the linkingMainExecutable = true of the main program, the weak binding in link will not be called during the main program, and the weak binding will be made on the main program after the weak binding is made on the dynamic library
Run the initialization method
InitializeMainExecutable will not be launched because there is a lot to explore later
returnmain
function
Once you’ve got the main function, you’re going to go into the main function that you’re familiar with
conclusion
Dyld load process: DYLD ::_main –> configure environment variables –> load shared cache –> instantiate main program –> Insert dynamic library –> Link main program –> Link dynamic library –> Weakly bind main program –> Run initialization method –> return main function
initializeMainExecutable
void initializeMainExecutable(a)
{
// record that we've reached this step
gLinkContext.startedInitializingMainExecutable = true;
// run initialzers for any inserted dylibs
// Run all the Initialzers methods in dylibs
ImageLoader::InitializerTimingList initializerTimes[allImagesCount()];
initializerTimes[0].count = 0;
const size_t rootCount = sImageRoots.size(a);// Run the dynamic library initialization method first
if ( rootCount > 1 ) {
for(size_t i=1; i < rootCount; ++i) {
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]); }}// run initializers for main executable and everything it brings up
// Run the initialization method of the main program
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]); . }Copy the code
Run the initialization method of the dynamic library first, and then run the initialization method of the main program
runInitializers
Search globally for runInitializers, source below
void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo) { uint64_t t1 = mach_absolute_time(); mach_port_t thisThread = mach_thread_self(); ImageLoader::UninitedUpwards up; up.count = 1; up.imagesAndPaths[0] = { this, this->getPath() }; ProcessInitializers (Context, thisThread, timingInfo, up); context.notifyBatch(dyld_image_state_initialized, false); mach_port_deallocate(mach_task_self(), thisThread); uint64_t t2 = mach_absolute_time(); fgTotalInitTime += (t2 - t1); }Copy the code
Enter processInitializers source code as follows
// upward dylib initializers can be run too soon
// To handle dangling dylibs which are upward linked but not downward, all upward linked dylibs
// have their initialization postponed until after the recursion through downward dylibs
// has completed.
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t
thisThread,InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
uint32_t maxImageCount = context.imageCount() +2;
ImageLoader::UninitedUpwards upsBuffer[maxImageCount];
ImageLoader::UninitedUpwards& ups = upsBuffer[0];
ups.count = 0;
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
// Recurse all 'images' in the list of all images
for (uintptr_t i=0; i < images.count; ++i) {
images.imagesAndPaths[i].first->recursiveInitialization(context,
thisThread, images.imagesAndPaths[i].second, timingInfo, ups);
}
// If any upward dependencies remain, init them.
// To ensure that all upward dependencies are initialized, uninitialize the image again
if ( ups.count > 0 )
processInitializers(context, thisThread, timingInfo, ups);
}
Copy the code
Global search recursiveInitialization, source code as follows
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t
this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
recursive_lock lock_info(this_thread);
recursiveSpinLock(lock_info); .// initialize lower level libraries first
// Initialize the most dependent library first
for(unsigned int i=0; i < libraryCount(a); ++i) { ImageLoader* dependentImage =libImage(i);
if( dependentImage ! =NULL ) {
// don't try to initialize stuff "above" me yet
if ( libIsUpward(i) ) {
uninitUps.imagesAndPaths[uninitUps.count] = { dependentImage,
libPath(i) };
uninitUps.count++;
}
else if ( dependentImage->fDepth >= fDepth ) {
dependentImage->recursiveInitialization(context, this_thread,
libPath(i), timingInfo, uninitUps); }}}// The image to be initialized
uint64_t t1 = mach_absolute_time(a); fState = dyld_image_state_dependents_initialized; oldState = fState; context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);
// initialize this image
// Initialize the image
bool hasInitializers = this->doInitialization(context);
// image initialization is complete
// let anyone know we finished initializing this image
fState = dyld_image_state_initialized;
oldState = fState;
context.notifySingle(dyld_image_state_initialized, this.NULL); .recursiveSpinUnLock(a); }Copy the code
- Dynamic library that needs to be initialized
image
fromlibImage()
, whilelibImage()
Is when linking dynamic librariesrecursiveLoadLibraries
In thesetLibImage
The savedimage
- The system will initialize each library according to its dependency depth. The depth value with the largest value will be initialized first. Each initialization will have one
image
file image
Will be calledcontext.notifySingle
Method to callload_images
callload
methodsdoInitialization
Is to initialize libraries that have no dependenciescontext.notifySingle(dyld_image_state_initialized, this, NULL)
It doesn’t really work,notifySingle
There is no judgment in the method, it could be inobjc
Register callback according to insidedyld_image_state_initialized
State to call the system library
Let’s explore notifySingle and doInitialization. Search for notifySingle globally
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader())) This is a function call. Global search sNotifyObjCInit, source code as follows
In registerObjCNotifiers, assign value to sNotifyObjCInit and search registerObjCNotifiers globally. The source code is as follows
A global search of the _dyLD_OBJC_notify_register shows that there is no place in the DYLD source code where this method is called. Now, what do I do? I can’t explore it, but I don’t know if you’ve forgotten that there’s a notation breakpoint in the exploration. Add the _DYLD_OBJC_Notify_register breakpoint to the project. My project runs dyLD2 on Mac and dyLD3 on IOS
doInitialization
–>doModInitFunctions
–>libSystem_initializer
–>libdispatch_init
–>_os_object_init
–>_objc_init
–>_dyld_objc_notify_register
–>registerObjCNotifiers
libSystem_initializer
Methods in thelibSystem
System in the librarylibdispatch_init
and_os_object_init
Methods in thelibdispatch
System in the library_objc_init
Methods in thelibobjc
In the system library,objc
The source code library is the most familiar
_dyLD_OBJC_NOTIFy_register is called by _objc_init, so the assignment should be in the _objc_init method. Ok
SNotifyObjCInit = load_images actually calls the load_images method. Let’s explore the load_images method
The call_load_methods method, from its name, calls the load method, and we’ll explore the call_load_methods method
To iterate over the load method, explore the call_class_loads and call_category_loads
The load method is called by both classes and classes, and the order of calls can be inferred from this
- Of the class
load
Classification of thanload
Method is called first in the classload
Method is not called until the class is calledload
methods - In the class
load
Methods are compiled in the order in which they are compiledload
Method first calls - classified
load
Methods are compiled in the order in which they are compiledload
Method first calls - Oops, accidentally solved another interview question
The load process is the same as our stack information display process, step by step to verify the stack information
_objc_init
process
_objC_init in the objC system library, in addition to being the most familiar source library, _objc_init serves as a link between the preceding and the following. So work backwards from _objc_init to the entire process. The _objc_init method is called by the _OS_OBJect_init method, which is searched globally in the libDispatch source library for _OS_OBJect_init
The _OS_OBJect_init method does call the _objc_init method. The _OS_OBJect_init method is called by libdispatch_init
The libdispatch_init method does call the _OS_object_init method. Libdispatch_init is called by libSystem_initializer, which is in the libSystem system library
The libSystem_initializer method does call the libdispatch_init method. The libSystem_initializer method is called by doModInitFunctions, which is in the dyLD source library
libSystem
Initializer, must run first, everything else will wait a little bit,doModInitFunctions
Call allC++
function
Add a global C++ function below the main function and break it to see the stack information
The stack information shows that the doModInitFunctions method calls the global c++ method, after the load method
doModInitFunctions
Methods are beingdoInitialization
Call, seedoInitialization
Should be familiar
RecursiveInitialization calls the doInitialization method, which is then recursiveInitialization
conclusion
Load method call flow
_dyld_start
–>dyldbootstrap::start
–>dyld::_main
–>intializeMainExecutable
–>runInitializers
–>processInitializers
–>runInitializers
–>recursiveInitialization
–>notifySingle
–>load_images
–>+[ViewController load]
Procedure for calling the _objc_init method
doInitialization
–>doModInitFunctions
–>libSystem_initializer
–>libdispatch_init
–>_os_object_init
–>_objc_init
–>_dyld_objc_notify_register
–>registerObjCNotifiers
These two call flows form a complete loop through doInitialization and notifySingle
main
Function entry
The above exploration found that the C++ function is executed after the load method, but before the main function, adding a breakpoint in the C++ function step by step debugging, must enter the main function
The breakpoint assembly is displayed in the C++ function, where step by step debugging goes to the next flow to find where the main function is called
dyldbootstrap::start
Returned to themain
The address of the function readx86_64
Register First register foundrax
Deposit ismain
The address of the function, the last step jumps tomain
function
Note: do not change the name of main in strange ways. If you change the name of main, you will get an error. Main is a fixed function
dyld
Loading flow chart
conclusion
Dyld inquiry is boring, tedious and complex, and many problems are encountered in the process of exploration and learning. I have grasped the whole loading process of DYLD basically