iOS
動態連結器dyld
中有一個神秘的變數__dso_handle
:
// dyld/dyldMain.cpp
static 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.h
struct 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.cpp
void 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
的頭部。
至此,謎底解開~。