macOS 应用包(.app)是高度自包含的目录结构,理解其内部机制有助于诊断启动失败、权限冲突及动态链接问题。通过剖析典型布局,能快速定位 Info.plist 配置失误或签名失效,避免 Gatekeeper 阻挡。
应用包根目录下为 Contents 文件夹,核心子目录包括 MacOS(存放主可执行文件,如 AppName)、Resources(图标、Nib 等资源)、Frameworks(捆绑动态库)和 Plugins/XPCServices(扩展服务)。必备文件有 Info.plist(元数据)和 PkgInfo(类型 / 创建者码)。签名目录 _CodeSignature/CodeResources 存储哈希校验数据,确保完整性。自 macOS 10.5 起引入签名,App Store 包额外含 _MASReceipt/receipt;公证后可钉入票据。
LaunchServices 和 RunningBoard 先解析 Info.plist:CFBundleExecutable 指定 MacOS/ 中的主程序,CFBundleIdentifier 唯一标识,LSMinimumSystemVersion 定义最低 OS 版本,CFBundleShortVersionString 为展示版号。使用 plutil -p App.app/Contents/Info.plist 查看键值,若缺失 CFBundleExecutable,则 launchd 无法定位可执行,导致 “无法打开” 错误。defaults read App.app/Contents/Info.plist CFBundleIdentifier 快速提取 ID。
代码签名嵌入 entitlements,确保运行时权限。codesign -dv --verbose=4 App.app 输出签名者、团队 ID 和要求;--entitlements :- 显示权限 plist,如 com.apple.security.cs.allow-jit。验证完整性用 spctl --assess --verbose App.app,若 “rejected: originates from unidentified developer”,需公证(xcrun notarytool submit)。工程参数:签名时 --options runtime --timestamp,启用 Hardened Runtime;阈值监控签名过期(codesign -dv 检查 Authority 有效期)。
沙盒是 MAS 强制机制,entitlements 中 com.apple.security.app-sandbox = true 限制访问~/Library/Containers/BUNDLEID/ 容器。常见权限:com.apple.security.network.client 网络出站、com.apple.security.files.user-selected.read-write 文件 picker。违规触发 sandboxd 日志 “deny file-read-data”,崩溃码 -10810。清单:Xcode Capabilities 启用 App Sandbox,添加最小权限;运行时 killall sandboxd 测试重置(慎用);otool -l App.app/Contents/MacOS/AppName | grep entitlements 确认嵌入。
dyld 是动态链接器,启动时加载 Frameworks/*.dylib 和系统缓存(/System/Library/dyld/dyld_shared_cache)。otool -L App.app/Contents/MacOS/AppName 列依赖,如 @rpath/../Frameworks/lib.dylib(rpath 从 Info.plist DYLD_FRAMEWORK_PATH 解析)。捆绑框架用 install_name_tool -add_rpath @executable_path/../Frameworks 调整路径。问题诊断:DYLD_PRINT_LIBRARIES=1 App.app 打印加载序,监控 “image not found” 导致 dyld 崩溃。参数:LC_LOAD_DYLIB 命令限 64 个,超限拆分框架;M1+ 优先 arm64 slice(lipo -info 检查)。
验证与工程清单:
- 结构:find App.app -type d | grep -E 'Contents/(MacOS|Resources|Frameworks)'
- plist:plutil -lint App.app/Contents/Info.plist;grep CFBundle App.app/Contents/Info.plist
- 签名:codesign -vvv --deep --strict App.app;codesign --display --entitlements :- App.app
- 沙盒:codesign -d --entitlements :- App.app | grep sandbox;ps aux | grep sandboxd
- dyld:otool -L -D App.app/Contents/MacOS/*;dyld_info -lazy_bind App.app/Contents/MacOS/AppName
- 启动测试:open -a App.app --args --verbose;tail -f /var/log/system.log | grep AppName
回滚策略:签名失效时 codesign --remove-signature 重签;沙盒冲突降级 entitlements 测试。监控阈值:签名覆盖率 100%、dyld 加载 <1s、沙盒 deny <5 / 启动。
资料来源:eclecticlight.co “The Anatomy of a macOS App”;Apple Bundles/Entitlements/Codesign 文档。