WWDC 2017: App Startup Time: Past, Present, and Future
Terminology:
Startup time = everything before main()
Launch closure = all info necessary to launch an app
dyld history
dyld 1.0 (1996-2004)
- Shipped in NeXTStep 3.3
- Predated
dlopen()
becoming POSIX-standardized - Prebinding added in Mac OS X Cheetah 1.0 - would patch your binaries on every launch to have the correct address of the libraries, so didn't have to re-bind in most cases on next launch
dyld 2.0 (2004-2007)
- Shipped in Mac OS X Tiger 10.4
- Complete rewrite w/ full native dlopen()/dlsym() semantics
- Was much faster, so didn't need to do prebinding hack anymore
dyld 2.x (2007-2017)
- More architectures and platforms: x86, arm64, iOS, tvOS
- Improved security: code signing, ASLR, bounds checking
- Prebinding completely replaced by shared cache
Today: dyld 3 (2017)
Complete rethink of dynamic linking
Starting with Apple's system apps, will eventually be used for 3rd party apps too
Most of dyld is now a daemon, so easier for the dyld team to test (previously, was very hard to test since XCTest itself relied on dyld)
3 components:
- Out-of-process Mach-O compiler and parser and writes launch closure
- Resolves all searchpaths, @rpaths, env vars
- Creates and writes launch closure with results
- In-process loading engine (i.e. embedded in your app)
- Validates launch closure
- Maps in all dylibs
- Applies fixups and runs initializers
- Jumps to main()
- Launch closure caching service (most launches go through this path)
- Built into shared cache
- 3rd party app launch closures built during install
Preparing for dyld 3:
- Unaligned pointers in
__DATA
- Can happen if you use
#pragma pack(1)
and__attribute__((__packed__, aligned(1)))
- ...but this is impossible in Swift! So use Swift
- Can happen if you use
- Previously used lazy symbol resolution, so if you used unknown symbol would crash on first-use
Now does eager symbol resolution for better perf, so now app will crash on launch if uses unknown symbols