dylibs and hardened runtime.

My hardened, developer-delivered MacOS app has a couple of embedded command-line executables, which are called from the main app, as well as dozens of dylibs that those executables require.The executables are in Contents/MacOS and the libraries are in Contents/lib/*.dylib. I used install_name_tool to point the executables at the appropriate dylibs with @executable_path. They are all signed; this has run great in the past, and the existing binary still runs fine.

But...now when I build and run the unchanged app in Xcode (both 12 and 13), I get an error when the executable is called:

dyld: Library not loaded
@executable_path/../lib/library.dylib 
Referenced from: .../Contents/MacOS/subprogram
Reason: no suitable image found.  Did find:
file system relative paths not allowed in hardened programs

Anybody know if a rule has changed here? Is @executable_path no longer supported in hardened environments?

I've spent hours experimenting here. In searching the forums, I find various references to putting the dylibs into a framework. I tried just moving the dylibs into ../Framework instead of ../lib, with same error. Will switching to @rpath fix this? Would encapsulating them in full frameworks fix this? Is there any tool/documentation for that process? Does it require one framework for each dylib?

  • After posting, I realized an obvious difference: I'm now building the Apple Silicon version, not the Intel one, so the "existing binary" is running under Rosetta, but the compiled one is running apple silicon.

Add a Comment

Replies

After posting, I realized an obvious difference

Hmmm, interesting.

I wrote up the following but I didn’t post it immediately because I was waiting for a doc to be published so I could reference it. I’m posting the whole thing verbatim now. I’m not sure how much of it is still relevant, but I figured you might find it useful.


Is @executable_path no longer supported in hardened environments?

That’s definitely supported.

I tried just moving the dylibs into ../Framework instead of ../lib, with same error.

OK. Still, you should make this switch anyway, to bring your app in line with the rules discussed in Placing Content in a Bundle.

Would encapsulating them in full frameworks fix this?

Unlikely.

Will switching to @rpath fix this?

Probably, although more likely by accident rather than because of some specific issue.

I suspect that you have some sort of relative reference in your libraries or their clients that you haven’t discovered yet. With enough runs of otool -L and otool -l you should be able to uncover that and fix it precisely.

However, I’m not sure that’s worth the effort. What I recommend is that you switch everything to be rpath relative, with an @executable_path relative rpath entry is the executable that uses these libraries. This will make life a lot easier in the end.

I’ve been writing a doc that explains how to do this and it finally hit the web yesterday. See Embedding Nonstandard Code Structures in a Bundle.

Share and Enjoy

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

So, the ultimate answer proved to be totally different. After agonizing amounts of diagnosing, trying new configurations, I finally realized that there are two error messages up there:

Reason: no suitable image found.  Did find:  
file system relative paths not allowed in hardened programs

The first one turned out to be correct; there was, in fact, a dylib missing from my build. (One for x86_64 arch) The second message is completely wrong, and led me down a long irrelevant path.