Notarization of universal static library (fat file) doesn't work

I am trying to notarize a framework built for both platforms, arm64 and x86_64 (universal).

The framework contains a static library (fat file) which can't be notarized.

I get the following errors in the log: "The binary is not signed." and "The signature does not include a secure timestamp."

When I build only for one of the two architectures (non-fat file), the notarization works without any issues.


Universal:

% file libtbarcode11.a 
libtbarcode11.a: Mach-O universal binary with 2 architectures: [x86_64:current ar archive] [arm64]
libtbarcode11.a (for architecture x86
_64): current ar archive
libtbarcode11.a (for architecture arm64): current ar archive

% lipo -info libtbarcode11.a
Architectures in the fat file: libtbarcode11.a are: x86_64 arm64
 
% codesign -v --verify libtbarcode11.a
libtbarcode11.a: valid on disk
libtbarcode11.a: satisfies its Designated Requirement

arm64:

% file libtbarcode11.a
libtbarcode11.a: current ar archive

% lipo -info libtbarcode11.a
Non-fat file: libtbarcode11.a is architecture: arm64

% codesign -v --verify libtbarcode11.a
libtbarcode11.a: valid on disk
libtbarcode11.a: satisfies its Designated Requirement


codesign commands I tried so far:

Code Block
codesign --force --verify --verbose --sign "MyDeveloperIDApplicationCert" libtbarcode11.a

Code Block
codesign --force --verify --verbose --sign "MyDeveloperIDApplicationCert" --options runtime libtbarcode11.a

Code Block
codesign --force --verify --verbose --sign "MyDeveloperIDApplicationCert" --deep libtbarcode11.a

Answered by DTS Engineer in 657313022

Yes I'm building an SDK.

Cool. In that case the easiest workaround is to ship multiple static libraries, one for each architecture. The bug I mentioned below is only triggered by the multi-architecture library.

This might be mildly inconvenient for your static library’s clients — depending on their build system, they might need to lipo the libraries together — but it doesn’t sound like it has a lot of clients anyway (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"
A single static library file is not a framework.

Frameworks, just by themselves, can't be notarized.

Anything that can be notarized must use the hardened runtime and a secure timestamp. So even if you had provided the secure timestamp flag, you wouldn't have been able to notarize this archive.
@Etresoft
I didn't say that I'm notarizing a single static library or a Framework by itself ^^

I notarize my Framework as package, the package contains a binary and multiple libraries including the static library.

Please note that I mentioned in my question that notarization does already work correctly for me when I build non-fat files. Only in case of fat files it fails. So I'm indeed able to notarize this archive.

I didn't say that I'm notarizing a single static library or a Framework by itself ^^

I notarize my Framework as package, the package contains a binary and multiple libraries including the static library.

I must have misinterpreted all of the "codesign ... libtbarcode11.a" commands.

So I'm indeed able to notarize this archive.

Excellent! Problem solved.

Excellent! Problem solved.

Sorry, I will try to clarify: I am able to notarize my installer package when the static library is a non-fat file (only one arch). But it still fails when I try to notarize it with the static library as fat file (universal).

:)
Why are you distributing a static library with your product? There are good reasons for doing this — for example, you’re building an SDK and thus you expect your ultimate clients to take the static library and link it into their final product — but most examples I see are folks including the static library by mistake )-:

As to what’s going wrong here, the notary service has a bug related in the way it handles multi-architecture static libraries (r. 57321912). There are various workarounds you can apply but, before going down that path, I need to get a better understanding of why you’re distributing a static library in the first place.



Etresoft wrote:

Frameworks, just by themselves, can't be notarized.

That’s not true. Well, you can’t notarise a framework per se, you have to package it up somehow (zip archive, disk image, installer package) but, once you do that, the notary service will accept it just fine.

Anything that can be notarized must use the hardened runtime and a
secure timestamp.

That’s true for the secure timestamp but not for the hardened runtime. The notary service requires that the hardened runtime be enabled for Mach-O main executables (MH_EXECUTE) but not for other Mach-O types.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

Sorry, I will try to clarify: I am able to notarize my installer package when the static library is a non-fat file (only one arch). But it still fails when I try to notarize it with the static library as fat file (universal).

There is only one valid reason to be including a static library in an installer package. That would be if you are shipping a developer framework to other developers, who are your customers. Here, I am using the work "framework" as a generic package of files for a developer to use, not an official "framework bundle" in Apple's platforms. An official "framework" is a bundle and wrapper around a dynamic library that includes header files and, optionally, some resources. A static library is not part of a framework.

Now you can distribute a static library along with your framework for those customers that want to link statically. I hadn't mentioned this possibility before because I didn't want to introduce additional confusion. I just accepted you at your word that you are doing exactly what you claim - trying to notarize a static library, which is not possible.

A static library is not runtime code. It does not need to be notarized. It does not even need to be signed. It doesn't need anything. It is just an "archive" of object files that you shove into your executable.

In theory, you could run with that "shove it in somewhere" static library approach and just shove your static library somewhere inside a real framework bundle. You haven't said anything about that, however. I would not suggest you do that for a number of reasons.

If you are getting stuck by some obscure Apple bug, that should be easy enough to avoid. Just zip the static library, or maybe use a tarball. The static library doesn't need to be inside the framework, and shouldn't be there either. If you are just shipping files based off of your "/usr/local" directory (a very bad idea, by the way) then you might have to do something else. In this case, you would have to zip/tarball the static library before your build your package. Add a post-install step to the package to extract the static library back to its original state. If worse comes to worse, you can carefully corrupt the archive or encrypt it in such a way that the notarization servers don't recognize it. You want the notarization server to just treat it as random binary and ignore it, more or less.

If none of this applies, or doesn't make sense, then please explain what you are attempting to do, at a high level.
@eskimo:

Yes I'm building an SDK. I don't expect my clients to include the static lib but I want to offer it as an option. So I'm shipping both the dynamic and the static library.

In the past I only shipped the dynamic library but we got requests from some Linux customers to include a static version too, so I added it on Linux and thought it wouldn't hurt when I add it on macOS too.

Can you please tell me more about those workarounds :)

Thanks
Accepted Answer

Yes I'm building an SDK.

Cool. In that case the easiest workaround is to ship multiple static libraries, one for each architecture. The bug I mentioned below is only triggered by the multi-architecture library.

This might be mildly inconvenient for your static library’s clients — depending on their build system, they might need to lipo the libraries together — but it doesn’t sound like it has a lot of clients anyway (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@apple.com"

There doesn't seem to be any way to compile a separate arm64 binary in Xcode 12.2.

You can force Xcode to build a specific architecture using the ARCHS build setting but it might be easier to let Xcode build both all the architectures and then add a post-processing step that runs lipo to create one file per architecture.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Notarization of universal static library (fat file) doesn't work
 
 
Q