-
概念
Mach-O
是一種文件格式,是mac
上可執(zhí)行文件的格式样悟。編寫(xiě)的C
、C++
庭猩、swift
窟她、OC
,最終編譯鏈接生成Mach-O
可執(zhí)行文件蔼水。鏈接的共用庫(kù)分為靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù):靜態(tài)庫(kù)是編譯時(shí)鏈接的庫(kù)震糖,需要鏈接進(jìn)你的
Mach-O
文件里,如果需要更新就要重新編譯一次趴腋,無(wú)法動(dòng)態(tài)加載和更新吊说;而動(dòng)態(tài)庫(kù)是運(yùn)行時(shí)鏈接的庫(kù)论咏,使用dyld
就可以實(shí)現(xiàn)動(dòng)態(tài)加載。
dyld
(the dynamic link editor)是蘋(píng)果的動(dòng)態(tài)鏈接器颁井,是蘋(píng)果操作系統(tǒng)的一個(gè)重要組成部分厅贪,在應(yīng)用被編譯打包成可執(zhí)行文件格式的Mach-O
文件之后,交由dyld
負(fù)責(zé)鏈接雅宾,加載程序 养涮。
-
加載流程
為了輔助探索底層,我們可以用符號(hào)斷點(diǎn)看一下堆棧信息:
- 首先調(diào)用的是
_dyld_start
眉抬,我們馬上進(jìn)入dyld源碼開(kāi)始探究:
- 然后從匯編跳轉(zhuǎn)調(diào)用
start
:
uintptr_t start(const struct macho_header* appsMachHeader, int argc, const char* argv[],
intptr_t slide, const struct macho_header* dyldsMachHeader,
uintptr_t* startGlue)
{
slide = slideOfMainExecutable(dyldsMachHeader);//通過(guò)一個(gè)隨機(jī)值來(lái)實(shí)現(xiàn)地址空間配置隨機(jī)加載, 防止被攻擊
bool shouldRebase = slide != 0;
...
// allow dyld to use mach messaging
mach_init();//開(kāi)放函數(shù)消息使用
...
// set up random value for stack canary
__guard_setup(apple);//設(shè)置堆棧保護(hù)
...
// now that we are done bootstrapping dyld, call dyld's main
uintptr_t appsSlide = slideOfMainExecutable(appsMachHeader);
return dyld::_main(appsMachHeader, appsSlide, argc, argv, envp, apple, startGlue);//開(kāi)始鏈接共享對(duì)象
}
- 接著進(jìn)入
_main
单寂,代碼很多所以只看流程代碼:
uintptr_t
_main(const macho_header* mainExecutableMH, uintptr_t mainExecutableSlide,
int argc, const char* argv[], const char* envp[], const char* apple[],
uintptr_t* startGlue)
{
...
{
checkEnvironmentVariables(envp);//檢查環(huán)境變量
defaultUninitializedFallbackPaths(envp);
}
...
// load shared cache
checkSharedRegionDisable((dyld3::MachOLoaded*)mainExecutableMH, mainExecutableSlide);//驗(yàn)證共享緩存路徑
...
if ( gLinkContext.sharedRegionMode != ImageLoader::kDontUseSharedRegion ) {
mapSharedCache();//加載共享緩存
}
...
try {
// add dyld itself to UUID list
addDyldImageToUUIDList();//添加dyld到UUID列表
...
reloadAllImages:
...
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide, sExecPath);//初始化主程序;內(nèi)核會(huì)映射到可執(zhí)行文件中
...
// load any inserted libraries
if ( sEnv.DYLD_INSERT_LIBRARIES != NULL ) {
for (const char* const* lib = sEnv.DYLD_INSERT_LIBRARIES; *lib != NULL; ++lib)
loadInsertedDylib(*lib);//加載插入的動(dòng)態(tài)庫(kù)
}
...
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);//鏈接主程序中各動(dòng)態(tài)庫(kù),進(jìn)行符號(hào)綁定
image->setNeverUnloadRecursive();
}
...
}
...
ImageLoader::applyInterposingToDyldCache(gLinkContext);//插入任何動(dòng)態(tài)加載的鏡像文件
...
// run all initializers
initializeMainExecutable(); //運(yùn)行所有初始化程序
...
notifyMonitoringDyldMain();//通知監(jiān)聽(tīng)dyld的main
// find entry point for main executable
result = (uintptr_t)sMainExecutable->getEntryFromLC_MAIN();//找到真正 main 函數(shù)入口并返回
...
}
...
}
- 流程:配置環(huán)境變量 -> 加載共享緩存 -> 實(shí)例化主程序 -> 加載動(dòng)態(tài)庫(kù) -> 鏈接動(dòng)態(tài)庫(kù) -> 運(yùn)行所有初始化程序 -> 通知監(jiān)聽(tīng)
dyld
的main
-> 找到真正main
函數(shù)入口
- 然后就開(kāi)始初始化:
static ImageLoaderMachO* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide, const char* path)
{
// try mach-o loader
if ( isCompatibleMachO((const uint8_t*)mh, path) ) {
ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide, path, gLinkContext);//通過(guò)instantiateMainExecutable中的sniffLoadCommands加載主程序其實(shí)就是對(duì)MachO文件中LoadCommons段的一系列加載
addImage(image);//生成鏡像文件后吐辙,添加到sAllImages全局鏡像中宣决,主程序永遠(yuǎn)是sAllImages的第一個(gè)對(duì)象
return (ImageLoaderMachO*)image;
}
throw "main executable not a known format";
}
- 接著下一步就是加載插入的動(dòng)態(tài)庫(kù):
static void loadInsertedDylib(const char* path)
{
ImageLoader* image = NULL;
unsigned cacheIndex;
try {
image = load(path, context, cacheIndex);
}
...
}
最后就是鏈接動(dòng)態(tài)庫(kù),插入任何動(dòng)態(tài)加載的鏡像文件昏苏。
然后就開(kāi)始運(yùn)行:
void initializeMainExecutable()
{
...
if ( rootCount > 1 ) {
for(size_t i=1; i < rootCount; ++i) {
sImageRoots[i]->runInitializers(gLinkContext, initializerTimes[0]);//先為所有插入并鏈接完成的動(dòng)態(tài)庫(kù)執(zhí)行初始化操作
}
}
// run initializers for main executable and everything it brings up
sMainExecutable->runInitializers(gLinkContext, initializerTimes[0]);//為主要可執(zhí)行文件及其帶來(lái)的一切運(yùn)行初始化程序
...
}
void ImageLoader::runInitializers(const LinkContext& context, InitializerTimingList& timingInfo)
{
...
processInitializers(context, thisThread, timingInfo, up);//初始化準(zhǔn)備
...
}
void ImageLoader::processInitializers(const LinkContext& context, mach_port_t thisThread,
InitializerTimingList& timingInfo, ImageLoader::UninitedUpwards& images)
{
...
// Calling recursive init on all images in images list, building a new list of
// uninitialized upward dependencies.
for (uintptr_t i=0; i < images.count; ++i) {//遍歷image.count
images.images[i]->recursiveInitialization(context, thisThread, images.images[i]->getPath(), timingInfo, ups);//遞歸初始化鏡像
}
...
}
void ImageLoader::recursiveInitialization(const LinkContext& context, mach_port_t this_thread, const char* pathToInitialize,
InitializerTimingList& timingInfo, UninitedUpwards& uninitUps)
{
...
try {
...
context.notifySingle(dyld_image_state_dependents_initialized, this, &timingInfo);//單個(gè)鏡像通知尊沸;獲取到鏡像的回調(diào)
// initialize this image
bool hasInitializers = this->doInitialization(context);
...
}
...
}
bool ImageLoaderMachO::doInitialization(const LinkContext& context)
{
CRSetCrashLogMessage2(this->getPath());
// mach-o has -init and static initializers
doImageInit(context);
doModInitFunctions(context);//比如會(huì)調(diào)用c++全局構(gòu)造函數(shù)-初始化
CRSetCrashLogMessage2(NULL);
return (fHasDashInit || fHasInitializers);
}
void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)
{
...
if ( type == S_MOD_INIT_FUNC_POINTERS ) {
Initializer* inits = (Initializer*)(sect->addr + fSlide);
...
for (size_t j=0; j < count; ++j) {
Initializer func = inits[j];
...
{
dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0);
func(context.argc, context.argv, context.envp, context.apple, &context.programVars);//就是調(diào)用libSystem_initializer
}
...
}
...
}
- 到這里就要跳轉(zhuǎn)到Libsystem源碼:
__attribute__((constructor))
static void
libSystem_initializer(int argc,
const char* argv[],
const char* envp[],
const char* apple[],
const struct ProgramVars* vars)
{
...
libdispatch_init();
...
}
- 接著又跳轉(zhuǎn)到libdispatch源碼:
void
libdispatch_init(void)
{
...
_dispatch_hw_config_init();
_dispatch_time_init();
_dispatch_vtable_init();
_os_object_init();
_voucher_init();
_dispatch_introspection_init();
}
void
_os_object_init(void)
{
_objc_init();
...
}
- 最后的最后終于調(diào)用了
_objc_init
,接著又要跳轉(zhuǎn)到objc4源碼:
void _objc_init(void)
{
...
_dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
map_images
:dyld
將image
加載進(jìn)內(nèi)存時(shí) , 會(huì)觸發(fā)該函數(shù).
load_images
:dyld
初始化image
會(huì)觸發(fā)該方法. ( 我們所熟知的load
方法也是在此處調(diào)用 ) .
unmap_image
:dyld
將image
移除時(shí) , 會(huì)觸發(fā)該函數(shù) .
- 很不幸贤惯,又要跳回到dyld源碼中:
void _dyld_objc_notify_register(_dyld_objc_notify_mapped mapped,
_dyld_objc_notify_init init,
_dyld_objc_notify_unmapped unmapped)
{
dyld::registerObjCNotifiers(mapped, init, unmapped);
}
void registerObjCNotifiers(_dyld_objc_notify_mapped mapped, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmapped)
{
// record functions to call
sNotifyObjCMapped = mapped;
sNotifyObjCInit = init;//load_images函數(shù)
...
for (std::vector<ImageLoader*>::iterator it=sAllImages.begin(); it != sAllImages.end(); it++) {
ImageLoader* image = *it;
if ( (image->getState() == dyld_image_state_initialized) && image->notifyObjC() ) {
dyld3::ScopedTimer timer(DBG_DYLD_TIMING_OBJC_INIT, (uint64_t)image->machHeader(), 0, 0);
(*sNotifyObjCInit)(image->getRealPath(), image->machHeader());//獲取鏡像文件真實(shí)地址; 比如會(huì)調(diào)用[ViewController load]
}
}
}
這里可以看到調(diào)用的sNotifyObjCInit
就是load_images
函數(shù)洼专。
(dyld
-> libSystem
-> libDispatch
-> libObjc
-> _objc_init
)
- 總結(jié)