深入分析 dyld 的源码需要详细了解其内部结构和关键函数。以下是一些关键部分的详细分析,结合了 dyld 的源码。
1. 加载可执行文件及其依赖库
详细分析
dyld 通过 _main 函数开始执行,该函数负责初始化环境并加载主可执行文件及其依赖库。
// dyld/src/dyld.cpp
void dyld::_main(const macho_header* mainExecutableMH, uintptr_t* mainExecutableSlide) {
// 初始化环境
initializeProcess();
// 加载主可执行文件
sMainExecutable = instantiateFromLoadedImage(mainExecutableMH, mainExecutableSlide);
// 加载插入的库
loadInsertedDylibs();
// 加载依赖库
link(sMainExecutable);
}
instantiateFromLoadedImage 函数负责创建 ImageLoader 对象,这是 dyld 中用于表示已加载图像的核心类。
// dyld/src/ImageLoader.cpp
ImageLoader* instantiateFromLoadedImage(const macho_header* mh, uintptr_t slide) {
// 创建 ImageLoader 对象
ImageLoader* image = ImageLoaderMachO::instantiateMainExecutable(mh, slide);
return image;
}
2. 解析符号和重定位
详细分析
link 函数负责解析符号和重定位。它遍历所有已加载的图像,并调用它们的 link 方法。
// dyld/src/dyld.cpp
void dyld::link(ImageLoader* image) {
// 解析符号并进行重定位
image->link(context, preflightOnly, false);
}
ImageLoader 类的 link 方法负责解析符号和重定位。
// dyld/src/ImageLoader.cpp
void ImageLoader::link(const LinkContext& context, bool preflightOnly, bool neverUnload) {
// 解析符号
resolveSymbols(context);
// 重定位
applyFixUps(context);
}
3. 处理 dyld 插入库
详细分析
loadInsertedDylibs 函数负责加载通过 DYLD_INSERT_LIBRARIES 环境变量指定的库。
// dyld/src/dyld.cpp
void dyld::loadInsertedDylibs() {
const char* const* dylibs = getenv("DYLD_INSERT_LIBRARIES");
if (dylibs != NULL) {
for (const char* const* dp = dylibs; *dp != NULL; ++dp) {
loadDylib(*dp);
}
}
}
loadDylib 函数负责加载指定的动态库。
// dyld/src/dyld.cpp
void dyld::loadDylib(const char* path) {
// 加载动态库
ImageLoader* image = load(path);
if (image != NULL) {
addImage(image);
}
}
4. 运行初始器(Initializers)
详细分析
runInitializers 函数负责运行所有已加载图像的初始器。
// dyld/src/dyld.cpp
void dyld::runInitializers(ImageLoader* image) {
// 运行初始器
image->runInitializers(context);
}
ImageLoader 类的 runInitializers 方法负责运行初始器。
// dyld/src/ImageLoader.cpp
void ImageLoader::runInitializers(const LinkContext& context) {
// 运行初始器
processInitializers(context);
}
5. 处理 @rpath, @loader_path, @executable_path
详细分析
getRealPath 函数负责解析特殊路径符号。
// dyld/src/ImageLoader.cpp
const char* ImageLoader::getRealPath(const char* path) {
if (strncmp(path, "@rpath", 6) == 0) {
// 处理 @rpath
return resolveRPath(path);
} else if (strncmp(path, "@loader_path", 12) == 0) {
// 处理 @loader_path
return resolveLoaderPath(path);
} else if (strncmp(path, "@executable_path", 16) == 0) {
// 处理 @executable_path
return resolveExecutablePath(path);
}
return path;
}
总结
dyld 的源码展示了其复杂的内部结构和关键函数。通过深入分析这些函数,我们可以更好地理解 dyld 是如何加载可执行文件和共享库、解析符号、重定位地址、运行初始器以及处理特殊路径符号的。这些分析有助于我们更好地理解动态链接器的工作原理,并在需要时进行调试和优化。