wowoqu 发表于 2025-2-7 02:58:06

dyld: 神秘的 __dso_handle

iOS动态链接器dyld中有一个神秘的变量__dso_handle:
// dyld/dyldMain.cppstatic const MachOAnalyzer* getDyldMH(){#if __LP64__    // 声明 __dso_handle    extern const MachOAnalyzer __dso_handle;    return &__dso_handle;#else    ...#endif // __LP64__}这个函数内部声明了一个变量__dso_handle,其类型是struct MachOAnalyzer。
查看struct MachOAnalyzer的定义,它继承自struct mach_header:

struct mach_header正是XNU内核里面,定义的Mach-O文件头:
// EXTENERL_HEADERS/mach-o/loader.hstruct mach_header {        uint32_t        magic;                /* mach magic number identifier */        cpu_type_t        cputype;        /* cpu specifier */        cpu_subtype_t        cpusubtype;        /* machine specifier */        uint32_t        filetype;        /* type of file */        uint32_t        ncmds;                /* number of load commands */        uint32_t        sizeofcmds;        /* the size of all the load commands */        uint32_t        flags;                /* flags */};从上面函数getDyldMH的名字来看,它返回dyld这个Mach-O文件的文件头,而这确实也符合变量__dso_handle的类型定义。
但是奇怪的事情发生了,搜遍整个dyld源码库,都无法找到变量__dso_handle的定义。所有能搜到的地方,都只是对这个变量__dso_handle的声明。
众所周知,动态连接器dyld本身是静态链接的。
也就是说,动态连接器dyld本身是不依赖任何其他动态库的。
因此,这个变量__dso_handle不可能定义在其他动态库。
既然这样,动态链接器dyld本身是如何静态链接通过的呢?
答案只可能是静态链接器ld在链接过程中做了手脚。
查看静态链接器ld的源码,也就是llvm的源码,可以找到如下代码:
// lld/MachO/SyntheticSections.cppvoid macho::createSyntheticSymbols() {// addHeaderSymbol 的 lamba 表达式auto addHeaderSymbol = [](const char *name) {    symtab->addSynthetic(name, in.header->isec, /*value=*/0,                         /*isPrivateExtern=*/true, /*includeInSymtab=*/false,                         /*referencedDynamically=*/false);};...// The Itanium C++ ABI requires dylibs to pass a pointer to __cxa_atexit// which does e.g. cleanup of static global variables. The ABI document// says that the pointer can point to any address in one of the dylib's// segments, but in practice ld64 seems to set it to point to the header,// so that's what's implemented here.addHeaderSymbol("___dso_handle");}上面代码定义了一个addHeaderSymbol的lamda表达式,然后使用它添加了一个符号,这个符号正是__dso_handle。
调用addHeaderSymbol上方的注释使用chatGPT翻译过来如下:
Itanium C++ ABI 要求动态库传递一个指向 __cxa_atexit 的指针,该函数负责例如静态全局变量的清理。ABI 文档指出,指针可以指向动态库的某个段中的任意地址,但实际上,ld64(苹果的链接器)似乎将其设置为指向头部,所以这里实现了这种做法。
注释中提到的Itanium C++ ABI最初是为英特尔和惠普联合开发的Itanium处理器架构设计的。
但其影响已经超过了最初设计的架构范围,并被广泛用于其他架构,比如x86和 x86-64上的多种编译器,包括GCC和Clang。
而且,注释中还提到,__dso_handle在苹果的实现里,是指向了Mach-O的头部。
至此,谜底解开~。
页: [1]
查看完整版本: dyld: 神秘的 __dso_handle