Xcode 由于 Mach-O header 中缺少 LC_VERSION_MIN,Mac App Store 分发的应用存档验证失败
Xcode app archive validation fails for Mac App Store distribution due to lack of LC_VERSION_MIN in a Mach-O header
我有一个 Swift 项目,它使用 NSTask 调用命令行实用程序。
为了便携性,我将该命令行实用程序包含在应用程序的捆绑资源中。
该实用程序非常复杂 - 它是一个 Ruby 解释型应用程序,带有一堆 gem,其中一些具有本机扩展。 gem 都与 Bundler 一起安装到实用程序中的标准 vendor/ 目录中。
我已经成功存档、验证、分发,并且 运行 应用程序 Mac 没有通过 Developer ID 安装该 CLI 实用程序(或 Xcode) ('outside the Mac App Store') 多次工作流程。
但是,存档无法验证 Mac App Store 分发,出现此错误:
2016-08-28 15:26:41 +0000 [MT] 呈现:错误域=DVTFoundationNSBundleAdditionsErrorDomain 代码=1 "Couldn't find platform family in Info.plist CFBundleSupportedPlatforms or Mach-O LC_VERSION_MIN for AbstractMemory.o" UserInfo={NSLocalizedDescription=无法在 [=75= 中找到平台系列] CFBundleSupportedPlatforms 或 Mach-O LC_VERSION_MIN 对于 AbstractMemory.o}
那么...为什么应用存档验证在 MAS 工作流程中会失败,而不是 Developer ID 工作流程,并出现这样的错误?
可能感兴趣的点:
- 该应用程序使用 1 个临时例外授权,使其可以读写主文件夹中的特定文件夹,而无需唠叨用户访问权限。
- 该应用程序不使用任何 MAS 特定功能/权利。
- AbstractMemory.o 是 CLI 实用程序使用的 FFI ruby gem 本机扩展的一部分。
- 我试过将调试信息格式设置为 DWARF 以及 DWARF + dSYM 进行验证,但同样的问题发生了。
- 'Copy Bundle Resources' 设置中未明确列出任何 .dSYM 文件(尽管它们可能隐藏在文件夹中或 gem CLI 可执行文件的本机扩展中)。
- Xcode 似乎抓住了本机扩展的某些部分并试图以特殊方式对待它们。以下位被提升到验证对话框中 'Binary and Entitlements' 列表的顶层,就在主要的 .app 工件下面,而其他位(如 gem 依赖文件夹中的纯文本文件)则不是:
- 可执行文件(例如
iconv
、xmlcatalog
)
- .o 文件(例如
AbstractMemory.o
)
- . 捆绑文件(例如
ffi_c.bundle
、nokogiri.bundle
)
- .dylib 文件(例如
libcapi.dylib
、libcharset.dylib
)
- .a 文件(例如
libcharset.a
)
- 在 'Binary and Entitlements' 列表中,主 .app 显示为具有预期数量的权利(我添加的权利),但可执行文件 .o、.a、.bundle 和 .dylib文件显示为具有 0 个权利。
TLDR
依赖项目最初是用一个非常旧的Xcode和OS X编译的,所以它里面的一些Mach-O可执行文件没有LC_VERSION_MIN_MACOSX 在他们的 header 中加载命令。基本上我必须找到一种方法来压缩加载命令。
唯一可靠且干净的方法是使用 Xcode 7 构建工具重新编译依赖项目中的所有本机代码。如果依赖项是开源的,你可以自己做。如果不是,您将不得不向您的软件供应商求情。
如果您需要 Yosemite 部署目标兼容性,您可以冒险在 Yosemite 构建箱上使用 Xcode 7.1 GM。但我建议您在 El Capitan build box 上使用 Xcode 7.1+ 重新编译代码,以确保您的二进制依赖项与 SIP 等 El Capitan 兼容。
详情
在我的例子中,依赖项目是开源的,所以我能够:
- 在 GitHub 上找到它的源代码。
- 叉它。
- 在不对 OS X 版本进行任何更改的情况下构建项目,该版本尽可能匹配第 3 方开发人员最初在其构建盒上用于编译依赖项的版本。
- 当(如果)失败时查看构建日志。
- 进行所有必要的更改以使构建绿色化。您现在已经达到与使用的原始构建框相同的水平。
- 现在将构建盒 OS X 版本更新为 Yosemite + Xcode 7.1 GM(如果你必须具有 Yosemite 兼容性)或 El Capitan + Xcode 7.1+.
- 重建,修复,yada yada。
- 再次变绿时,抓取新编译的二进制神器。
- 在捆绑包中找到 Mach-O 可执行文件。
- 运行
otool -l [path to executable]
.
- 在 Mach-O header 加载命令的大列表中,您现在应该看到 LC_VERSION_MIN_MACOSX 10.10 或 10.11(取决于您使用的是什么)。
- 你的二进制文件应该可以运行了。
通过使用 Travis CI 的 Mac 构建队列,我能够设置多个 OS X 构建 VM 而不会发疯。
剩余 non-showstopper 个问题
"Include symbols for debugging" 必须取消选中以进行存档验证或导出。这是因为符号器无法处理第三方 object 代码。我想这是因为它是为发布而构建的,所以 object 代码没有附带任何调试符号。
如果无法重新编译代码
如果您无法重新编译代码,我听说有肮脏的解决方法可以将 Mach-O 加载命令填充到 pre-existing 二进制文件中。这存在以下问题:
- 这种方法非常脆弱,因为它依赖于在 header 中为您的新加载命令留下足够的空白位。不出所料,我听说修改现有加载命令的值往往比插入新命令更可靠。
- 二进制 不能 已经签名。 Gatekeeper 将立即拒绝在签名后以任何方式被篡改的二进制文件。
- Mach-O header 重写不是一件小事,因此往往需要借助第三方工具来完成。如果您不习惯在二进制 + 十六进制编辑器级别工作,则很难验证这些工具做了什么,因此您将非常信任它们 (a) 正确地执行它并且 (b)不要做任何邪恶的事情。
额外讨论
我 cross-posted 在 https://forums.developer.apple.com/message/175427 的 Apple Developer 论坛上提出这个问题,看看更专业的社区是否能更快地回答这个问题。它不是,但您可能会通过 link.
找到对该问题的更多讨论
我有一个 Swift 项目,它使用 NSTask 调用命令行实用程序。
为了便携性,我将该命令行实用程序包含在应用程序的捆绑资源中。
该实用程序非常复杂 - 它是一个 Ruby 解释型应用程序,带有一堆 gem,其中一些具有本机扩展。 gem 都与 Bundler 一起安装到实用程序中的标准 vendor/ 目录中。
我已经成功存档、验证、分发,并且 运行 应用程序 Mac 没有通过 Developer ID 安装该 CLI 实用程序(或 Xcode) ('outside the Mac App Store') 多次工作流程。
但是,存档无法验证 Mac App Store 分发,出现此错误:
2016-08-28 15:26:41 +0000 [MT] 呈现:错误域=DVTFoundationNSBundleAdditionsErrorDomain 代码=1 "Couldn't find platform family in Info.plist CFBundleSupportedPlatforms or Mach-O LC_VERSION_MIN for AbstractMemory.o" UserInfo={NSLocalizedDescription=无法在 [=75= 中找到平台系列] CFBundleSupportedPlatforms 或 Mach-O LC_VERSION_MIN 对于 AbstractMemory.o}
那么...为什么应用存档验证在 MAS 工作流程中会失败,而不是 Developer ID 工作流程,并出现这样的错误?
可能感兴趣的点:
- 该应用程序使用 1 个临时例外授权,使其可以读写主文件夹中的特定文件夹,而无需唠叨用户访问权限。
- 该应用程序不使用任何 MAS 特定功能/权利。
- AbstractMemory.o 是 CLI 实用程序使用的 FFI ruby gem 本机扩展的一部分。
- 我试过将调试信息格式设置为 DWARF 以及 DWARF + dSYM 进行验证,但同样的问题发生了。
- 'Copy Bundle Resources' 设置中未明确列出任何 .dSYM 文件(尽管它们可能隐藏在文件夹中或 gem CLI 可执行文件的本机扩展中)。
- Xcode 似乎抓住了本机扩展的某些部分并试图以特殊方式对待它们。以下位被提升到验证对话框中 'Binary and Entitlements' 列表的顶层,就在主要的 .app 工件下面,而其他位(如 gem 依赖文件夹中的纯文本文件)则不是:
- 可执行文件(例如
iconv
、xmlcatalog
) - .o 文件(例如
AbstractMemory.o
) - . 捆绑文件(例如
ffi_c.bundle
、nokogiri.bundle
) - .dylib 文件(例如
libcapi.dylib
、libcharset.dylib
) - .a 文件(例如
libcharset.a
)
- 可执行文件(例如
- 在 'Binary and Entitlements' 列表中,主 .app 显示为具有预期数量的权利(我添加的权利),但可执行文件 .o、.a、.bundle 和 .dylib文件显示为具有 0 个权利。
TLDR
依赖项目最初是用一个非常旧的Xcode和OS X编译的,所以它里面的一些Mach-O可执行文件没有LC_VERSION_MIN_MACOSX 在他们的 header 中加载命令。基本上我必须找到一种方法来压缩加载命令。
唯一可靠且干净的方法是使用 Xcode 7 构建工具重新编译依赖项目中的所有本机代码。如果依赖项是开源的,你可以自己做。如果不是,您将不得不向您的软件供应商求情。
如果您需要 Yosemite 部署目标兼容性,您可以冒险在 Yosemite 构建箱上使用 Xcode 7.1 GM。但我建议您在 El Capitan build box 上使用 Xcode 7.1+ 重新编译代码,以确保您的二进制依赖项与 SIP 等 El Capitan 兼容。
详情
在我的例子中,依赖项目是开源的,所以我能够:
- 在 GitHub 上找到它的源代码。
- 叉它。
- 在不对 OS X 版本进行任何更改的情况下构建项目,该版本尽可能匹配第 3 方开发人员最初在其构建盒上用于编译依赖项的版本。
- 当(如果)失败时查看构建日志。
- 进行所有必要的更改以使构建绿色化。您现在已经达到与使用的原始构建框相同的水平。
- 现在将构建盒 OS X 版本更新为 Yosemite + Xcode 7.1 GM(如果你必须具有 Yosemite 兼容性)或 El Capitan + Xcode 7.1+.
- 重建,修复,yada yada。
- 再次变绿时,抓取新编译的二进制神器。
- 在捆绑包中找到 Mach-O 可执行文件。
- 运行
otool -l [path to executable]
. - 在 Mach-O header 加载命令的大列表中,您现在应该看到 LC_VERSION_MIN_MACOSX 10.10 或 10.11(取决于您使用的是什么)。
- 你的二进制文件应该可以运行了。
通过使用 Travis CI 的 Mac 构建队列,我能够设置多个 OS X 构建 VM 而不会发疯。
剩余 non-showstopper 个问题
"Include symbols for debugging" 必须取消选中以进行存档验证或导出。这是因为符号器无法处理第三方 object 代码。我想这是因为它是为发布而构建的,所以 object 代码没有附带任何调试符号。
如果无法重新编译代码
如果您无法重新编译代码,我听说有肮脏的解决方法可以将 Mach-O 加载命令填充到 pre-existing 二进制文件中。这存在以下问题:
- 这种方法非常脆弱,因为它依赖于在 header 中为您的新加载命令留下足够的空白位。不出所料,我听说修改现有加载命令的值往往比插入新命令更可靠。
- 二进制 不能 已经签名。 Gatekeeper 将立即拒绝在签名后以任何方式被篡改的二进制文件。
- Mach-O header 重写不是一件小事,因此往往需要借助第三方工具来完成。如果您不习惯在二进制 + 十六进制编辑器级别工作,则很难验证这些工具做了什么,因此您将非常信任它们 (a) 正确地执行它并且 (b)不要做任何邪恶的事情。
额外讨论
我 cross-posted 在 https://forums.developer.apple.com/message/175427 的 Apple Developer 论坛上提出这个问题,看看更专业的社区是否能更快地回答这个问题。它不是,但您可能会通过 link.
找到对该问题的更多讨论