简体   繁体   English

由于Mach-O标头中缺少LC_VERSION_MIN,Xcode app存档验证因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

I have a Swift project which calls a command line utility using NSTask. 我有一个Swift项目,它使用NSTask调用命令行实用程序。

For portability, I have included that command line utility in the bundled resources of the app. 为了便于携带,我在应用程序的捆绑资源中包含了该命令行实用程序。

The utility is quite complex - it's a Ruby interpreted application, with a bunch of gems, some of which have native extensions. 该实用程序非常复杂 - 它是一个Ruby解释的应用程序,带有一堆宝石,其中一些具有原生扩展。 The gems are all installed with Bundler into a standard vendor/ directory within the utility. 这些gem都与Bundler一起安装到实用程序中的标准供应商/目录中。

I have successfully archived, validated, distributed, and run the app on a second Mac which does not have that CLI utility (or Xcode) installed through the Developer ID ('outside the Mac App Store') workflow several times. 我已经在第二台Mac上成功存档,验证,分发和运行应用程序,而第二台Mac上没有通过开发者ID(“Mac App Store”外部)工作流程安装的CLI实用程序(或Xcode)多次。

However, the archive failed to validate for Mac App Store distribution with this error: 但是,存档无法验证Mac App Store分发时出现此错误:

2016-08-28 15:26:41 +0000 [MT] Presenting: Error Domain=DVTFoundationNSBundleAdditionsErrorDomain Code=1 "Couldn't find platform family in Info.plist CFBundleSupportedPlatforms or Mach-O LC_VERSION_MIN for AbstractMemory.o" UserInfo={NSLocalizedDescription=Couldn't find platform family in Info.plist CFBundleSupportedPlatforms or Mach-O LC_VERSION_MIN for AbstractMemory.o} 2016-08-28 15:26:41 +0000 [MT]呈现:错误域= DVTFoundationNSBundleAdditionsErrorDomain Code = 1“无法在Info.plist CFBundleSupportedPlatforms中找到平台族或者在AbstractMemory.o中找到Mach-O LC_VERSION_MIN”UserInfo = {NSLocalizedDescription =无法在Info.plist CFBundleSupportedPlatforms中找到平台族或者在AbstractMemory.o中找到Mach-O LC_VERSION_MIN}

So... why would app archive validation fail in the MAS workflow, but not the Developer ID workflow, with such an error? 那么......为什么app工作流验证会在MAS工作流程中失败,而不是Developer ID工作流程会出现这样的错误?


Potentially interesting points: 潜在的有趣点:

  • The app uses 1 temporary exception entitlement to let it read and write a particular folder inside the home folder without having to nag the user for access. 该应用程序使用1个临时例外权利让它在主文件夹中读取和写入特定文件夹,而不必唠叨用户进行访问。
  • The app does not use any MAS specific capabilities / entitlements. 该应用程序不使用任何MAS特定功能/权利。
  • AbstractMemory.o is part of the native extension for the FFI ruby gem, which is used by the CLI utility. AbstractMemory.o是FFI ruby​​ gem的本机扩展的一部分,由CLI实用程序使用。
  • I've tried validating with Debug Information Format set to DWARF, and also to DWARF + dSYM, but the same problem happens. 我已尝试使用设置为DWARF的调试信息格式进行验证,也尝试使用DWARF + dSYM进行验证,但同样的问题也会发生。
  • No .dSYM files are listed explicitly in the 'Copy Bundle Resources' settings (though they might be hiding in the folder or gem native extensions of the CLI executable). 没有.dSYM文件在“复制包资源”设置中明确列出(尽管它们可能隐藏在CLI可执行文件的文件夹或gem本机扩展中)。
  • Xcode seems to latch on to certain bits of the native extensions and tries to treat them in special ways. Xcode似乎锁定了原生扩展的某些位,并尝试以特殊方式处理它们。 The following bits are hoisted to the top level of the 'Binary and Entitlements' list in the validate dialog, just underneath the main .app artifact, while other bits (like plain text files in the gem dependency folders) are not: 以下位被提升到验证对话框中“二进制和权利”列表的顶层,就在主.app工件下面,而其他位(如gem依赖文件夹中的纯文本文件)不是:
    • Executables (eg iconv , xmlcatalog ) 可执行文件(例如iconvxmlcatalog
    • .o files (eg AbstractMemory.o ) .o文件(例如AbstractMemory.o
    • .bundle files (eg ffi_c.bundle , nokogiri.bundle ) .bundle文件(例如ffi_c.bundlenokogiri.bundle
    • .dylib files (eg libcapi.dylib , libcharset.dylib ) .dylib文件(例如libcapi.dyliblibcharset.dylib
    • .a files (eg libcharset.a ) .a文件(例如libcharset.a
  • In the 'Binary and Entitlements' list, the main .app is shown as having the expected number of entitlements (the ones I added), but the executables, .o, .a, .bundle, and .dylib files are shown as having 0 entitlements. 在“二进制和权利”列表中,主.app显示为具有预期数量的权利(我添加的权限),但可执行文件.o,.a,.bundle和.dylib文件显示为具有0权利。

TLDR TLDR

The dependency project had originally been compiled with a really old version of Xcode and OS X, so a number of Mach-O executables inside it did not have the LC_VERSION_MIN_MACOSX load command in their headers. 依赖项目最初是使用旧版本的Xcode和OS X编译的,因此其中的许多Mach-O可执行文件在其标题中没有LC_VERSION_MIN_MACOSX加载命令。 Basically I had to find a way to squeeze that load command in. 基本上我必须找到一种方法来挤压该命令。

The only surefire and clean way to do that is to recompile all the native code in the dependency project using the Xcode 7 build tools. 唯一可靠且干净的方法是使用Xcode 7构建工具重新编译依赖项目中的所有本机代码。 If the dependency is open source you could do it yourself. 如果依赖项是开源的,您可以自己完成。 If not you'll have to plead with your software vendor. 如果不是,您将不得不恳求您的软件供应商。

If you need Yosemite Deployment Target compatibility, you could risk it and use Xcode 7.1 GM on a Yosemite build box. 如果您需要Yosemite部署目标兼容性,您可能会冒险并在Yosemite构建框上使用Xcode 7.1 GM。 But I recommend that you recompile the code with Xcode 7.1+ on an El Capitan build box, to ensure your binary dependency plays nice with El Capitan things like SIP. 但我建议您在El Capitan构建盒上使用Xcode 7.1+重新编译代码,以确保您的二进制依赖性能与SIP等El Capitan之类的东西相媲美。

Details 细节

In my case, the dependency project was open source, so I was able to: 就我而言,依赖项目是开源的,所以我能够:

  1. Track down its source code on GitHub. 在GitHub上查找其源代码。
  2. Fork it. 叉吧。
  3. Build the project without making any changes on a version of OS X that matched, as far as possible, the version that the 3rd party devs originally used on their build box to compile the dependency. 构建项目而不对OS X版本进行任何更改,尽可能地匹配第三方开发人员最初在其构建框上使用的版本来编译依赖项。
  4. Look at the build log when (if) it fails. 当(如果)失败时查看构建日志。
  5. Make all the necessary changes to get the build green. 进行所有必要的更改以使构建变为绿色。 You have now reached parity with the original build box that was used. 您现在已经与使用的原始构建框达到了奇偶校验。
  6. Now update the build box OS X version to either Yosemite + Xcode 7.1 GM (if you must have Yosemite compatibility) or El Capitan + Xcode 7.1+. 现在将构建框OS X版本更新为Yosemite + Xcode 7.1 GM(如果必须具有Yosemite兼容性)或El Capitan + Xcode 7.1+。
  7. Rebuild, fix, yada yada. 重建,修复,yada yada。
  8. When it's green again, grab the newly compiled binary artifact. 当它再次变为绿色时,抓取新编译的二进制工件。
  9. Find a Mach-O executable inside the bundle. 在bundle中找到Mach-O可执行文件。
  10. Run otool -l [path to executable] . 运行otool -l [path to executable]
  11. In the big list of Mach-O header load commands, you should now see LC_VERSION_MIN_MACOSX of 10.10 or 10.11 (depending on what you used). 在Mach-O标头加载命令的大清单中,您现在应该看到LC_VERSION_MIN_MACOSX为10.10或10.11(取决于您使用的内容)。
  12. Your binary artefact should be good to go. 你的二元文物应该很好用。

I was able to set up multiple OS X build VMs without going mad by using Travis CI's Mac build fleet. 通过使用Travis CI的Mac构建机队,我能够设置多个OS X构建VM而不会生气。

Remaining non-showstopper problems 剩下的非showstopper问题

"Include symbols for debugging" had to be unticked for archive validation or export. 必须取消“包含用于调试的符号”以进行存档验证或导出。 This is because the symbolicator could not deal with the third party object code. 这是因为符号化器无法处理第三方目标代码。 I guess this is because it was built for release, and so the object code did not come with any accompanying debug symbols. 我想这是因为它是为发布而构建的,因此目标代码没有附带任何附带的调试符号。

If you cannot recompile the code 如果您无法重新编译代码

If you are not able to recompile the code I've heard rumours of dirty workarounds that can stuff Mach-O load commands into a pre-existing binary. 如果您无法重新编译代码,我听说有关于可以将Mach-O加载命令加载到预先存在的二进制文件中的肮脏变通方法的传闻。 This suffers from the following problems: 这会遇到以下问题:

  • This approach is very fragile because it relies on there being sufficient blank bits left in the header for your new load command. 这种方法非常脆弱,因为它依赖于新的加载命令在标题中留下足够的空白位。 Unsurprisingly I've heard that modifying the value of an existing load command tends to be more reliable than inserting a new one. 不出所料,我听说修改现有加载命令的值往往比插入新命令更可靠。
  • The binary must not be signed already. 二进制不得已经签约。 Gatekeeper will instantly reject a binary which has been tampered with in any way after it was signed. Gatekeeper将立即拒绝在签名后以任何方式被篡改的二进制文件。
  • Mach-O header rewriting is not a trivial task, so it tends to be done with the assistance of third party tools. Mach-O头重写不是一项简单的任务,因此它往往是在第三方工具的帮助下完成的。 If you are not used to working at the binary + hex editor level, it is very hard to verify what these tools have done, so you will be putting an awful lot of trust in them to (a) do it correctly and (b) not do anything nefarious. 如果您不习惯在二进制+十六进制编辑器级别工作,那么很难验证这些工具的功能,因此您将非常信任它们(a)正确地执行此操作(b)没有做任何邪恶的事。

Extra discussion 额外的讨论

I cross-posted this question to the Apple Developer forums at https://forums.developer.apple.com/message/175427 to see if it would get answered any faster by a more specialist community. 我将此问题交叉发布到Apple开发人员论坛, 网址https://forums.developer.apple.com/message/175427 ,看看是否会由更专业的社区更快地得到答复。 It wasn't, but you may find additional discussion of the problem through that link. 事实并非如此,但您可以通过该链接找到对该问题的进一步讨论。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM