Compilation process of IOS app code
- source file: load h、. m、. CPP and other documents
- Pretreatment: replace macros, delete comments, expand header files, and generate I file
- compile: will I file is converted into assembly language to generate S file
-
assembly: convert assembly file into machine code file to generate O documentation
link: Yes O reference other libraries in the file to generate the final executable file

Static library
- In the link phase, the static library will link and package the target program generated by the assembly and the referenced library into the executable file. At this time, the static library will not be changed, because it is copied directly to the target program during compilation
- Benefits: after compilation, the library file is actually useless. The target program has no external dependencies and can be run directly
- Disadvantages: since there will be two static libraries, it will increase the volume of the target program and consume a lot of memory, performance and speed
Dynamic library
The dynamic library is not linked to the target program when the program is compiled. The target program only stores references to the dynamic library and is loaded when the program is running
-
Advantages:
1. Reduce the size of the app after packaging: because it does not need to be copied to the target program, it will not affect the volume of the target program. Compared with the static library, it reduces the size of the app.
2. Share memory and save resources: the same library can be used by multiple programs. The purpose of updating the program is achieved by updating the dynamic library: due to the feature of loading only at runtime, the library can be replaced at any time without recompiling the code -
Disadvantages: dynamic loading will bring some performance loss. Using dynamic library will also make the program dependent on the external environment. If the environment lacks dynamic library or the library version is incorrect, the program will not run
dyld
-
Dyld (the dynamic link editor) is Apple’s dynamic linker and an important part of Apple’s operating system. After the app is compiled and packaged into mach-o file in executable file format, dyld is responsible for connecting and loading the program.
The startup flow chart of app is as follows:
image.png
- [app startup starting point]: it is found by running the program from
dyld
Medium_dyld_start
It started, so I need to goOpensource download a copy of dyld’s source codeFor analysis

