App can't start because of broken/unverifiable dylib

I have an MacOS App that get's accepted by Transporter/App Store Connect and is available to download in TestFlight. The App also installs fine. But when I try to open it I get this error:

“libgdx64.dylib” can’t be opened because Apple cannot check it for malicious software. This software needs to be updated. Contact the developer for more information.

When I click "Show in Finder" I get taken to this folder:

/private/var/folders/cs/bqb_3kr17f35kbtv34cw7s700000gn/T/<AppBundleID>/libgdxmayr/231c5cdb/libgdx64.dylib

When I try to pen this file in any capacity, e.g. in a hex editor, I get this error:

“libgdx64.dylib” is damaged and can’t be opened. You should move it to the Bin.

This is weird to me for multiple reasons. When I compare(with the diff command) the file to the original file in the package I sent to App Store Connect, there is no difference at all. And when I manually replace the broken file in the /private/var/folders/... directory with the working one, the file still gets marked as damaged, even though it worked a second earlier outside of that location.

The file actually gets copied to two other places as well.

1:

/private/var/folders/cs/bqb_3kr17f35kbtv34cw7s700000gn/T/<AppBundleID>/231c5cdb259220476539382411.tmp

This also is on a byte level the exactly same file as libgdx64.dylib.

2:

/Users/mayr/Library/Containers/69A9AB69-9ECB-4B55-A715-1A28FC7168B6/Data/.libgdx/231c5cdb/libgdx64.dylib

This location specifically seems to be a App Sandbox location, so I suppose the whole problem has to do with the Sandbox.

More Context:

The file libgdx64.dylib originally lives in a jar file in the Apps Resources folder. The file belongs to a java game engine framework, called libgdx. I have signed it myself and the signature persists, even to the 3 broken files.

No other files neither in my own code, nor from the framework, get copied anywhere. Only this specific dylib behaves this way.

When I click "OK" on the first popup, two others appear, for the other two files I mentioned earlier. If I say OK to all of them and approve all of them in the System Settings -> Security & Privacy Settings, the app starts as it normally would. So maybe it is also a signing issue, but as I wrote, I signed the file and the signature is visible on the broken files.

Answered by DTS Engineer in 701177022

The file libgdx64.dylib originally lives in a jar file in the Apps Resources folder.

This won’t fly on the Mac App Store. When you submit an app to the Mac App Store, you sign it with your distribution signing identity (either Apple Distribution, aka Apple Distribution: TTT, or Mac App Distribution, 3rd Party Mac Developer Application: TTT, where TTT identifies your team). The App Store ingestion process uses the resulting code signature to confirm that your code was signed by your and arrived intact. However, it doesn’t ship that version of your app to customers. Rather, it re-signs the app use in Apple signing identity.

You can confirm this with codesign:

% codesign -d -vv "/Applications/Tap Forms 5.app"
…
Authority=Apple Mac OS Application Signing
…

Note that the certificate in the signature is Apple Mac OS Application Signing, which looks nothing like the distribution signing identities I referenced above.

For this to work the App Store ingestion process has to be able to ‘see’ all the code in your app. If you stash code away in an archive, the App Store can’t re-sign it and you end up with problems like this.

There isn’t any way for you to fix this in your code signature, because you don’t have access to the Apple Mac OS Application Signing identity. The only solution is for you to change your app’s structure so that all code is visible to the App Store.

Share and Enjoy

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

Accepted Answer

The file libgdx64.dylib originally lives in a jar file in the Apps Resources folder.

This won’t fly on the Mac App Store. When you submit an app to the Mac App Store, you sign it with your distribution signing identity (either Apple Distribution, aka Apple Distribution: TTT, or Mac App Distribution, 3rd Party Mac Developer Application: TTT, where TTT identifies your team). The App Store ingestion process uses the resulting code signature to confirm that your code was signed by your and arrived intact. However, it doesn’t ship that version of your app to customers. Rather, it re-signs the app use in Apple signing identity.

You can confirm this with codesign:

% codesign -d -vv "/Applications/Tap Forms 5.app"
…
Authority=Apple Mac OS Application Signing
…

Note that the certificate in the signature is Apple Mac OS Application Signing, which looks nothing like the distribution signing identities I referenced above.

For this to work the App Store ingestion process has to be able to ‘see’ all the code in your app. If you stash code away in an archive, the App Store can’t re-sign it and you end up with problems like this.

There isn’t any way for you to fix this in your code signature, because you don’t have access to the Apple Mac OS Application Signing identity. The only solution is for you to change your app’s structure so that all code is visible to the App Store.

Share and Enjoy

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

Thank you very much for your insights. I have restructured my resources and now have two things in my resources folder:

  1. A folder named jre, containing the jre.

  2. A folder named desktop-1.0 containing the contents of the prior jar file.

To start the java process I am using this code in a binary located in the MacOS Folder. It is the first thing that gets executed

NSBundle* myBundle = [NSBundle mainBundle];
NSString* absPath = [myBundle resourcePath];

NSTask *task = [[NSTask alloc] init];

task.launchPath = [NSString stringWithFormat:@"%@%s", absPath, "/jre/bin/java"];
task.arguments = @[@"-XstartOnFirstThread", @"-cp", [NSString stringWithFormat:@"%@%s", absPath, "/desktop-1.0"], @"software.mayr.rougelike.desktop.DesktopLauncher"];

[task launch];

Now the app can start, but it instantly crashes with this in the error report:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Couldn't posix_spawn: error 1'
terminating with uncaught exception of type NSException
abort() called

I suppose that it is either not allowed to start tasks from a sanboxed App or that the java executable is not correctly signed. I have signed it once with just the

<key>com.apple.security.app-sandbox</key>
<true/>

value, and once with these two:

<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>

I tried both, because I was not sure if this process needs to keep the same sandbox as the main process, or if it should use a separate one.

If you switch to -launchAndReturnError:, NSTask will return an error rather than throw an exception. That makes it easier to debug such issues (-:

The posix_spawn: error 1 message suggests you’re hitting an EPERM, which typically means that access to the executable has been blocked by the sandbox (I discuss the significance of this error code in On File System Permissions). If you call access with X_OK on launchPath, what do you get back?

Share and Enjoy

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

access( [task.launchPath UTF8String], X_OK) returns 0.

I used the launchAndReturnError: method you recommended, and it shows me this error:

Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"

Ok, I found something new in the Console.

exec of /Applications/***.app/Contents/Resources/jre/bin/java denied since it was quarantined by Hex\x20Fiend and not approved by Gatekeeper, qtn-flags was 0x00000082

I have a feeling what this means. Originally when uploading my app to the App Store I got this error:

ITMS-90511: CFBundleIdentifier Collision - The Info.plist CFBundleIdentifier value 'net.java.openjdk.cmd' of 'java' is already in use by another application.

I searched around a bit and found that I should replace the CFBundleIdentifier in the java binary with a hex editor. I changed it to something unique under my domain with the same length as net.java.openjdk.cmd. I used Hex Fiend to do that, and apparently that has marked the binary as quarantined. I did not notice that, and the App Store also did not notify me of that. I now used

xattr -d com.apple.quarantine <path/to/java>

to remove the quarantine tag. I am currently uploading the App again to see if that solved the issue.

Ok, that worked. The App now fully works via TestFlight.

Thanks a lot for your support : )

App can't start because of broken/unverifiable dylib
 
 
Q