macOS共享库

备注

我还没有搞清楚macOS的底层库原理,这里仅记录汇总资料,有待后续学习...

我在尝试构建 darwin-jail 时候,惊奇地发现,打包提取的macOS运行环境居然高达 7.3G。这使我非常失望,如果构建一个jail环境,进而运行 Darwin Containers 需要如此巨大的footprint,那么构建类似 Docker / Kubernetes 这样的容器运行环境性价比就很低。

联想到很久以前在支付宝工作时,为线上构建线上查询日志的chroot环境,在Linux上很容易通过 ldd 找出运行程序所依赖的库文件。那么,对于 macOS ,是否有对应的工具呢?

dyld

dyld动态链接编辑器 ( Dynamic Link Editor ),在 macOSiOS 中负责在运行时加载和链接动态库。

dyld 负责定位和加载动态框架(dynamic frameworks),程序需要dyld来运行时和链接代码需要的库。 dyld 也负责在库里面管理符号 symbols (变量和函数的名字)并使符号对于程序可用。

在任何程序加载代码执行时, dyld 加载所有动态框架,所添加动态框架越多,则处理任务实践越长。

备注

emergetools.com 开发了一个 Emerge's performance analysis 可以分析程序加载使用的动态库框架,以及性能分析。非常类似 火焰图 ,可以辅助定位app应用性能问题。

Link fast: Improve build and launch times WWDC 2022的一个技术分享,介绍了链接静态库的原理以及苹果 ld64 的改进和如何结合参数来对程序进行瘦身和加速,以及持续改进 dyld

dyld-shared-cache-extrator

从 macOS Big Sur 开始,Apple 不再随 macOS 一起提供系统库,而是提供所有内置动态库的生成缓存,并排除原始库。 dyld-shared-cache-extrator 工具可以从缓存中提取这些库以进行逆向工程。

darwin-jail 中抽取 /System/Volumes/Preboot/Cryptexes/OS/System/Library/dyld/dyld_shared_cache_* 到jail中的 /System/Library/dyld/ 就是这个原理。

dyld 库非常巨大,主要是包含了数百个库文件以及被应用加载的库的缓存(以加快启动)。 CleanMyMac 工具提供了清理 dyld 库垃圾文件的功能(我理解是清理缓存以及删除程序后不再需要的库)。

显示加载的 dylibs

dyld 提供了一个打印所有加载的 dylibs 的方法:

输出所有 dyld 加载的 dylibs
export DYLD_PRINT_LIBRARIES=1
export DYLD_PRINT_RPATHS=1
export DYLD_PRINT_TO_FILE=dyld.log
myprogram

检查 dyld.log

另外 otool 可以显示程序使用的库文件,类似Linux下的 ld 。但是,这些库文件并没有实际存在于文件系统中,而是 dyld 加载的动态库(我感觉应该被加载到内存了) ,所以你实际是无法根据 otool 来打包文件的(文件不存在):

使用 otool 显示程序使用的库
otool -L /usr/bin/file

输出的使用库文件并不是存在于文件系统中

使用 otool 显示程序使用的库输出案例
/usr/bin/file:
	/usr/lib/libbz2.1.0.dylib (compatibility version 1.0.0, current version 1.0.8)
	/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.12)
	/usr/lib/liblzma.5.dylib (compatibility version 6.0.0, current version 6.3.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)

# 但是你直接在文件系统中是找不到该文件
% ls /usr/lib/libz.1.dylib
ls: /usr/lib/libz.1.dylib: No such file or directory

参考