1、__dyld_start:
2、dyldbootstrap::start(app_mh, argc, argv, dyld_mh, &startGlue)
uintptr_t start(const dyld3::MachOLoaded* appsMachHeader, int argc, const char* argv[],
const dyld3::MachOLoaded* dyldsMachHeader, uintptr_t* startGlue){
dyld3::kdebug_trace_dyld_marker(DBG_DYLD_TIMING_BOOTSTRAP_START, 0, 0, 0, 0);
rebaseDyld(dyldsMachHeader);
const char** envp = &argv[argc+1];
const char** apple = envp;
while(*apple != NULL) { ++apple; }
++apple;
__guard_setup(apple);
#if DYLD_INITIALIZER_SUPPORT
runDyldInitializers(argc, argv, envp, apple);
#endif
_subsystem_init(apple);
uintptr_t appsSlide = appsMachHeader->getSlide();
return dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
}
3、dyld::_main((macho_header*)appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
//Get the cdhash of the main executable from the environment
// Grab the cdHash of the main executable from the environment
uint8_t mainExecutableCDHashBuffer[20];
const uint8_t* mainExecutableCDHash = nullptr;
if ( const char* mainExeCdHashStr = _simple_getenv(apple, "executable_cdhash") ) {
unsigned bufferLenUsed;
if ( hexStringToBytes(mainExeCdHashStr, mainExecutableCDHashBuffer, sizeof(mainExecutableCDHashBuffer), bufferLenUsed) )
mainExecutableCDHash = mainExecutableCDHashBuffer;
}
getHostInfo(mainExecutableMH, mainExecutableSlide);// Gets information about the current running environment schema
//[step 1: environment variable configuration]
checkEnvironmentVariables(envp);// Check the set environment variables
defaultUninitializedFallbackPaths(envp);// (dyld_fallback_library_path is nil, set the default value for it)
//[step 2: shared cache]
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);// Check whether the shared cache is turned on (it must be turned on in IOS)
mapSharedCache(mainExecutableSlide);// Check whether the shared cache is mapped to the shared area
//[step 3: initialization of main program] instantiate imageloader for main executable
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);// Load the executable and generate an imageloader instance object
gLinkContext.mainExecutable = sMainExecutable;
gLinkContext.mainExecutableCodeSigned = hasCodeSignatureLoadCommand(mainExecutableMH);
//[step 4: insert dynamic libraries] load any inserted Libraries
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
//Load all dyld_ INSERT_ Libraries specifies the library
//Traverse dyld_ INSERT_ Libraries environment variable, call loadinserteddylib to load
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);
}
//Record count of inserted libraries so that a flat search will look at
// inserted libraries, then main, then others. (insert stock in, then main, then others)
sInsertedDylibCount = sAllImages.size()-1;
// link main executable
gLinkContext.linkingMainExecutable = true;
#if SUPPORT_ACCELERATE_TABLES
if ( mainExcutableAlreadyRebased ) {
// previous link() on main executable has already adjusted its internal pointers for ASLR
// work around that by rebasing by inverse amount
sMainExecutable->rebase(gLinkContext, -mainExecutableSlide);
}
#endif
//[step 5: link main program] link main program
link(sMainExecutable, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
sMainExecutable->setNeverUnloadRecursive();
if ( sMainExecutable->forceFlat() ) {
gLinkContext.bindFlat = true;
gLinkContext.prebindUsage = ImageLoader::kUseNoPrebinding;
}
//[step 6: link dynamic libraries] link any inserted Libraries
//Do this after linking main executable so that any dylibs pulled in by inserted
//Dylibs (e.g. libsystem) will not be in front of the program uses dylibs (e.g. libsystem)
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
link(image, sEnv.DYLD_BIND_AT_LAUNCH, true, ImageLoader::RPathChain(NULL, NULL), -1);
image->setNeverUnloadRecursive();
}
if ( gLinkContext.allowInterposing ) {
//Only inserted libraries can be inserted
//Register interacting info after all inserted libraries are bound so chaining works
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
//Registration symbol insertion
image->registerInterposing(gLinkContext);
}
}
}
//[step 7: weak sign binding]
//Apply interacting to initial set of images
for(int i=0; i < sImageRoots.size(); ++i) {
sImageRoots[i]->applyInterposing(gLinkContext);
}
ImageLoader::applyInterposingToDyldCache(gLinkContext);
// Bind and notify for the main executable now that interposing has been registered
uint64_t bindMainExecutableStartTime = mach_absolute_time();
sMainExecutable->recursiveBindWithAccounting(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true);
uint64_t bindMainExecutableEndTime = mach_absolute_time();
ImageLoaderMachO::fgTotalBindTime += bindMainExecutableEndTime - bindMainExecutableStartTime;
gLinkContext.notifyBatch(dyld_image_state_bound, false);
// Bind and notify for the inserted images now interposing has been registered
if ( sInsertedDylibCount > 0 ) {
for(unsigned int i=0; i < sInsertedDylibCount; ++i) {
ImageLoader* image = sAllImages[i+1];
image->recursiveBind(gLinkContext, sEnv.DYLD_BIND_AT_LAUNCH, true, nullptr);
}
}
// < rdar://problem/12186933 >Do weak binding only after all inserted images linked
//Weak binding
sMainExecutable->weakBind(gLinkContext);
gLinkContext.linkingMainExecutable = false;
sMainExecutable->recursiveMakeDataReadOnly(gLinkContext);
CRSetCrashLogMessage("dyld: launch, running initializers");
//[step 8: execute initialization method]
initializeMainExecutable();
// notify any montoring proccesses that this process is about to enter main()
notifyMonitoringDyldMain();
//[step 9: find the main function as the main program entry]
// find entry point for main executable
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();
if ( result != 0 ) {
//The main executable uses LC \ u main. We need to use the helper in libdyld to call main ()
// main executable uses LC_MAIN, we need to use helper in libdyld to call into main()
if ( (gLibSystemHelpers != NULL) && (gLibSystemHelpers->version >= 9) )
*startGlue = (uintptr_t)gLibSystemHelpers->startGlueToCallExit;
else
halt("libdyld.dylib support not present for LC_MAIN");
}
else {
//The main executable uses LC \ u unixthread. Dyld needs to "start" in the program set for main()
// main executable uses LC_UNIXTHREAD, dyld needs to let "start" in program set up for main()
result = (uintptr_t)sMainExecutable->getEntryFromLC_UNIXTHREAD();
*startGlue = 0;
}
}
return result;
}
[summary] the source chain of load is: _dyld_start — > dyldbootstrap:: Start — > dyld:: _main — > dyld:: initializemainexecutable — > imageloader:: runinitializers — > imageloader:: processinitializers — > imageloader:: recursive initialization — > dyld:: notifysingle (a callback process) – > snotifyobjcinit — > load_images (libobjcic. A.dylib)
image.png
