启动过程调用堆栈是这个样子:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 5.1
* frame #0: 0x0000000104c0acd4 Xman_New`___lldb_unnamed_symbol191473$$Xman_New
frame #1: 0x0000000237695ffc libobjc.A.dylib`call_load_methods +
frame #2: 0x0000000237697e54 libobjc.A.dylib`load_images + 180
frame #3: 0x000000010544e390 dyld`dyld::notifySingle(dyld_image_states, ImageLoader const*, ImageLoader::InitializerTimingList*) + 444
frame #4: 0x0000000105460380 dyld`ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, char const*, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 440
frame #5: 0x000000010545f3dc dyld`ImageLoader::processInitializers(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&) + 136
frame #6: 0x000000010545f498 dyld`ImageLoader::runInitializers(ImageLoader::LinkContext const&, ImageLoader::InitializerTimingList&) + 84
frame #7: 0x000000010544e6d8 dyld`dyld::initializeMainExecutable() + 220
frame #8: 0x00000001054532a0 dyld`dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*) + 4304
frame #9: 0x000000010544d044 dyld`_dyld_start + 68
这里有点注意就是网上很多资料写的不具体的一点是函数notifySingle中调用load_images函数,并执行完成+load函数和其他初始化函数。
(还有在ImageLoaderMachO::doInitialization中的doImageInit和doModInitFunctions,完成初始化函数调用和dyld`dyld::notifySingle中完成的初始化函数调用之间是什么关系,还没有捋顺。)
在调用 load_images -> load_images_nolock -> prepare_load_methods -> schedule_class_load -> add_class_to_loadable_list
的时候会将未加载的类添加到 loadable_classes
数组中
其中prepare_load_methods中调用
objc_class *__fastcall prepare_load_methods(const mach_header_64 *a1)
{
const mach_header_64 *v1; // x19
__int64 v2; // x0
__int64 v3; // x20
unsigned __int64 v4; // x21
objc_class *v5; // x0
objc_class *result; // x0
objc_class *v7; // x19
unsigned __int64 v8; // x21
__int64 v9; // x20
__int64 v10; // x0
unsigned __int64 v11; // [xsp+8h] [xbp-28h]
v1 = a1;
v2 = _getObjc2NonlazyClassList(a1, &v11); // 得到Nolazy列表+load数量
if ( v11 )
{
v3 = v2;
v4 = 0LL;
do
{
v5 = remapClass(*(objc_class **)(v3 + 8 * v4));// 0x10a006160是列表
schedule_class_load((__int64)v5);
++v4;
}
while ( v4 < v11 );
}
result = (objc_class *)_getObjc2NonlazyCategoryList(v1, &v11);
if ( v11 )
{
v7 = result;
v8 = 0LL;
do
{
v9 = *((_QWORD *)v7 + v8);
result = remapClass(*(objc_class **)(v9 + 8));
if ( result )
{
if ( *((_BYTE *)result + 32) & 2 )
{
v10 = _objc_fatal("Swift class extensions and categories on Swift classes are not allowed to have +load methods");
unmap_image(v10);
}
realizeClassWithoutSwift(result);
result = (objc_class *)add_category_to_loadable_list(v9);
}
++v8;
}
while ( v8 < v11 );
}
return result;
}
v2 = _getObjc2NonlazyClassList(a1, &v11); // 得到Nolazy列表+load数量
_getObjc2NonlazyClassList就读取是在MachO文件中的DATA,__objc_nlclslist中的内容,v11要初始化类的+load函数数量;v2是__objc_nlclslist实际内存地址。
在调用_getObjc2NonlazyClassList之后,调用v5 = remapClass(*(objc_class **)(v3 + 8 * v4));函数,其中v3=v2,这里就是从__objc_nlclslist中读取函数指针
在remapClass之后调用schedule_class_load就是将上面的v5添加到列表当中,汇编里就是一个内存地址。
__int64 __fastcall schedule_class_load(__int64 result)
{
objc_class *v1; // x19
unsigned int *v2; // x9
unsigned int *v3; // x8
unsigned int v4; // off
if ( result )
{
v1 = (objc_class *)result;
if ( !(*(_BYTE *)((*(_QWORD *)(result + 0x20) & 0x7FFFFFFFFFF8LL) + 2) & 0x80) )// x+0x20是类bits变量,这一步应该是判断是否为元类
// cls->data()->flags & RW_LOADED
{
schedule_class_load(*(_QWORD *)(result + 8));// +8是supperclass
// schedule_class_load(cls->superclass);
result = add_class_to_loadable_list(v1);
v3 = (unsigned int *)(*((_QWORD *)v1 + 4) & 0x7FFFFFFFFFF8LL);
do
{
v4 = __ldaxr(v2);
v2 = (unsigned int *)((unsigned int)v2 | 0x800000);
}
while ( __stlxr((unsigned int)v2, v3) );
}
}
return result;
}
//传入参数实际是类内存地址。result + 0x20是oc类结构中的 class_rw_t *bits。
(*(_BYTE *)((*(_QWORD *)(result + 0x20) & 0x7FFFFFFFFFF8LL) + 2) & 0x80) 这一句意思cls->data()->flags & RW_LOADED进行判断。判断是否为元类且是否为RW_LOADED
在add_class_to_loadable_list(v1);将方法添加到+load函数列表当中即loadable_classes当中。loadable_classes是全局变量,吐槽下,全局变量怎么不作为入口参数传入那!
在回到prepare_load_methods函数中下一步就是对_getObjc2NonlazyCategoryList,这部分初始化函数进行加载了。这部分是对MachO中的DATA,__objc_nlcatlist进行加载,具体没仔细看,这里不讨论。
然后返回到load_images中下一步开始执行call_load_methods();这里就开始执行列表中的所有+load函数了。
初始化函数ImageLoaderMachO::doModInitFunctions
__int64 __fastcall ImageLoaderMachO::doModInitFunctions(__int64 result, __int64 a2)
{
__int64 v2; // x22
unsigned __int64 v3; // x23
__int64 (__fastcall *v4)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD); // x24
__int64 v5; // x25
void *v6; // x26
__int64 v7; // x30
_QWORD *v8; // x19
__int64 v9; // x8
__int64 v10; // x20
int v11; // w9
_DWORD *v12; // x10
__int64 v13; // x21
unsigned __int64 v14; // x27
unsigned __int64 v15; // x8
unsigned __int64 v16; // x10
unsigned __int64 v17; // x9
__int64 v18; // x0
__int64 v19; // x8
__int64 v20; // x8
__int64 v21; // x8
__int64 v22; // x0
__int64 v23; // x2
_QWORD *v24; // x19
__int64 v25; // x8
unsigned int v26; // w21
__int64 v27; // x20
unsigned int v28; // w22
signed __int64 v29; // x23
unsigned __int64 v30; // x25
unsigned __int64 v31; // x26
unsigned __int64 v32; // x8
unsigned __int64 v33; // x9
__int64 v34; // x10
unsigned __int8 v35; // cf
unsigned __int64 v36; // x10
__int64 v37; // x8
__int128 v38; // q0
__int64 v39; // ST20_8
__int64 v40; // x0
__int64 v41; // x1
__int128 v42; // [xsp+28h] [xbp-158h]
__int64 v43; // [xsp+38h] [xbp-148h]
void *v44; // [xsp+40h] [xbp-140h]
__int64 v45; // [xsp+48h] [xbp-138h]
__int64 (__fastcall *v46)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD); // [xsp+50h] [xbp-130h]
unsigned __int64 v47; // [xsp+58h] [xbp-128h]
__int64 v48; // [xsp+60h] [xbp-120h]
__int64 v49; // [xsp+68h] [xbp-118h]
__int64 v50; // [xsp+70h] [xbp-110h]
_QWORD *v51; // [xsp+78h] [xbp-108h]
__int64 *v52; // [xsp+80h] [xbp-100h]
__int64 v53; // [xsp+88h] [xbp-F8h]
__int64 (__fastcall *v54)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD); // [xsp+90h] [xbp-F0h]
__int64 v55; // [xsp+98h] [xbp-E8h]
unsigned int v56; // [xsp+A0h] [xbp-E0h]
int v57; // [xsp+A4h] [xbp-DCh]
_DWORD *v58; // [xsp+A8h] [xbp-D8h]
unsigned __int64 v59; // [xsp+B0h] [xbp-D0h]
int v60; // [xsp+B8h] [xbp-C8h]
__int64 v61; // [xsp+C0h] [xbp-C0h]
__int64 v62; // [xsp+C8h] [xbp-B8h]
__int64 (__fastcall *v63)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD); // [xsp+D0h] [xbp-B0h]
__int128 v64; // [xsp+D8h] [xbp-A8h]
__int128 v65; // [xsp+E8h] [xbp-98h]
__int128 v66; // [xsp+F8h] [xbp-88h]
__int128 v67; // [xsp+108h] [xbp-78h]
__int128 v68; // [xsp+118h] [xbp-68h]
__int64 vars0; // [xsp+180h] [xbp+0h]
if ( *(_BYTE *)(result + 117) & 0x80 )
{
v8 = (_QWORD *)result;
v9 = *(_QWORD *)(result + 80); // 0
v56 = *(_DWORD *)(v9 + 16); // 0x19
if ( v56 )
{
v10 = a2;
v11 = 0;
v12 = (_DWORD *)(v9 + 32);
v13 = a2 + 224;
while ( 1 )
{
v57 = v11;
v58 = v12;
if ( *v12 == 25 ) // 0x25是LC_SEGMENT_64的宏
{
v14 = (unsigned __int64)(v58 + 0x12);
v59 = (unsigned __int64)&v58[0x14 * v58[16] + 0x12];
if ( v59 > (unsigned __int64)(v58 + 0x12) )
break;
}
LABEL_23:
v12 = (_DWORD *)((char *)v58 + (unsigned int)v58[1]);// //第一步对LC_SEGMENT_64(_TEXT)对应的 Section段扫描查找_mod_init_func
// //第二步对LC_SEGMENT_64(_DATA)对应的 Section段扫描查找_mod_init_func
v11 = v57 + 1;
if ( v57 + 1 >= v56 )
return result;
}
while ( 1 )
{
if ( *(_BYTE *)(v14 + 0x40) == 9 ) // 9是mod_init_func的Flags的宏
{
v15 = *(_QWORD *)(v14 + 0x20); // 得到 偏移地址
v16 = *((_QWORD *)v58 + 3); // LC_SEGMENT_64(_DATA)地址
if ( v15 < v16 )
goto LABEL_27; // 容错判断
v17 = *(_QWORD *)(v14 + 0x28); // 得到 size
if ( __CFADD__(v17, v15) || v17 + v15 > *((_QWORD *)v58 + 4) + v16 )
goto LABEL_27; // 容错判断
v3 = v17 >> 3;
if ( v17 >> 3 )
break;
}
LABEL_22:
v14 += 0x50LL; // 初始 为0x00000001098f8068 "__text"即dylb+0x68是Section段0x50是这段间距
//
if ( v14 >= v59 ) // v59是LC_SEGMENT_64(_DATA)开头,所这段是循环判断LC_SEGMENT_64(_TEXT)段内容
goto LABEL_23;
}
v2 = 0LL; // 到这里得到_mod_init_func_section地址偏移
v5 = v8[12] + v15; // v8[12]是dylib头地址+v15是偏移地址,v5就是_mod_init_func_section实际地址
while ( 1 )
{
v4 = *(__int64 (__fastcall **)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD))(v5 + 8 * v2);// ImageLoader::containsAddress参数是InitFunc_0的地址。应该连续加载初始化函数
if ( !((*(__int64 (__fastcall **)(_QWORD *, _QWORD))(*v8 + 64LL))(v8, *(_QWORD *)(v5 + 8 * v2)) & 1) )
break;
if ( !*((_BYTE *)off_5BAC0 + 25) )
{
v18 = (*(__int64 (__fastcall **)(_QWORD *))(*v8 + 48LL))(v8);
if ( !v18 || (unsigned int)sub_3E310(v18, "/usr/lib/libSystem.B.dylib") )
goto LABEL_26;
}
if ( *(_BYTE *)(v10 + 0x14D) )
{
v19 = v8[1];
v54 = v4;
v55 = v19;
sub_1B40("dyld: calling initializer function %p in %s\n");
}
v6 = off_5B738;
v20 = v8[10];
v60 = 520552448;
v61 = v20;
v62 = 0LL;
v63 = v4;
v67 = 0u;
v68 = 0u;
v65 = 0u;
v66 = 0u;
v64 = 0u;
dyld3::ScopedTimer::startTimer__(&v60);
dyld3::ScopedTimer::endTimer__(&v60);
result = v4( // InitFunc_0
*(unsigned int *)(v10 + 188),// 1
*(_QWORD *)(v10 + 192), // "/var/containers/Bundle/Application/9F94AAB3-F845-4F47-9A99-97C236323030/Xman_New.app/Xman_New"
*(_QWORD *)(v10 + 200), // "TMPDIR=/private/var/mobile/Containers/Data/Application/1A2AE213-8E07-4C10-B998-1DDB33E31826/tmp/"
*(_QWORD *)(v10 + 208), // "executable_path=/var/containers/Bundle/Application/9F94AAB3-F845-4F47-9A99-97C236323030/Xman_New.app/Xman_New"
v13); // app的mach文件
if ( !v6 )
{
if ( off_5B738 )
*((_BYTE *)off_5BAC0 + 25) = 1;
}
if ( ++v2 >= v3 )
goto LABEL_22;
}
v21 = v8[1];
v54 = v4;
v55 = v21;
sub_18A8("initializer function %p not in mapped image for %s\n");
LABEL_26:
v54 = (__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD))v8[1];
sub_18A8("initializer in image (%s) that does not link with libSystem.dylib\n");
LABEL_27:
v54 = (__int64 (__fastcall *)(_QWORD, _QWORD, _QWORD, _QWORD, _QWORD))v8[1];
v22 = sub_18A8("__mod_init_funcs section has malformed address range for %s\n");
result = sub_1BEC(v22);
v44 = v6;
v45 = v5;
v46 = v4;
v47 = v3;
v48 = v2;
v49 = v13;
v50 = v10;
v51 = v8;
v52 = &vars0;
v53 = v7;
if ( *(_BYTE *)(result + 117) & 0x20 )
{
v24 = (_QWORD *)result;
v25 = *(_QWORD *)(result + 80);
v26 = *(_DWORD *)(v25 + 16);
if ( v26 )
{
v27 = v23;
v28 = 0;
v29 = v25 + 32;
do
{
if ( *(_DWORD *)v29 == 25 )
{
v30 = v29 + 72;
v31 = v29 + 72 + 80LL * *(unsigned int *)(v29 + 64);
if ( v31 > v29 + 72 )
{
do
{
if ( *(_BYTE *)(v30 + 64) == 15 )
{
v32 = *(_QWORD *)(v30 + 32);
v33 = *(_QWORD *)(v29 + 24);
if ( v32 < v33
|| (v34 = *(_QWORD *)(v30 + 40), v35 = __CFADD__(v34, v32), v36 = v34 + v32, v35)
|| v36 > *(_QWORD *)(v29 + 32) + v33 )
{
v39 = v24[1];
v40 = sub_18A8("DOF section has malformed address range for %s\n");
return ImageLoaderMachO::doInitialization(v40, v41);
}
*(_QWORD *)&v42 = v24[12] + v32;
*((_QWORD *)&v42 + 1) = (*(__int64 (__fastcall **)(_QWORD *))(*v24 + 104LL))(v24);
result = sub_12048(v24);
v43 = result;
v37 = *(_QWORD *)(v27 + 8);
if ( v37 == *(_QWORD *)(v27 + 16) )
{
result = sub_1A114(v27, &v42);
}
else
{
v38 = v42;
*(_QWORD *)(v37 + 16) = v43;
*(_OWORD *)v37 = v38;
*(_QWORD *)(v27 + 8) += 24LL;
}
}
v30 += 80LL;
}
while ( v30 < v31 );
}
}
v29 += *(unsigned int *)(v29 + 4);
++v28;
}
while ( v28 < v26 );
}
}
}
}
return result;
}